From 43005869bd317feb836781654de542ca5d1abce2 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 7 Jun 2023 10:06:26 +0200 Subject: [PATCH 01/24] feat: add initial Maven POM --- packages/core/java/pom.xml | 56 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 packages/core/java/pom.xml diff --git a/packages/core/java/pom.xml b/packages/core/java/pom.xml new file mode 100644 index 000000000..0b01bf095 --- /dev/null +++ b/packages/core/java/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + org.itk.wasm + itk-wasm + 0.1.0 + + itk-wasm for Java + Java interface to itk-wasm WebAssembly modules. + https://github.com/InsightSoftwareConsortium/itk-wasm + 2023 + + ITK + https://itk.org/ + + + + The Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0 + + + + + + Matt McCormick + https://github.com/thewtex + thewtex + + + + + Curtis Rueden + https://github.com/ctrueden + ctrueden + + + + + scm:https://github.com/InsightSoftwareConsortium/itk-wasm + scm:git@github.com:InsightSoftwareConsortium/itk-wasm + https://github.com/InsightSoftwareConsortium/itk-wasm + + + + 0.14.0 + + + + + io.github.kawamuray.wasmtime + wasmtime-java + ${wasmtime-java.version} + + + From 733ab4b43a292cd26e6160bdf159a594ae7fc80a Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 7 Jun 2023 10:18:28 +0200 Subject: [PATCH 02/24] feat: extend pom-scijava parent POM This inherits the SciJava Bill of Materials; see https://github.com/scijava/pom-scijava#readme for details. --- packages/core/java/pom.xml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/packages/core/java/pom.xml b/packages/core/java/pom.xml index 0b01bf095..e6a4daf5d 100644 --- a/packages/core/java/pom.xml +++ b/packages/core/java/pom.xml @@ -2,6 +2,12 @@ 4.0.0 + + org.scijava + pom-scijava + 35.1.1 + + org.itk.wasm itk-wasm 0.1.0 @@ -36,13 +42,32 @@ + + + ITK Forum + https://discourse.itk.org/ + + + scm:https://github.com/InsightSoftwareConsortium/itk-wasm scm:git@github.com:InsightSoftwareConsortium/itk-wasm https://github.com/InsightSoftwareConsortium/itk-wasm + + GitHub Issues + https://github.com/InsightSoftwareConsortium/itk-wasm/issues + + + GitHub Actions + https://github.com/InsightSoftwareConsortium/itk-wasm/actions + + apache_v2 + ITK developers. + Java bindings for itk-wasm. + 0.14.0 @@ -52,5 +77,9 @@ wasmtime-java ${wasmtime-java.version} + + net.imglib2 + imglib2 + From 5904f33f96668c67504c9a21895aaaf3d71de7e9 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 7 Jun 2023 10:19:37 +0200 Subject: [PATCH 03/24] feat: add placeholder main class as starting point --- packages/core/java/pom.xml | 2 ++ .../java/src/main/java/org/itk/wasm/Main.java | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 packages/core/java/src/main/java/org/itk/wasm/Main.java diff --git a/packages/core/java/pom.xml b/packages/core/java/pom.xml index e6a4daf5d..f10b4c991 100644 --- a/packages/core/java/pom.xml +++ b/packages/core/java/pom.xml @@ -64,6 +64,8 @@ + org.itk.wasm.Main + apache_v2 ITK developers. Java bindings for itk-wasm. diff --git a/packages/core/java/src/main/java/org/itk/wasm/Main.java b/packages/core/java/src/main/java/org/itk/wasm/Main.java new file mode 100644 index 000000000..ed7e3b0cd --- /dev/null +++ b/packages/core/java/src/main/java/org/itk/wasm/Main.java @@ -0,0 +1,26 @@ +/*- + * #%L + * Java bindings for itk-wasm. + * %% + * Copyright (C) 2023 ITK developers. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package org.itk.wasm; + +public class Main { + public static void main(String... args) { + System.out.println("Hello world"); + } +} From 7f6ab63130eb62bdfa5c3058f113029971b2be90 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 7 Jun 2023 10:34:02 +0200 Subject: [PATCH 04/24] feat: ignore build folder and Eclipse metadata --- packages/core/java/.gitignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 packages/core/java/.gitignore diff --git a/packages/core/java/.gitignore b/packages/core/java/.gitignore new file mode 100644 index 000000000..10d81e8c6 --- /dev/null +++ b/packages/core/java/.gitignore @@ -0,0 +1,4 @@ +/.classpath +/.project +/.settings/ +/target/ From 61eae3fb92e9d53673f3bd37e6958d6f2cd02c09 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 7 Jun 2023 11:26:51 +0200 Subject: [PATCH 05/24] feat: update main class to run WASM code Adapted from wasmtime-java example at: https://github.com/kawamuray/wasmtime-java/blob/v0.14.0/examples/src/main/java/examples/HelloWorld.java --- .../java/src/main/java/org/itk/wasm/Main.java | 69 ++++++++++++++++++- .../src/main/resources/org/itk/wasm/hello.wat | 4 ++ 2 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 packages/core/java/src/main/resources/org/itk/wasm/hello.wat diff --git a/packages/core/java/src/main/java/org/itk/wasm/Main.java b/packages/core/java/src/main/java/org/itk/wasm/Main.java index ed7e3b0cd..a6c3f195f 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/Main.java +++ b/packages/core/java/src/main/java/org/itk/wasm/Main.java @@ -19,8 +19,73 @@ */ package org.itk.wasm; +import io.github.kawamuray.wasmtime.Engine; +import io.github.kawamuray.wasmtime.Extern; +import io.github.kawamuray.wasmtime.Func; +import io.github.kawamuray.wasmtime.Instance; +import io.github.kawamuray.wasmtime.Module; +import io.github.kawamuray.wasmtime.Store; +import io.github.kawamuray.wasmtime.WasmFunctions; + +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Collection; + public class Main { - public static void main(String... args) { - System.out.println("Hello world"); + public static void main(String... args) throws IOException { + // Configure the initial compilation environment, creating the global + // `Store` structure. Note that you can also tweak configuration settings + // with a `Config` and an `Engine` if desired. + System.err.println("Initializing..."); + try (Store store = Store.withoutData()) { + // Compile the wasm binary into an in-memory instance of a `Module`. + System.err.println("Compiling module..."); + try (Engine engine = store.engine(); + Module module = new Module(engine, readWAT("hello.wat"))) + { + // Here we handle the imports of the module, which in this case is our + // `HelloCallback` type and its associated implementation of `Callback. + System.err.println("Creating callback..."); + try (Func helloFunc = WasmFunctions.wrap(store, () -> { + System.err.println("CB!! Calling back..."); + System.err.println("CB!! > Hello World!"); + })) { + // Once we've got that all set up we can then move to the instantiation + // phase, pairing together a compiled module as well as a set of imports. + // Note that this is where the wasm `start` function, if any, would run. + System.err.println("Instantiating module..."); + Collection imports = Arrays.asList(Extern.fromFunc(helloFunc)); + try (Instance instance = new Instance(store, module, imports)) { + // Next we poke around a bit to extract the `run` function from the module. + System.err.println("Extracting export..."); + try (Func f = instance.getFunc(store, "run").get()) { + WasmFunctions.Consumer0 fn = WasmFunctions.consumer(store, f); + + // And last but not least we can call it! + System.err.println("Calling export..."); + fn.accept(); + + System.err.println("Done."); + } + } + } + } + } + } + + private static byte[] readWAT(String filename) throws IOException { + try (InputStream is = new FileInputStream("/home/curtis/code/kitware/itk-wasm/packages/core/java/src/main/resources/org/itk/wasm/" + filename)) { + //try (InputStream is = Main.class.getResourceAsStream(filename)) { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int nRead; + byte[] buf = new byte[16384]; + while ((nRead = is.read(buf, 0, buf.length)) != -1) { + buffer.write(buf, 0, nRead); + } + return buffer.toByteArray(); + } } } diff --git a/packages/core/java/src/main/resources/org/itk/wasm/hello.wat b/packages/core/java/src/main/resources/org/itk/wasm/hello.wat new file mode 100644 index 000000000..1c56c5582 --- /dev/null +++ b/packages/core/java/src/main/resources/org/itk/wasm/hello.wat @@ -0,0 +1,4 @@ +(module + (func $hello (import "" "hello")) + (func (export "run") (call $hello)) +) From 059a6a8bf148069faef04e8dffc84ad4bd1a43da Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 7 Jun 2023 12:16:29 +0200 Subject: [PATCH 06/24] feat: update main code to call itk-wasm code We call stdout-stderr-test.wasi.wasm's unnamed function. --- .../java/src/main/java/org/itk/wasm/Main.java | 61 ++++++++----------- 1 file changed, 25 insertions(+), 36 deletions(-) diff --git a/packages/core/java/src/main/java/org/itk/wasm/Main.java b/packages/core/java/src/main/java/org/itk/wasm/Main.java index a6c3f195f..526777842 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/Main.java +++ b/packages/core/java/src/main/java/org/itk/wasm/Main.java @@ -19,13 +19,17 @@ */ package org.itk.wasm; -import io.github.kawamuray.wasmtime.Engine; import io.github.kawamuray.wasmtime.Extern; import io.github.kawamuray.wasmtime.Func; import io.github.kawamuray.wasmtime.Instance; +import io.github.kawamuray.wasmtime.Linker; +import io.github.kawamuray.wasmtime.Memory; import io.github.kawamuray.wasmtime.Module; import io.github.kawamuray.wasmtime.Store; import io.github.kawamuray.wasmtime.WasmFunctions; +import io.github.kawamuray.wasmtime.WasmFunctions.Consumer0; +import io.github.kawamuray.wasmtime.wasi.WasiCtx; +import io.github.kawamuray.wasmtime.wasi.WasiCtxBuilder; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; @@ -33,6 +37,7 @@ import java.io.InputStream; import java.util.Arrays; import java.util.Collection; +import java.util.Optional; public class Main { public static void main(String... args) throws IOException { @@ -40,45 +45,29 @@ public static void main(String... args) throws IOException { // `Store` structure. Note that you can also tweak configuration settings // with a `Config` and an `Engine` if desired. System.err.println("Initializing..."); - try (Store store = Store.withoutData()) { - // Compile the wasm binary into an in-memory instance of a `Module`. - System.err.println("Compiling module..."); - try (Engine engine = store.engine(); - Module module = new Module(engine, readWAT("hello.wat"))) - { - // Here we handle the imports of the module, which in this case is our - // `HelloCallback` type and its associated implementation of `Callback. - System.err.println("Creating callback..."); - try (Func helloFunc = WasmFunctions.wrap(store, () -> { - System.err.println("CB!! Calling back..."); - System.err.println("CB!! > Hello World!"); - })) { - // Once we've got that all set up we can then move to the instantiation - // phase, pairing together a compiled module as well as a set of imports. - // Note that this is where the wasm `start` function, if any, would run. - System.err.println("Instantiating module..."); - Collection imports = Arrays.asList(Extern.fromFunc(helloFunc)); - try (Instance instance = new Instance(store, module, imports)) { - // Next we poke around a bit to extract the `run` function from the module. - System.err.println("Extracting export..."); - try (Func f = instance.getFunc(store, "run").get()) { - WasmFunctions.Consumer0 fn = WasmFunctions.consumer(store, f); + try ( + WasiCtx wasi = new WasiCtxBuilder().inheritStdout().inheritStderr().build(); + Store store = Store.withoutData(wasi); + Linker linker = new Linker(store.engine()); + Module module = Module.fromBinary(store.engine(), readBytes("../python/itkwasm/test/input/stdout-stderr-test.wasi.wasm"))) + { + // Here we handle the imports of the module, which in this case is our + // `HelloCallback` type and its associated implementation of `Callback. + System.err.println("Creating callback..."); - // And last but not least we can call it! - System.err.println("Calling export..."); - fn.accept(); - - System.err.println("Done."); - } - } - } - } + WasiCtx.addToLinker(linker); + //linker.define("xyz", "poll_word", Extern.fromFunc(pollWordFn)); + String moduleName = "instance1"; + linker.module(store, moduleName, module); + Extern extern = linker.get(store, moduleName, "").get(); + Consumer0 doWork = WasmFunctions.consumer(store, extern.func()); + doWork.accept(); } } - private static byte[] readWAT(String filename) throws IOException { - try (InputStream is = new FileInputStream("/home/curtis/code/kitware/itk-wasm/packages/core/java/src/main/resources/org/itk/wasm/" + filename)) { - //try (InputStream is = Main.class.getResourceAsStream(filename)) { + private static byte[] readBytes(String filename) throws IOException { + //try (InputStream is = Main.class.getResourceAsStream(filename)) { + try (InputStream is = new FileInputStream(filename)) { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); int nRead; byte[] buf = new byte[16384]; From 30fb527e3d78a9f369695f0f6c4bddedd19bd5d3 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 7 Jun 2023 13:52:39 +0200 Subject: [PATCH 07/24] Remove unused imports --- packages/core/java/src/main/java/org/itk/wasm/Main.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/core/java/src/main/java/org/itk/wasm/Main.java b/packages/core/java/src/main/java/org/itk/wasm/Main.java index 526777842..22b550eba 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/Main.java +++ b/packages/core/java/src/main/java/org/itk/wasm/Main.java @@ -20,10 +20,7 @@ package org.itk.wasm; import io.github.kawamuray.wasmtime.Extern; -import io.github.kawamuray.wasmtime.Func; -import io.github.kawamuray.wasmtime.Instance; import io.github.kawamuray.wasmtime.Linker; -import io.github.kawamuray.wasmtime.Memory; import io.github.kawamuray.wasmtime.Module; import io.github.kawamuray.wasmtime.Store; import io.github.kawamuray.wasmtime.WasmFunctions; @@ -35,9 +32,6 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.util.Arrays; -import java.util.Collection; -import java.util.Optional; public class Main { public static void main(String... args) throws IOException { From dcc84e2f00d5042dbe50102a0f8193a86decaa02 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 7 Jun 2023 13:53:06 +0200 Subject: [PATCH 08/24] Translate some Python classes to Java Thanks ChatGPT for the kickstart. --- .../main/java/org/itk/wasm/BinaryStream.java | 28 +++++++++++++ .../java/org/itk/wasm/InterfaceTypes.java | 40 +++++++++++++++++++ .../main/java/org/itk/wasm/TextStream.java | 28 +++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 packages/core/java/src/main/java/org/itk/wasm/BinaryStream.java create mode 100644 packages/core/java/src/main/java/org/itk/wasm/InterfaceTypes.java create mode 100644 packages/core/java/src/main/java/org/itk/wasm/TextStream.java diff --git a/packages/core/java/src/main/java/org/itk/wasm/BinaryStream.java b/packages/core/java/src/main/java/org/itk/wasm/BinaryStream.java new file mode 100644 index 000000000..1d0bc8fe6 --- /dev/null +++ b/packages/core/java/src/main/java/org/itk/wasm/BinaryStream.java @@ -0,0 +1,28 @@ +/*- + * #%L + * Java bindings for itk-wasm. + * %% + * Copyright (C) 2023 ITK developers. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package org.itk.wasm; + +public class BinaryStream { + public byte[] data; + + public BinaryStream(byte[] data) { + this.data = data; + } +} diff --git a/packages/core/java/src/main/java/org/itk/wasm/InterfaceTypes.java b/packages/core/java/src/main/java/org/itk/wasm/InterfaceTypes.java new file mode 100644 index 000000000..6d81ad76d --- /dev/null +++ b/packages/core/java/src/main/java/org/itk/wasm/InterfaceTypes.java @@ -0,0 +1,40 @@ +/*- + * #%L + * Java bindings for itk-wasm. + * %% + * Copyright (C) 2023 ITK developers. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package org.itk.wasm; +public enum InterfaceTypes { + TextFile("InterfaceTextFile"), + BinaryFile("InterfaceBinaryFile"), + TextStream("InterfaceTextStream"), + BinaryStream("InterfaceBinaryStream"), + Image("InterfaceImage"), + Mesh("InterfaceMesh"), + PolyData("InterfacePolyData"), + JsonObject("InterfaceJsonObject"); + + private String value; + + private InterfaceTypes(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/packages/core/java/src/main/java/org/itk/wasm/TextStream.java b/packages/core/java/src/main/java/org/itk/wasm/TextStream.java new file mode 100644 index 000000000..4af11b839 --- /dev/null +++ b/packages/core/java/src/main/java/org/itk/wasm/TextStream.java @@ -0,0 +1,28 @@ +/*- + * #%L + * Java bindings for itk-wasm. + * %% + * Copyright (C) 2023 ITK developers. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package org.itk.wasm; + +public class TextStream { + public String data; + + public TextStream(String data) { + this.data = data; + } +} From 446830d0f5e390ac5736cc2ad939c924c4c377ce Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 7 Jun 2023 17:36:32 +0200 Subject: [PATCH 09/24] WIP: start migrating Pipeline class --- packages/core/java/pom.xml | 24 ++ .../main/java/org/itk/wasm/BinaryStream.java | 6 +- .../src/main/java/org/itk/wasm/Pipeline.java | 230 ++++++++++++++++++ .../main/java/org/itk/wasm/PipelineInput.java | 7 + .../java/org/itk/wasm/PipelineOutput.java | 7 + .../main/java/org/itk/wasm/TextStream.java | 8 +- 6 files changed, 278 insertions(+), 4 deletions(-) create mode 100644 packages/core/java/src/main/java/org/itk/wasm/Pipeline.java create mode 100644 packages/core/java/src/main/java/org/itk/wasm/PipelineInput.java create mode 100644 packages/core/java/src/main/java/org/itk/wasm/PipelineOutput.java diff --git a/packages/core/java/pom.xml b/packages/core/java/pom.xml index f10b4c991..3102611fd 100644 --- a/packages/core/java/pom.xml +++ b/packages/core/java/pom.xml @@ -83,5 +83,29 @@ net.imglib2 imglib2 + + net.java.dev.jna + jna + + + net.java.dev.jna + jna-platform + + + com.google.code.gson + gson + + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + diff --git a/packages/core/java/src/main/java/org/itk/wasm/BinaryStream.java b/packages/core/java/src/main/java/org/itk/wasm/BinaryStream.java index 1d0bc8fe6..46d1e342e 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/BinaryStream.java +++ b/packages/core/java/src/main/java/org/itk/wasm/BinaryStream.java @@ -19,10 +19,12 @@ */ package org.itk.wasm; +import java.nio.ByteBuffer; + public class BinaryStream { - public byte[] data; + public ByteBuffer data; - public BinaryStream(byte[] data) { + public BinaryStream(ByteBuffer data) { this.data = data; } } diff --git a/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java b/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java new file mode 100644 index 000000000..53aa22808 --- /dev/null +++ b/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java @@ -0,0 +1,230 @@ +import static io.github.kawamuray.wasmtime.WasmValType.I32; + +import io.github.kawamuray.wasmtime.Config; +import io.github.kawamuray.wasmtime.Engine; +import io.github.kawamuray.wasmtime.Extern; +import io.github.kawamuray.wasmtime.Linker; +import io.github.kawamuray.wasmtime.Memory; +import io.github.kawamuray.wasmtime.Module; +import io.github.kawamuray.wasmtime.Store; +import io.github.kawamuray.wasmtime.WasmFunctions; +import io.github.kawamuray.wasmtime.WasmFunctions.Consumer0; +import io.github.kawamuray.wasmtime.WasmFunctions.Consumer1; +import io.github.kawamuray.wasmtime.WasmFunctions.Function0; +import io.github.kawamuray.wasmtime.WasmFunctions.Function1; +import io.github.kawamuray.wasmtime.WasmFunctions.Function2; +import io.github.kawamuray.wasmtime.WasmFunctions.Function3; +import io.github.kawamuray.wasmtime.WasmFunctions.Function4; +import io.github.kawamuray.wasmtime.wasi.WasiCtx; +import io.github.kawamuray.wasmtime.wasi.WasiCtxBuilder; + +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class Pipeline { + private Config config; + private Engine engine; + private Linker linker; + private Module module; + private String moduleName; + + /** TEMP */ + public Pipeline() throws IOException { + this("../python/itkwasm/test/input/stdout-stderr-test.wasi.wasm"); + } + + public Pipeline(String path) throws IOException { + this(readBytes(path)); + } + + public Pipeline(byte[] wasmBytes) throws IOException { + config = new Config(); + config.wasmBulkMemory(true); + config.wasmSimd(true); + //config.wasmMemory64(true); + engine = new Engine(config); + + linker = new Linker(engine); + //linker.allowShadowing(true); + module = new Module(engine, wasmBytes); + } + + public List run(List args, List outputs, List inputs) throws Exception { + WasiCtx wasiConfig = new WasiCtxBuilder() + .inheritEnv() + .inheritStderr() + .inheritStdin() + .inheritStdout() + .args(args).build(); + + Set preopenDirectories = new HashSet<>(); + /* + TODO -- ensure these paths are POSIX style, not Windows + for (int index = 0; index < inputs.size(); index++) { + PipelineInput input = inputs.get(index); + if (input.type == InterfaceTypes.TextFile || input.type == InterfaceTypes.BinaryFile) { + Path parentPath = Path.of(input.data.getPath()).getParent(); + if (parentPath != null) { + preopenDirectories.add(parentPath.toString()); + } + } + } + for (int index = 0; index < outputs.size(); index++) { + PipelineOutput output = outputs.get(index); + if (output.type == InterfaceTypes.TextFile || output.type == InterfaceTypes.BinaryFile) { + Path parentPath = Path.of(output.data.getPath()).getParent(); + if (parentPath != null) { + preopenDirectories.add(parentPath.toString()); + } + } + } + */ + // + for (String preopen : preopenDirectories) { + wasiConfig.pushPreopenDir(Path.of(preopen), preopen); + } + + Store store = new Store<>(engine, wasiConfig); + + WasiCtx.addToLinker(linker); + + // TODO: Decide how to name this more appropriately. + moduleName = "instance1"; + + linker.module(store, moduleName, module); + + Consumer0 main = consumer0(store, ""); + Consumer0 initialize = consumer0(store, "_initialize"); + Function0 delayedStart = func0(store, "itk_wasm_delayed_start"); + Consumer1 delayedExit = consumer1(store, "itk_wasm_delayed_exit"); + Function4 inputArrayAlloc = func4(store, "itk_wasm_input_array_alloc"); + Function3 inputJsonAlloc = func3(store, "itk_wasm_input_json_alloc"); + Function2 outputJsonAddress = func2(store, "itk_wasm_output_json_address"); + Function2 outputJsonSize = func2(store, "itk_wasm_output_json_size"); + Function3 outputArrayAddress = func3(store, "itk_wasm_output_array_address"); + Function3 outputArraySize = func3(store, "itk_wasm_output_array_size"); + Consumer0 freeAll = consumer0(store, "itk_wasm_free_all"); + Memory memory = extern(store, "memory").memory(); + + int returnCode = delayedStart.call(); + + List populatedOutputs = new ArrayList<>(); + if (!outputs.isEmpty() && returnCode == 0) { + for (int index = 0; index < outputs.size(); index++) { + PipelineOutput output = outputs.get(index); + if (output.type == InterfaceTypes.TextStream) { + Pointer dataPtr = outputArrayAddress.invokeP(store, 0, index, 0); + long dataLen = outputArraySize.invokeL(store, 0, index, 0); + byte[] dataBytes = wasmTimeLift(dataPtr, dataLen); + String dataString = new String(dataBytes, StandardCharsets.UTF_8); + TextStream textStream = new TextStream(dataString); + populatedOutputs.add(new PipelineOutput(InterfaceTypes.TextStream, textStream)); + } else if (output.type == InterfaceTypes.BinaryStream) { + Pointer dataPtr = outputArrayAddress.invokeP(store, 0, index, 0); + long dataLen = outputArraySize.invokeL(store, 0, index, 0); + byte[] dataBytes = wasmTimeLift(dataPtr, dataLen); + BinaryStream binaryStream = new BinaryStream(dataBytes); + populatedOutputs.add(new PipelineOutput(InterfaceTypes.BinaryStream, binaryStream)); + } else { + throw new IllegalArgumentException("Unexpected/not yet supported output.type " + output.type); + } + } + } + + DelayedExit delayedExit = instance.getExports(store).get("itk_wasm_delayed_exit"); + delayedExit.invokeV(store, returnCode); + + return populatedOutputs; + } + + private Extern extern(Store store, String name) { + return linker.get(store, moduleName, name).get(); + } + private Consumer0 consumer0(Store store, String name) { + return WasmFunctions.consumer(store, extern(store, name).func()); + } + private Consumer1 consumer1(Store store, String name) { + return WasmFunctions.consumer(store, extern(store, name).func(), I32); + } + private Function0 func0(Store store, String name) { + return WasmFunctions.func(store, extern(store, name).func(), I32); + } + private Function1 func1(Store store, String name) { + return WasmFunctions.func(store, extern(store, name).func(), I32, I32); + } + private Function2 func2(Store store, String name) { + return WasmFunctions.func(store, extern(store, name).func(), I32, I32, I32); + } + private Function3 func3(Store store, String name) { + return WasmFunctions.func(store, extern(store, name).func(), I32, I32, I32, I32); + } + private Function4 func4(Store store, String name) { + return WasmFunctions.func(store, extern(store, name).func(), I32, I32, I32, I32, I32); + } + + private byte[] wasmTimeLift(Pointer ptr, long size) { + long ptrValue = Pointer.nativeValue(ptr); + if (ptrValue + size > memory.capacity()) { + throw new IndexOutOfBoundsException("Attempting to lift out of bounds"); + } + ByteBuffer byteBuffer = memory.getByteBuffer(ptrValue, size); + byte[] data = new byte[byteBuffer.remaining()]; + byteBuffer.get(data); + return data; + } + + private void wasmTimeLower(Pointer ptr, byte[] data) { + long ptrValue = Pointer.nativeValue(ptr); + long size = data.length; + if (ptrValue + size > memory.capacity()) { + throw new IndexOutOfBoundsException("Attempting to lower out of bounds"); + } + ByteBuffer byteBuffer = memory.getByteBuffer(ptrValue, size); + byteBuffer.put(data); + } + + private Pointer setInputArray(byte[] dataArray, int inputIndex, int subIndex) { + Pointer dataPtr = new Memory(dataArray.length); + dataPtr.write(0, dataArray, 0, dataArray.length); + Pointer resultPtr = inputArrayAlloc.invokeP(store, 0, inputIndex, subIndex, dataArray.length); + wasmTimeLower(resultPtr, dataArray); + return resultPtr; + } + + private void setInputJson(Map dataObject, int inputIndex) throws JsonProcessingException { + byte[] dataJson = objectMapper.writeValueAsBytes(dataObject); + Pointer jsonPtr = inputJsonAlloc.invokeP(store, 0, inputIndex, dataJson.length); + wasmTimeLower(jsonPtr, dataJson); + } + + private Map getOutputJson(int outputIndex) throws IOException { + Pointer jsonPtr = outputJsonAddress.invokeP(store, 0, outputIndex); + long jsonLen = outputJsonSize.invokeL(store, 0, outputIndex); + byte[] jsonBytes = wasmTimeLift(jsonPtr, jsonLen); + String jsonString = new String(jsonBytes, StandardCharsets.UTF_8); + return objectMapper.readValue(jsonString, Map.class); + } + + private static byte[] readBytes(String filename) throws IOException { + //try (InputStream is = Main.class.getResourceAsStream(filename)) { + try (InputStream is = new FileInputStream(filename)) { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int nRead; + byte[] buf = new byte[16384]; + while ((nRead = is.read(buf, 0, buf.length)) != -1) { + buffer.write(buf, 0, nRead); + } + return buffer.toByteArray(); + } + } +} diff --git a/packages/core/java/src/main/java/org/itk/wasm/PipelineInput.java b/packages/core/java/src/main/java/org/itk/wasm/PipelineInput.java new file mode 100644 index 000000000..8e4ac6deb --- /dev/null +++ b/packages/core/java/src/main/java/org/itk/wasm/PipelineInput.java @@ -0,0 +1,7 @@ +package org.itk.wasm; + +public class PipelineInput { + public InterfaceTypes type; + public T data; + public String path; +} diff --git a/packages/core/java/src/main/java/org/itk/wasm/PipelineOutput.java b/packages/core/java/src/main/java/org/itk/wasm/PipelineOutput.java new file mode 100644 index 000000000..603343e21 --- /dev/null +++ b/packages/core/java/src/main/java/org/itk/wasm/PipelineOutput.java @@ -0,0 +1,7 @@ +package org.itk.wasm; + +public class PipelineOutput { + public InterfaceTypes type; + public T data; + public String path; +} diff --git a/packages/core/java/src/main/java/org/itk/wasm/TextStream.java b/packages/core/java/src/main/java/org/itk/wasm/TextStream.java index 4af11b839..9b89469b3 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/TextStream.java +++ b/packages/core/java/src/main/java/org/itk/wasm/TextStream.java @@ -19,10 +19,14 @@ */ package org.itk.wasm; +import java.nio.ByteBuffer; + public class TextStream { - public String data; + public ByteBuffer data; - public TextStream(String data) { + public TextStream(ByteBuffer data) { this.data = data; } + + // TODO: implement toString to copy data into a String ? } From fb4d1ea4f32a31b99e3d58a8650407b6989a793e Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 7 Jun 2023 18:44:37 +0200 Subject: [PATCH 10/24] WIP: continuing Pipeline implementation --- .../main/java/org/itk/wasm/BinaryStream.java | 6 +- .../src/main/java/org/itk/wasm/Pipeline.java | 422 +++++++++++------- .../main/java/org/itk/wasm/PipelineInput.java | 19 + .../java/org/itk/wasm/PipelineOutput.java | 24 + .../main/java/org/itk/wasm/TextStream.java | 8 +- 5 files changed, 299 insertions(+), 180 deletions(-) diff --git a/packages/core/java/src/main/java/org/itk/wasm/BinaryStream.java b/packages/core/java/src/main/java/org/itk/wasm/BinaryStream.java index 46d1e342e..1d0bc8fe6 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/BinaryStream.java +++ b/packages/core/java/src/main/java/org/itk/wasm/BinaryStream.java @@ -19,12 +19,10 @@ */ package org.itk.wasm; -import java.nio.ByteBuffer; - public class BinaryStream { - public ByteBuffer data; + public byte[] data; - public BinaryStream(ByteBuffer data) { + public BinaryStream(byte[] data) { this.data = data; } } diff --git a/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java b/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java index 53aa22808..3e919bed1 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java +++ b/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java @@ -1,5 +1,31 @@ +/*- + * #%L + * Java bindings for itk-wasm. + * %% + * Copyright (C) 2023 ITK developers. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package org.itk.wasm; + import static io.github.kawamuray.wasmtime.WasmValType.I32; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.reflect.TypeToken; + import io.github.kawamuray.wasmtime.Config; import io.github.kawamuray.wasmtime.Engine; import io.github.kawamuray.wasmtime.Extern; @@ -25,6 +51,7 @@ import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -32,199 +59,254 @@ import java.util.Set; public class Pipeline { - private Config config; - private Engine engine; - private Linker linker; - private Module module; - private String moduleName; - - /** TEMP */ - public Pipeline() throws IOException { - this("../python/itkwasm/test/input/stdout-stderr-test.wasi.wasm"); - } - public Pipeline(String path) throws IOException { - this(readBytes(path)); - } + private Config config; + private Engine engine; + private Linker linker; + private Module module; + private String moduleName; - public Pipeline(byte[] wasmBytes) throws IOException { - config = new Config(); - config.wasmBulkMemory(true); - config.wasmSimd(true); - //config.wasmMemory64(true); - engine = new Engine(config); + /** TEMP */ + public Pipeline() throws IOException { + this("../python/itkwasm/test/input/stdout-stderr-test.wasi.wasm"); + } - linker = new Linker(engine); - //linker.allowShadowing(true); - module = new Module(engine, wasmBytes); + public Pipeline(String path) throws IOException { + this(readBytes(path)); + } + + public Pipeline(byte[] wasmBytes) { + config = new Config(); + config.wasmBulkMemory(true); + config.wasmSimd(true); + //config.wasmMemory64(true); + engine = new Engine(config); + + linker = new Linker(engine); + //linker.allowShadowing(true); + module = new Module(engine, wasmBytes); + } + + public List> run(List args, List> outputs, List> inputs) { + try (RunInstance ri = new RunInstance(args, outputs, inputs)) { + int returnCode = ri.delayedStart(); + + List> populatedOutputs = new ArrayList<>(); + if (!outputs.isEmpty() && returnCode == 0) { + for (int index = 0; index < outputs.size(); index++) { + PipelineOutput output = outputs.get(index); + if (output.type == InterfaceTypes.TextStream) { + int dataPtr = ri.outputArrayAddress(0, index, 0); + int dataLen = ri.outputArraySize(0, index, 0); + byte[] dataBytes = ri.wasmTimeLift(dataPtr, dataLen); + String dataString = str(dataBytes); + TextStream textStream = new TextStream(dataString); + populatedOutputs.add(new PipelineOutput<>(InterfaceTypes.TextStream, textStream)); + } else if (output.type == InterfaceTypes.BinaryStream) { + int dataPtr = ri.outputArrayAddress(0, index, 0); + int dataLen = ri.outputArraySize(0, index, 0); + byte[] dataBytes = ri.wasmTimeLift(dataPtr, dataLen); + BinaryStream binaryStream = new BinaryStream(dataBytes); + populatedOutputs.add(new PipelineOutput<>(InterfaceTypes.BinaryStream, binaryStream)); + } else { + throw new IllegalArgumentException("Unexpected/not yet supported output.type " + output.type); + } + } + } + + return populatedOutputs; + } + } + + private static String purePosixPath(Path p) { + // TODO -- ensure path is POSIX style, not Windows + return p.toString(); + } + + private static String str(byte[] bytes) { + return new String(bytes, StandardCharsets.UTF_8); + } + private static byte[] bytes(String str) { + return str.getBytes(StandardCharsets.UTF_8); + } + + private static byte[] readBytes(String filename) throws IOException { + //try (InputStream is = Main.class.getResourceAsStream(filename)) { + try (InputStream is = new FileInputStream(filename)) { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int nRead; + byte[] buf = new byte[16384]; + while ((nRead = is.read(buf, 0, buf.length)) != -1) { + buffer.write(buf, 0, nRead); + } + return buffer.toByteArray(); } + } - public List run(List args, List outputs, List inputs) throws Exception { - WasiCtx wasiConfig = new WasiCtxBuilder() - .inheritEnv() - .inheritStderr() - .inheritStdin() - .inheritStdout() - .args(args).build(); - - Set preopenDirectories = new HashSet<>(); - /* - TODO -- ensure these paths are POSIX style, not Windows - for (int index = 0; index < inputs.size(); index++) { - PipelineInput input = inputs.get(index); - if (input.type == InterfaceTypes.TextFile || input.type == InterfaceTypes.BinaryFile) { - Path parentPath = Path.of(input.data.getPath()).getParent(); - if (parentPath != null) { - preopenDirectories.add(parentPath.toString()); - } - } - } - for (int index = 0; index < outputs.size(); index++) { - PipelineOutput output = outputs.get(index); - if (output.type == InterfaceTypes.TextFile || output.type == InterfaceTypes.BinaryFile) { - Path parentPath = Path.of(output.data.getPath()).getParent(); - if (parentPath != null) { - preopenDirectories.add(parentPath.toString()); - } - } + private Extern extern(Store store, String name) { + return linker.get(store, moduleName, name).get(); + } + private Consumer0 consumer0(Store store, String name) { + return WasmFunctions.consumer(store, extern(store, name).func()); + } + private Consumer1 consumer1(Store store, String name) { + return WasmFunctions.consumer(store, extern(store, name).func(), I32); + } + private Function0 func0(Store store, String name) { + return WasmFunctions.func(store, extern(store, name).func(), I32); + } + private Function1 func1(Store store, String name) { + return WasmFunctions.func(store, extern(store, name).func(), I32, I32); + } + private Function2 func2(Store store, String name) { + return WasmFunctions.func(store, extern(store, name).func(), I32, I32, I32); + } + private Function3 func3(Store store, String name) { + return WasmFunctions.func(store, extern(store, name).func(), I32, I32, I32, I32); + } + private Function4 func4(Store store, String name) { + return WasmFunctions.func(store, extern(store, name).func(), I32, I32, I32, I32, I32); + } + + public class RunInstance implements AutoCloseable { + + private Store store; + + private Consumer0 main; + private Consumer0 initialize; + private Function0 delayedStart; + private Consumer1 delayedExit; + private Function4 inputArrayAlloc; + private Function3 inputJsonAlloc; + private Function2 outputJsonAddress; + private Function2 outputJsonSize; + private Function3 outputArrayAddress; + private Function3 outputArraySize; + private Consumer0 freeAll; + private Memory memory; + + public RunInstance(List args, List> outputs, + List> inputs) + { + WasiCtx wasiConfig = new WasiCtxBuilder() + .inheritEnv() + .inheritStderr() + .inheritStdin() + .inheritStdout() + .args(args).build(); + + Set preopenDirectories = new HashSet<>(); + for (PipelineInput input : inputs) { + if (input.type == InterfaceTypes.TextFile || input.type == InterfaceTypes.BinaryFile) { + // TODO: enable once TextFile/BinaryFile exists + //Path path = ((TextFile) input.data).path; + Path path = null; + preopenDirectories.add(purePosixPath(path.getParent())); } - */ - // - for (String preopen : preopenDirectories) { - wasiConfig.pushPreopenDir(Path.of(preopen), preopen); + } + for (PipelineOutput output : outputs) { + if (output.type == InterfaceTypes.TextFile || output.type == InterfaceTypes.BinaryFile) { + // TODO: enable once TextFile/BinaryFile exists + //Path path = ((TextFile) input.data).path; + Path path = null; + preopenDirectories.add(purePosixPath(path.getParent())); } + } - Store store = new Store<>(engine, wasiConfig); - - WasiCtx.addToLinker(linker); - - // TODO: Decide how to name this more appropriately. - moduleName = "instance1"; - - linker.module(store, moduleName, module); - - Consumer0 main = consumer0(store, ""); - Consumer0 initialize = consumer0(store, "_initialize"); - Function0 delayedStart = func0(store, "itk_wasm_delayed_start"); - Consumer1 delayedExit = consumer1(store, "itk_wasm_delayed_exit"); - Function4 inputArrayAlloc = func4(store, "itk_wasm_input_array_alloc"); - Function3 inputJsonAlloc = func3(store, "itk_wasm_input_json_alloc"); - Function2 outputJsonAddress = func2(store, "itk_wasm_output_json_address"); - Function2 outputJsonSize = func2(store, "itk_wasm_output_json_size"); - Function3 outputArrayAddress = func3(store, "itk_wasm_output_array_address"); - Function3 outputArraySize = func3(store, "itk_wasm_output_array_size"); - Consumer0 freeAll = consumer0(store, "itk_wasm_free_all"); - Memory memory = extern(store, "memory").memory(); - - int returnCode = delayedStart.call(); - - List populatedOutputs = new ArrayList<>(); - if (!outputs.isEmpty() && returnCode == 0) { - for (int index = 0; index < outputs.size(); index++) { - PipelineOutput output = outputs.get(index); - if (output.type == InterfaceTypes.TextStream) { - Pointer dataPtr = outputArrayAddress.invokeP(store, 0, index, 0); - long dataLen = outputArraySize.invokeL(store, 0, index, 0); - byte[] dataBytes = wasmTimeLift(dataPtr, dataLen); - String dataString = new String(dataBytes, StandardCharsets.UTF_8); - TextStream textStream = new TextStream(dataString); - populatedOutputs.add(new PipelineOutput(InterfaceTypes.TextStream, textStream)); - } else if (output.type == InterfaceTypes.BinaryStream) { - Pointer dataPtr = outputArrayAddress.invokeP(store, 0, index, 0); - long dataLen = outputArraySize.invokeL(store, 0, index, 0); - byte[] dataBytes = wasmTimeLift(dataPtr, dataLen); - BinaryStream binaryStream = new BinaryStream(dataBytes); - populatedOutputs.add(new PipelineOutput(InterfaceTypes.BinaryStream, binaryStream)); - } else { - throw new IllegalArgumentException("Unexpected/not yet supported output.type " + output.type); - } - } - } + for (String preopen : preopenDirectories) { + Path p = Paths.get(preopen); + wasiConfig.pushPreopenDir(p, preopen); + } + + store = new Store<>(engine, wasiConfig); + + WasiCtx.addToLinker(linker); + + // TODO: Decide how to name this more appropriately. + moduleName = "instance1"; - DelayedExit delayedExit = instance.getExports(store).get("itk_wasm_delayed_exit"); - delayedExit.invokeV(store, returnCode); + linker.module(store, moduleName, module); - return populatedOutputs; + main = consumer0(store, ""); + initialize = consumer0(store, "_initialize"); + delayedStart = func0(store, "itk_wasm_delayed_start"); + delayedExit = consumer1(store, "itk_wasm_delayed_exit"); + inputArrayAlloc = func4(store, "itk_wasm_input_array_alloc"); + inputJsonAlloc = func3(store, "itk_wasm_input_json_alloc"); + outputJsonAddress = func2(store, "itk_wasm_output_json_address"); + outputJsonSize = func2(store, "itk_wasm_output_json_size"); + outputArrayAddress = func3(store, "itk_wasm_output_array_address"); + outputArraySize = func3(store, "itk_wasm_output_array_size"); + freeAll = consumer0(store, "itk_wasm_free_all"); + memory = extern(store, "memory").memory(); } - private Extern extern(Store store, String name) { - return linker.get(store, moduleName, name).get(); + public Integer delayedStart() { return delayedStart.call(); } + public void delayedExit(Integer i) { delayedExit.accept(i); } + public Integer inputArrayAlloc(Integer i1, Integer i2, Integer i3, Integer i4) { return inputArrayAlloc.call(i1, i2, i3, i4); } + public Integer inputJsonAlloc(Integer i1, Integer i2, Integer i3) { return inputJsonAlloc.call(i1, i2, i3); } + public Integer outputJsonAddress(Integer i1, Integer i2) { return outputJsonAddress.call(i1, i2); } + public Integer outputJsonSize(Integer i1, Integer i2) { return outputJsonSize.call(i1, i2); } + public Integer outputArrayAddress(Integer i1, Integer i2, Integer i3) { return outputArrayAddress.call(i1, i2, i3); } + public Integer outputArraySize(Integer i1, Integer i2, Integer i3) { return outputArraySize.call(i1, i2, i3); } + public void freeAll() { freeAll.accept(); } + + public ByteBuffer memoryBuffer(int offset, int length) { + ByteBuffer buffer = memory.buffer(store); + buffer.position(offset); + buffer.limit(length); + return buffer.slice(); } - private Consumer0 consumer0(Store store, String name) { - return WasmFunctions.consumer(store, extern(store, name).func()); - } - private Consumer1 consumer1(Store store, String name) { - return WasmFunctions.consumer(store, extern(store, name).func(), I32); - } - private Function0 func0(Store store, String name) { - return WasmFunctions.func(store, extern(store, name).func(), I32); - } - private Function1 func1(Store store, String name) { - return WasmFunctions.func(store, extern(store, name).func(), I32, I32); - } - private Function2 func2(Store store, String name) { - return WasmFunctions.func(store, extern(store, name).func(), I32, I32, I32); - } - private Function3 func3(Store store, String name) { - return WasmFunctions.func(store, extern(store, name).func(), I32, I32, I32, I32); - } - private Function4 func4(Store store, String name) { - return WasmFunctions.func(store, extern(store, name).func(), I32, I32, I32, I32, I32); - } - - private byte[] wasmTimeLift(Pointer ptr, long size) { - long ptrValue = Pointer.nativeValue(ptr); - if (ptrValue + size > memory.capacity()) { - throw new IndexOutOfBoundsException("Attempting to lift out of bounds"); - } - ByteBuffer byteBuffer = memory.getByteBuffer(ptrValue, size); - byte[] data = new byte[byteBuffer.remaining()]; - byteBuffer.get(data); - return data; + public int memorySize() { return memory.size(store); } + + @Override + public void close() { + store.close(); } - private void wasmTimeLower(Pointer ptr, byte[] data) { - long ptrValue = Pointer.nativeValue(ptr); - long size = data.length; - if (ptrValue + size > memory.capacity()) { - throw new IndexOutOfBoundsException("Attempting to lower out of bounds"); - } - ByteBuffer byteBuffer = memory.getByteBuffer(ptrValue, size); - byteBuffer.put(data); + private byte[] wasmTimeLift(int offset, int length) { + if (offset + length > memorySize()) { + throw new IndexOutOfBoundsException("Attempting to lift out of bounds"); + } + ByteBuffer byteBuffer = memoryBuffer(offset, length); + byte[] data = new byte[byteBuffer.remaining()]; + byteBuffer.get(data); + return data; } - private Pointer setInputArray(byte[] dataArray, int inputIndex, int subIndex) { - Pointer dataPtr = new Memory(dataArray.length); - dataPtr.write(0, dataArray, 0, dataArray.length); - Pointer resultPtr = inputArrayAlloc.invokeP(store, 0, inputIndex, subIndex, dataArray.length); - wasmTimeLower(resultPtr, dataArray); - return resultPtr; + private void wasmTimeLower(int offset, byte[] data) { + int size = data.length; + if (offset + size > memorySize()) { + throw new IndexOutOfBoundsException("Attempting to lower out of bounds"); + } + ByteBuffer byteBuffer = memoryBuffer(offset, size); + byteBuffer.put(data); } - private void setInputJson(Map dataObject, int inputIndex) throws JsonProcessingException { - byte[] dataJson = objectMapper.writeValueAsBytes(dataObject); - Pointer jsonPtr = inputJsonAlloc.invokeP(store, 0, inputIndex, dataJson.length); - wasmTimeLower(jsonPtr, dataJson); + private int setInputArray(byte[] dataArray, int inputIndex, int subIndex) { + int dataPtr = 0; + if (dataArray != null) { + dataPtr = inputArrayAlloc(0, inputIndex, subIndex, dataArray.length); + wasmTimeLower(dataPtr, dataArray); + } + return dataPtr; } - private Map getOutputJson(int outputIndex) throws IOException { - Pointer jsonPtr = outputJsonAddress.invokeP(store, 0, outputIndex); - long jsonLen = outputJsonSize.invokeL(store, 0, outputIndex); - byte[] jsonBytes = wasmTimeLift(jsonPtr, jsonLen); - String jsonString = new String(jsonBytes, StandardCharsets.UTF_8); - return objectMapper.readValue(jsonString, Map.class); + private void setInputJson(Map dataObject, int inputIndex) { + Gson gson = new GsonBuilder().create(); + JsonElement jsonElement = gson.toJsonTree(dataObject); + byte[] dataJson = bytes(jsonElement.toString()); + int jsonPtr = inputJsonAlloc(0, inputIndex, dataJson.length); + wasmTimeLower(jsonPtr, dataJson); } - private static byte[] readBytes(String filename) throws IOException { - //try (InputStream is = Main.class.getResourceAsStream(filename)) { - try (InputStream is = new FileInputStream(filename)) { - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - int nRead; - byte[] buf = new byte[16384]; - while ((nRead = is.read(buf, 0, buf.length)) != -1) { - buffer.write(buf, 0, nRead); - } - return buffer.toByteArray(); + private Map getOutputJson(int outputIndex) { + int jsonPtr = outputJsonAddress(0, outputIndex); + int jsonLen = outputJsonSize(0, outputIndex); + byte[] jsonBytes = wasmTimeLift(jsonPtr, jsonLen); + String jsonString = str(jsonBytes); + Gson gson = new GsonBuilder().create(); + return gson.fromJson(jsonString, new TypeToken>() {}); } } } diff --git a/packages/core/java/src/main/java/org/itk/wasm/PipelineInput.java b/packages/core/java/src/main/java/org/itk/wasm/PipelineInput.java index 8e4ac6deb..9a207030a 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/PipelineInput.java +++ b/packages/core/java/src/main/java/org/itk/wasm/PipelineInput.java @@ -1,3 +1,22 @@ +/*- + * #%L + * Java bindings for itk-wasm. + * %% + * Copyright (C) 2023 ITK developers. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ package org.itk.wasm; public class PipelineInput { diff --git a/packages/core/java/src/main/java/org/itk/wasm/PipelineOutput.java b/packages/core/java/src/main/java/org/itk/wasm/PipelineOutput.java index 603343e21..9c2ae76c0 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/PipelineOutput.java +++ b/packages/core/java/src/main/java/org/itk/wasm/PipelineOutput.java @@ -1,7 +1,31 @@ +/*- + * #%L + * Java bindings for itk-wasm. + * %% + * Copyright (C) 2023 ITK developers. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ package org.itk.wasm; public class PipelineOutput { public InterfaceTypes type; public T data; public String path; + + public PipelineOutput(InterfaceTypes type, T data) { + this.type = type; + this.data = data; + } } diff --git a/packages/core/java/src/main/java/org/itk/wasm/TextStream.java b/packages/core/java/src/main/java/org/itk/wasm/TextStream.java index 9b89469b3..4af11b839 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/TextStream.java +++ b/packages/core/java/src/main/java/org/itk/wasm/TextStream.java @@ -19,14 +19,10 @@ */ package org.itk.wasm; -import java.nio.ByteBuffer; - public class TextStream { - public ByteBuffer data; + public String data; - public TextStream(ByteBuffer data) { + public TextStream(String data) { this.data = data; } - - // TODO: implement toString to copy data into a String ? } From 73a3972e4277eb53c79b801238d9ad4baac12ce1 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 7 Jun 2023 19:04:46 +0200 Subject: [PATCH 11/24] Fix whitespace --- .../main/java/org/itk/wasm/BinaryStream.java | 8 +-- .../java/org/itk/wasm/InterfaceTypes.java | 30 ++++----- .../src/main/java/org/itk/wasm/Pipeline.java | 64 +++++++++---------- .../main/java/org/itk/wasm/PipelineInput.java | 6 +- .../java/org/itk/wasm/PipelineOutput.java | 14 ++-- .../main/java/org/itk/wasm/TextStream.java | 8 +-- 6 files changed, 65 insertions(+), 65 deletions(-) diff --git a/packages/core/java/src/main/java/org/itk/wasm/BinaryStream.java b/packages/core/java/src/main/java/org/itk/wasm/BinaryStream.java index 1d0bc8fe6..617b33e5c 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/BinaryStream.java +++ b/packages/core/java/src/main/java/org/itk/wasm/BinaryStream.java @@ -20,9 +20,9 @@ package org.itk.wasm; public class BinaryStream { - public byte[] data; + public byte[] data; - public BinaryStream(byte[] data) { - this.data = data; - } + public BinaryStream(byte[] data) { + this.data = data; + } } diff --git a/packages/core/java/src/main/java/org/itk/wasm/InterfaceTypes.java b/packages/core/java/src/main/java/org/itk/wasm/InterfaceTypes.java index 6d81ad76d..fcb513ee7 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/InterfaceTypes.java +++ b/packages/core/java/src/main/java/org/itk/wasm/InterfaceTypes.java @@ -19,22 +19,22 @@ */ package org.itk.wasm; public enum InterfaceTypes { - TextFile("InterfaceTextFile"), - BinaryFile("InterfaceBinaryFile"), - TextStream("InterfaceTextStream"), - BinaryStream("InterfaceBinaryStream"), - Image("InterfaceImage"), - Mesh("InterfaceMesh"), - PolyData("InterfacePolyData"), - JsonObject("InterfaceJsonObject"); + TextFile("InterfaceTextFile"), + BinaryFile("InterfaceBinaryFile"), + TextStream("InterfaceTextStream"), + BinaryStream("InterfaceBinaryStream"), + Image("InterfaceImage"), + Mesh("InterfaceMesh"), + PolyData("InterfacePolyData"), + JsonObject("InterfaceJsonObject"); - private String value; + private String value; - private InterfaceTypes(String value) { - this.value = value; - } + private InterfaceTypes(String value) { + this.value = value; + } - public String getValue() { - return value; - } + public String getValue() { + return value; + } } diff --git a/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java b/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java index 3e919bed1..746d86aef 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java +++ b/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java @@ -88,34 +88,34 @@ public Pipeline(byte[] wasmBytes) { } public List> run(List args, List> outputs, List> inputs) { - try (RunInstance ri = new RunInstance(args, outputs, inputs)) { - int returnCode = ri.delayedStart(); - - List> populatedOutputs = new ArrayList<>(); - if (!outputs.isEmpty() && returnCode == 0) { - for (int index = 0; index < outputs.size(); index++) { - PipelineOutput output = outputs.get(index); - if (output.type == InterfaceTypes.TextStream) { - int dataPtr = ri.outputArrayAddress(0, index, 0); - int dataLen = ri.outputArraySize(0, index, 0); - byte[] dataBytes = ri.wasmTimeLift(dataPtr, dataLen); - String dataString = str(dataBytes); - TextStream textStream = new TextStream(dataString); - populatedOutputs.add(new PipelineOutput<>(InterfaceTypes.TextStream, textStream)); - } else if (output.type == InterfaceTypes.BinaryStream) { - int dataPtr = ri.outputArrayAddress(0, index, 0); - int dataLen = ri.outputArraySize(0, index, 0); - byte[] dataBytes = ri.wasmTimeLift(dataPtr, dataLen); - BinaryStream binaryStream = new BinaryStream(dataBytes); - populatedOutputs.add(new PipelineOutput<>(InterfaceTypes.BinaryStream, binaryStream)); - } else { - throw new IllegalArgumentException("Unexpected/not yet supported output.type " + output.type); - } - } - } - - return populatedOutputs; - } + try (RunInstance ri = new RunInstance(args, outputs, inputs)) { + int returnCode = ri.delayedStart(); + + List> populatedOutputs = new ArrayList<>(); + if (!outputs.isEmpty() && returnCode == 0) { + for (int index = 0; index < outputs.size(); index++) { + PipelineOutput output = outputs.get(index); + if (output.type == InterfaceTypes.TextStream) { + int dataPtr = ri.outputArrayAddress(0, index, 0); + int dataLen = ri.outputArraySize(0, index, 0); + byte[] dataBytes = ri.wasmTimeLift(dataPtr, dataLen); + String dataString = str(dataBytes); + TextStream textStream = new TextStream(dataString); + populatedOutputs.add(new PipelineOutput<>(InterfaceTypes.TextStream, textStream)); + } else if (output.type == InterfaceTypes.BinaryStream) { + int dataPtr = ri.outputArrayAddress(0, index, 0); + int dataLen = ri.outputArraySize(0, index, 0); + byte[] dataBytes = ri.wasmTimeLift(dataPtr, dataLen); + BinaryStream binaryStream = new BinaryStream(dataBytes); + populatedOutputs.add(new PipelineOutput<>(InterfaceTypes.BinaryStream, binaryStream)); + } else { + throw new IllegalArgumentException("Unexpected/not yet supported output.type " + output.type); + } + } + } + + return populatedOutputs; + } } private static String purePosixPath(Path p) { @@ -252,10 +252,10 @@ public RunInstance(List args, List> outputs, public void freeAll() { freeAll.accept(); } public ByteBuffer memoryBuffer(int offset, int length) { - ByteBuffer buffer = memory.buffer(store); - buffer.position(offset); - buffer.limit(length); - return buffer.slice(); + ByteBuffer buffer = memory.buffer(store); + buffer.position(offset); + buffer.limit(length); + return buffer.slice(); } public int memorySize() { return memory.size(store); } diff --git a/packages/core/java/src/main/java/org/itk/wasm/PipelineInput.java b/packages/core/java/src/main/java/org/itk/wasm/PipelineInput.java index 9a207030a..d92882b7f 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/PipelineInput.java +++ b/packages/core/java/src/main/java/org/itk/wasm/PipelineInput.java @@ -20,7 +20,7 @@ package org.itk.wasm; public class PipelineInput { - public InterfaceTypes type; - public T data; - public String path; + public InterfaceTypes type; + public T data; + public String path; } diff --git a/packages/core/java/src/main/java/org/itk/wasm/PipelineOutput.java b/packages/core/java/src/main/java/org/itk/wasm/PipelineOutput.java index 9c2ae76c0..d706273a3 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/PipelineOutput.java +++ b/packages/core/java/src/main/java/org/itk/wasm/PipelineOutput.java @@ -20,12 +20,12 @@ package org.itk.wasm; public class PipelineOutput { - public InterfaceTypes type; - public T data; - public String path; + public InterfaceTypes type; + public T data; + public String path; - public PipelineOutput(InterfaceTypes type, T data) { - this.type = type; - this.data = data; - } + public PipelineOutput(InterfaceTypes type, T data) { + this.type = type; + this.data = data; + } } diff --git a/packages/core/java/src/main/java/org/itk/wasm/TextStream.java b/packages/core/java/src/main/java/org/itk/wasm/TextStream.java index 4af11b839..30dc4ed1e 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/TextStream.java +++ b/packages/core/java/src/main/java/org/itk/wasm/TextStream.java @@ -20,9 +20,9 @@ package org.itk.wasm; public class TextStream { - public String data; + public String data; - public TextStream(String data) { - this.data = data; - } + public TextStream(String data) { + this.data = data; + } } From 485a18c622e1d472d6d64da21ae3cc5da29fc247 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 7 Jun 2023 21:51:44 +0200 Subject: [PATCH 12/24] Migrate packages test files to packages root --- packages/core/python/itkwasm/test/test_image.py | 4 ++-- packages/core/python/itkwasm/test/test_mesh.py | 4 ++-- packages/core/python/itkwasm/test/test_pipeline.py | 5 +++-- .../baseline/test_pipeline_write_read_image.png | Bin .../itkwasm/test => test/data}/input/cow.vtk | 0 .../itkwasm/test => test/data}/input/cthead1.png | Bin .../data}/input/input-output-files-test.wasi.wasm | Bin .../itkwasm/test => test/data}/input/input.bin | 0 .../itkwasm/test => test/data}/input/input.txt | 0 .../data}/input/median-filter-test.wasi.wasm | Bin .../data}/input/mesh-read-write-test.wasi.wasm | Bin .../data}/input/mesh-to-poly-data.wasi.wasm | Bin .../data}/input/poly-data-to-mesh.wasi.wasm | Bin .../data}/input/stdout-stderr-test.wasi.wasm | Bin 14 files changed, 7 insertions(+), 6 deletions(-) rename packages/core/{python/itkwasm/test => test/data}/baseline/test_pipeline_write_read_image.png (100%) rename packages/core/{python/itkwasm/test => test/data}/input/cow.vtk (100%) rename packages/core/{python/itkwasm/test => test/data}/input/cthead1.png (100%) rename packages/core/{python/itkwasm/test => test/data}/input/input-output-files-test.wasi.wasm (100%) rename packages/core/{python/itkwasm/test => test/data}/input/input.bin (100%) rename packages/core/{python/itkwasm/test => test/data}/input/input.txt (100%) rename packages/core/{python/itkwasm/test => test/data}/input/median-filter-test.wasi.wasm (100%) rename packages/core/{python/itkwasm/test => test/data}/input/mesh-read-write-test.wasi.wasm (100%) rename packages/core/{python/itkwasm/test => test/data}/input/mesh-to-poly-data.wasi.wasm (100%) rename packages/core/{python/itkwasm/test => test/data}/input/poly-data-to-mesh.wasi.wasm (100%) rename packages/core/{python/itkwasm/test => test/data}/input/stdout-stderr-test.wasi.wasm (100%) diff --git a/packages/core/python/itkwasm/test/test_image.py b/packages/core/python/itkwasm/test/test_image.py index e9a649421..d26087611 100644 --- a/packages/core/python/itkwasm/test/test_image.py +++ b/packages/core/python/itkwasm/test/test_image.py @@ -7,7 +7,7 @@ import numpy as np def test_image(): - data = Path(__file__).absolute().parent / "input" / "cthead1.png" + data = Path(__file__).absolute().parent.parent.parent.parent / "test" / "data" / "input" / "cthead1.png" itk_image = itk.imread(data, itk.UC) itk_image_dict = itk.dict_from_image(itk_image) itkwasm_image = Image(**itk_image_dict) @@ -35,4 +35,4 @@ def test_image_defaults(): assert image.size[1] == 1 assert isinstance(image.metadata, dict) - assert image.data == None \ No newline at end of file + assert image.data == None diff --git a/packages/core/python/itkwasm/test/test_mesh.py b/packages/core/python/itkwasm/test/test_mesh.py index 01c96ae5f..d657b0e51 100644 --- a/packages/core/python/itkwasm/test/test_mesh.py +++ b/packages/core/python/itkwasm/test/test_mesh.py @@ -7,7 +7,7 @@ import numpy as np def test_mesh(): - data = Path(__file__).absolute().parent / "input" / "cow.vtk" + data = Path(__file__).absolute().parent.parent.parent.parent / "test" / "data" / "input" / "cow.vtk" itk_mesh = itk.meshread(data) itk_mesh_dict = itk.dict_from_mesh(itk_mesh) @@ -43,4 +43,4 @@ def test_mesh(): assert itk_mesh_dict["cellBufferSize"] == itk_mesh_roundtrip_dict["cellBufferSize"] assert itk_mesh_dict["numberOfCellPixels"] == itk_mesh_roundtrip_dict["numberOfCellPixels"] - assert np.array_equal(itk_mesh_dict["cellData"], itk_mesh_roundtrip_dict["cellData"]) \ No newline at end of file + assert np.array_equal(itk_mesh_dict["cellData"], itk_mesh_roundtrip_dict["cellData"]) diff --git a/packages/core/python/itkwasm/test/test_pipeline.py b/packages/core/python/itkwasm/test/test_pipeline.py index 666ed5ef7..1470cda54 100644 --- a/packages/core/python/itkwasm/test/test_pipeline.py +++ b/packages/core/python/itkwasm/test/test_pipeline.py @@ -10,8 +10,9 @@ from itkwasm import InterfaceTypes, TextStream, BinaryStream, PipelineInput, PipelineOutput, Pipeline, TextFile, BinaryFile, Image, Mesh -test_input_dir = Path(__file__).resolve().parent / 'input' -test_baseline_dir = Path(__file__).resolve().parent / 'baseline' +test_data_dir = Path(__file__).resolve().parent.parent.parent.parent / 'test' / 'data' +test_input_dir = test_data_dir / 'input' +test_baseline_dir = test_data_dir / 'baseline' def test_stdout_stderr(): diff --git a/packages/core/python/itkwasm/test/baseline/test_pipeline_write_read_image.png b/packages/core/test/data/baseline/test_pipeline_write_read_image.png similarity index 100% rename from packages/core/python/itkwasm/test/baseline/test_pipeline_write_read_image.png rename to packages/core/test/data/baseline/test_pipeline_write_read_image.png diff --git a/packages/core/python/itkwasm/test/input/cow.vtk b/packages/core/test/data/input/cow.vtk similarity index 100% rename from packages/core/python/itkwasm/test/input/cow.vtk rename to packages/core/test/data/input/cow.vtk diff --git a/packages/core/python/itkwasm/test/input/cthead1.png b/packages/core/test/data/input/cthead1.png similarity index 100% rename from packages/core/python/itkwasm/test/input/cthead1.png rename to packages/core/test/data/input/cthead1.png diff --git a/packages/core/python/itkwasm/test/input/input-output-files-test.wasi.wasm b/packages/core/test/data/input/input-output-files-test.wasi.wasm similarity index 100% rename from packages/core/python/itkwasm/test/input/input-output-files-test.wasi.wasm rename to packages/core/test/data/input/input-output-files-test.wasi.wasm diff --git a/packages/core/python/itkwasm/test/input/input.bin b/packages/core/test/data/input/input.bin similarity index 100% rename from packages/core/python/itkwasm/test/input/input.bin rename to packages/core/test/data/input/input.bin diff --git a/packages/core/python/itkwasm/test/input/input.txt b/packages/core/test/data/input/input.txt similarity index 100% rename from packages/core/python/itkwasm/test/input/input.txt rename to packages/core/test/data/input/input.txt diff --git a/packages/core/python/itkwasm/test/input/median-filter-test.wasi.wasm b/packages/core/test/data/input/median-filter-test.wasi.wasm similarity index 100% rename from packages/core/python/itkwasm/test/input/median-filter-test.wasi.wasm rename to packages/core/test/data/input/median-filter-test.wasi.wasm diff --git a/packages/core/python/itkwasm/test/input/mesh-read-write-test.wasi.wasm b/packages/core/test/data/input/mesh-read-write-test.wasi.wasm similarity index 100% rename from packages/core/python/itkwasm/test/input/mesh-read-write-test.wasi.wasm rename to packages/core/test/data/input/mesh-read-write-test.wasi.wasm diff --git a/packages/core/python/itkwasm/test/input/mesh-to-poly-data.wasi.wasm b/packages/core/test/data/input/mesh-to-poly-data.wasi.wasm similarity index 100% rename from packages/core/python/itkwasm/test/input/mesh-to-poly-data.wasi.wasm rename to packages/core/test/data/input/mesh-to-poly-data.wasi.wasm diff --git a/packages/core/python/itkwasm/test/input/poly-data-to-mesh.wasi.wasm b/packages/core/test/data/input/poly-data-to-mesh.wasi.wasm similarity index 100% rename from packages/core/python/itkwasm/test/input/poly-data-to-mesh.wasi.wasm rename to packages/core/test/data/input/poly-data-to-mesh.wasi.wasm diff --git a/packages/core/python/itkwasm/test/input/stdout-stderr-test.wasi.wasm b/packages/core/test/data/input/stdout-stderr-test.wasi.wasm similarity index 100% rename from packages/core/python/itkwasm/test/input/stdout-stderr-test.wasi.wasm rename to packages/core/test/data/input/stdout-stderr-test.wasi.wasm From 44dd04352d36b387c84b74e17a9426e9c5a80e5e Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 7 Jun 2023 21:52:59 +0200 Subject: [PATCH 13/24] WIP: migrate pipeline test class --- .../src/main/java/org/itk/wasm/Pipeline.java | 5 ++ .../test/java/org/itk/wasm/TestPipeline.java | 81 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 packages/core/java/src/test/java/org/itk/wasm/TestPipeline.java diff --git a/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java b/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java index 746d86aef..bf10b1425 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java +++ b/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java @@ -53,6 +53,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -87,6 +88,10 @@ public Pipeline(byte[] wasmBytes) { module = new Module(engine, wasmBytes); } + public List> run(List args) { + return run(args, Collections.emptyList(), Collections.emptyList()); + } + public List> run(List args, List> outputs, List> inputs) { try (RunInstance ri = new RunInstance(args, outputs, inputs)) { int returnCode = ri.delayedStart(); diff --git a/packages/core/java/src/test/java/org/itk/wasm/TestPipeline.java b/packages/core/java/src/test/java/org/itk/wasm/TestPipeline.java new file mode 100644 index 000000000..ba5b34f34 --- /dev/null +++ b/packages/core/java/src/test/java/org/itk/wasm/TestPipeline.java @@ -0,0 +1,81 @@ +/*- + * #%L + * Java bindings for itk-wasm. + * %% + * Copyright (C) 2023 ITK developers. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package org.itk.wasm; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; + +public class TestPipeline { + + private Path test_input_dir; + private Path test_baseline_dir; + + public TestPipeline() { + test_input_dir = Paths.get("").toAbsolutePath().resolve("input"); + test_baseline_dir = Paths.get("").toAbsolutePath().resolve("baseline"); + } + + @Test + public void testStdoutStderr() throws IOException { + // Test logic goes here + Pipeline pipeline = new Pipeline(test_input_dir.resolve("stdout-stderr-test.wasi.wasm").toString()); + pipeline.run(new ArrayList<>()); + + // Test re-run + pipeline.run(new ArrayList<>()); + } + + @Test + public void test_pipeline_bytes() { + // Test logic goes here + } + + @Test + public void test_pipeline_input_output_streams() { + // Test logic goes here + } + + @EnabledOnOs(OS.LINUX) // Skip this test on Windows platform + @Test + public void test_pipeline_input_output_files() { + // Test logic goes here + } + + @Test + public void test_pipeline_write_read_image() { + // Test logic goes here + } + + @Test + public void test_pipeline_write_read_mesh() { + // Test logic goes here + } + + @Test + public void test_pipeline_write_read_polydata() { + // Test logic goes here + } +} From f9a452850ce57089f03116a11227fe1b6744bddd Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 9 Jun 2023 10:23:28 +0200 Subject: [PATCH 14/24] Fix itk-wasm java version --- packages/core/java/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/java/pom.xml b/packages/core/java/pom.xml index 3102611fd..1e250da3c 100644 --- a/packages/core/java/pom.xml +++ b/packages/core/java/pom.xml @@ -10,7 +10,7 @@ org.itk.wasm itk-wasm - 0.1.0 + 0.1.0-SNAPSHOT itk-wasm for Java Java interface to itk-wasm WebAssembly modules. From 20bbeced376e70fad7a90697c81956d7af95bbd1 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 9 Jun 2023 10:31:22 +0200 Subject: [PATCH 15/24] Ignore more WASI build files --- .gitignore | 1 + packages/core/python/.gitignore | 1 + 2 files changed, 2 insertions(+) create mode 100644 packages/core/python/.gitignore diff --git a/.gitignore b/.gitignore index bed43ed36..9459e8a3a 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ build-emscripten build-wasi src/io/internal/ImageIOIndex.ts src/io/internal/MeshIOIndex.ts +wasi-build/ dist/* !dist/dicom diff --git a/packages/core/python/.gitignore b/packages/core/python/.gitignore new file mode 100644 index 000000000..c18dd8d83 --- /dev/null +++ b/packages/core/python/.gitignore @@ -0,0 +1 @@ +__pycache__/ From 78f8b4f922218717a6db9632fc46718d720ac01c Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 9 Jun 2023 10:42:47 +0200 Subject: [PATCH 16/24] Remove empty Python source file --- packages/core/python/itkwasm/itkwasm/transform.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 packages/core/python/itkwasm/itkwasm/transform.py diff --git a/packages/core/python/itkwasm/itkwasm/transform.py b/packages/core/python/itkwasm/itkwasm/transform.py deleted file mode 100644 index e69de29bb..000000000 From f80002a341a326bd0034016d21041e7f636c024c Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 9 Jun 2023 10:52:43 +0200 Subject: [PATCH 17/24] Add BinaryFile and TextFile types For now, just a straight conversion from Python. We will adjust later to avoid the anti-pattern of enumerated cases, in favor of extensibility via Java's type system. --- .../main/java/org/itk/wasm/BinaryFile.java | 7 +++ .../src/main/java/org/itk/wasm/Pipeline.java | 24 ++++++---- .../main/java/org/itk/wasm/PurePosixPath.java | 48 +++++++++++++++++++ .../src/main/java/org/itk/wasm/TextFile.java | 7 +++ 4 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 packages/core/java/src/main/java/org/itk/wasm/BinaryFile.java create mode 100644 packages/core/java/src/main/java/org/itk/wasm/PurePosixPath.java create mode 100644 packages/core/java/src/main/java/org/itk/wasm/TextFile.java diff --git a/packages/core/java/src/main/java/org/itk/wasm/BinaryFile.java b/packages/core/java/src/main/java/org/itk/wasm/BinaryFile.java new file mode 100644 index 000000000..447fb741a --- /dev/null +++ b/packages/core/java/src/main/java/org/itk/wasm/BinaryFile.java @@ -0,0 +1,7 @@ +package org.itk.wasm; +public class BinaryFile { + public PurePosixPath path; + public BinaryFile(PurePosixPath path) { + this.path = path; + } +} \ No newline at end of file diff --git a/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java b/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java index bf10b1425..d851c49b6 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java +++ b/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java @@ -202,19 +202,23 @@ public RunInstance(List args, List> outputs, Set preopenDirectories = new HashSet<>(); for (PipelineInput input : inputs) { - if (input.type == InterfaceTypes.TextFile || input.type == InterfaceTypes.BinaryFile) { - // TODO: enable once TextFile/BinaryFile exists - //Path path = ((TextFile) input.data).path; - Path path = null; - preopenDirectories.add(purePosixPath(path.getParent())); + if (input.type == InterfaceTypes.TextFile) { + PurePosixPath path = ((TextFile) input.data).path; + preopenDirectories.add(path.getParent().toString()); + } + if (input.type == InterfaceTypes.BinaryFile) { + PurePosixPath path = ((BinaryFile) input.data).path; + preopenDirectories.add(path.getParent().toString()); } } for (PipelineOutput output : outputs) { - if (output.type == InterfaceTypes.TextFile || output.type == InterfaceTypes.BinaryFile) { - // TODO: enable once TextFile/BinaryFile exists - //Path path = ((TextFile) input.data).path; - Path path = null; - preopenDirectories.add(purePosixPath(path.getParent())); + if (output.type == InterfaceTypes.TextFile) { + PurePosixPath path = ((TextFile) output.data).path; + preopenDirectories.add(path.getParent().toString()); + } + if (output.type == InterfaceTypes.BinaryFile) { + PurePosixPath path = ((BinaryFile) output.data).path; + preopenDirectories.add(path.getParent().toString()); } } diff --git a/packages/core/java/src/main/java/org/itk/wasm/PurePosixPath.java b/packages/core/java/src/main/java/org/itk/wasm/PurePosixPath.java new file mode 100644 index 000000000..b03cb96ab --- /dev/null +++ b/packages/core/java/src/main/java/org/itk/wasm/PurePosixPath.java @@ -0,0 +1,48 @@ +/*- + * #%L + * Java bindings for itk-wasm. + * %% + * Copyright (C) 2023 ITK developers. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package org.itk.wasm; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class PurePosixPath { + public Path path; + + public PurePosixPath(Path path) { + this.path = path; + } + public PurePosixPath(String path) { + this.path = Paths.get(path); + } + public PurePosixPath(File path) { + this.path = path.toPath(); + } + + public PurePosixPath getParent() { + return new PurePosixPath(path.getParent()); + } + + @Override + public String toString() { + // TODO: Ensure path string conforms to POSIX requirements. + return path.toString(); + } +} diff --git a/packages/core/java/src/main/java/org/itk/wasm/TextFile.java b/packages/core/java/src/main/java/org/itk/wasm/TextFile.java new file mode 100644 index 000000000..3057a3ee4 --- /dev/null +++ b/packages/core/java/src/main/java/org/itk/wasm/TextFile.java @@ -0,0 +1,7 @@ +package org.itk.wasm; +public class TextFile { + public PurePosixPath path; + public TextFile(PurePosixPath path) { + this.path = path; + } +} \ No newline at end of file From b430589eb88040a48f606ab53197fae8bcf0ebf4 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 9 Jun 2023 10:54:19 +0200 Subject: [PATCH 18/24] Migrate a few more classes from Python --- .../main/java/org/itk/wasm/FloatTypes.java | 18 +++++++++++ .../src/main/java/org/itk/wasm/IntTypes.java | 27 ++++++++++++++++ .../main/java/org/itk/wasm/JsonObject.java | 19 ++++++++++++ .../main/java/org/itk/wasm/PixelTypes.java | 31 +++++++++++++++++++ 4 files changed, 95 insertions(+) create mode 100644 packages/core/java/src/main/java/org/itk/wasm/FloatTypes.java create mode 100644 packages/core/java/src/main/java/org/itk/wasm/IntTypes.java create mode 100644 packages/core/java/src/main/java/org/itk/wasm/JsonObject.java create mode 100644 packages/core/java/src/main/java/org/itk/wasm/PixelTypes.java diff --git a/packages/core/java/src/main/java/org/itk/wasm/FloatTypes.java b/packages/core/java/src/main/java/org/itk/wasm/FloatTypes.java new file mode 100644 index 000000000..d03f6fb96 --- /dev/null +++ b/packages/core/java/src/main/java/org/itk/wasm/FloatTypes.java @@ -0,0 +1,18 @@ +package org.itk.wasm; + +public enum FloatTypes { + Float32("float32"), + Float64("float64"), + SpacePrecisionType("float64"); + + private final String value; + + FloatTypes(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } +} diff --git a/packages/core/java/src/main/java/org/itk/wasm/IntTypes.java b/packages/core/java/src/main/java/org/itk/wasm/IntTypes.java new file mode 100644 index 000000000..791cabe48 --- /dev/null +++ b/packages/core/java/src/main/java/org/itk/wasm/IntTypes.java @@ -0,0 +1,27 @@ +package org.itk.wasm; + +public enum IntTypes { + Int8("int8"), + UInt8("uint8"), + Int16("int16"), + UInt16("uint16"), + Int32("int32"), + UInt32("uint32"), + Int64("int64"), + UInt64("uint64"), + SizeValueType("uint64"), + IdentifierType("uint64"), + IndexValueType("int64"), + OffsetValueType("int64"); + + private final String value; + + IntTypes(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } +} diff --git a/packages/core/java/src/main/java/org/itk/wasm/JsonObject.java b/packages/core/java/src/main/java/org/itk/wasm/JsonObject.java new file mode 100644 index 000000000..a31d4a7b4 --- /dev/null +++ b/packages/core/java/src/main/java/org/itk/wasm/JsonObject.java @@ -0,0 +1,19 @@ +package org.itk.wasm; + +import java.util.Map; + +public class JsonObject { + private Map data; + + public JsonObject(Map data) { + this.data = data; + } + + public Map getData() { + return data; + } + + public void setData(Map data) { + this.data = data; + } +} diff --git a/packages/core/java/src/main/java/org/itk/wasm/PixelTypes.java b/packages/core/java/src/main/java/org/itk/wasm/PixelTypes.java new file mode 100644 index 000000000..28273f7c2 --- /dev/null +++ b/packages/core/java/src/main/java/org/itk/wasm/PixelTypes.java @@ -0,0 +1,31 @@ +package org.itk.wasm; + +public enum PixelTypes { + Unknown("Unknown"), + Scalar("Scalar"), + RGB("RGB"), + RGBA("RGBA"), + Offset("Offset"), + Vector("Vector"), + Point("Point"), + CovariantVector("CovariantVector"), + SymmetricSecondRankTensor("SymmetricSecondRankTensor"), + DiffusionTensor3D("DiffusionTensor3D"), + Complex("Complex"), + FixedArray("FixedArray"), + Array("Array"), + Matrix("Matrix"), + VariableLengthVector("VariableLengthVector"), + VariableSizeMatrix("VariableSizeMatrix"); + + private final String value; + + PixelTypes(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } +} From 2265d6822779e2e478166a5ef301ea14e66aafae Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 9 Jun 2023 11:01:52 +0200 Subject: [PATCH 19/24] TestPipeline: fix resource paths --- .../java/src/test/java/org/itk/wasm/TestPipeline.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/core/java/src/test/java/org/itk/wasm/TestPipeline.java b/packages/core/java/src/test/java/org/itk/wasm/TestPipeline.java index ba5b34f34..9d8aa5658 100644 --- a/packages/core/java/src/test/java/org/itk/wasm/TestPipeline.java +++ b/packages/core/java/src/test/java/org/itk/wasm/TestPipeline.java @@ -30,12 +30,14 @@ public class TestPipeline { - private Path test_input_dir; - private Path test_baseline_dir; + private final Path test_data_dir; + private final Path test_input_dir; + private final Path test_baseline_dir; public TestPipeline() { - test_input_dir = Paths.get("").toAbsolutePath().resolve("input"); - test_baseline_dir = Paths.get("").toAbsolutePath().resolve("baseline"); + test_data_dir = Paths.get("..", "test", "data").toAbsolutePath(); + test_input_dir = test_data_dir.resolve("input"); + test_baseline_dir = test_data_dir.resolve("baseline"); } @Test From 60c01234f51144e56264dd3caa1221065c0d9566 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 9 Jun 2023 11:22:20 +0200 Subject: [PATCH 20/24] Pipeline: comment out non-working methods For some reason, these methods throw ClassNotFoundException... sort of. They don't actually *throw* it, but in the Eclipse debugger, I see the constructor terminate early and reporting that this exception happened. But it doesn't stop execution of the thread for some reason. --- packages/core/java/src/main/java/org/itk/wasm/Pipeline.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java b/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java index d851c49b6..b609c036c 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java +++ b/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java @@ -78,9 +78,9 @@ public Pipeline(String path) throws IOException { public Pipeline(byte[] wasmBytes) { config = new Config(); - config.wasmBulkMemory(true); - config.wasmSimd(true); - //config.wasmMemory64(true); + //config.wasmBulkMemory(true); // <-- This method causes a mysterious ClassNotFoundException + //config.wasmSimd(true); // <-- This method causes a mysterious ClassNotFoundException + //config.wasmMemory64(true); // <-- This method does not exist engine = new Engine(config); linker = new Linker(engine); From 85b72252ead66dee3d1f0ff1e0db441a4a99be14 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 9 Jun 2023 11:37:13 +0200 Subject: [PATCH 21/24] Main: fix path to .wasm --- packages/core/java/src/main/java/org/itk/wasm/Main.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/java/src/main/java/org/itk/wasm/Main.java b/packages/core/java/src/main/java/org/itk/wasm/Main.java index 22b550eba..22f98c052 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/Main.java +++ b/packages/core/java/src/main/java/org/itk/wasm/Main.java @@ -43,7 +43,7 @@ public static void main(String... args) throws IOException { WasiCtx wasi = new WasiCtxBuilder().inheritStdout().inheritStderr().build(); Store store = Store.withoutData(wasi); Linker linker = new Linker(store.engine()); - Module module = Module.fromBinary(store.engine(), readBytes("../python/itkwasm/test/input/stdout-stderr-test.wasi.wasm"))) + Module module = Module.fromBinary(store.engine(), readBytes("../test/data/input/stdout-stderr-test.wasi.wasm"))) { // Here we handle the imports of the module, which in this case is our // `HelloCallback` type and its associated implementation of `Callback. From 2eba9347355de738729f45aea736c675df9a8020 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 9 Jun 2023 11:51:52 +0200 Subject: [PATCH 22/24] WIP: IT WORKS --- .../java/src/main/java/org/itk/wasm/Main.java | 45 ++++++++++--------- .../src/main/java/org/itk/wasm/Pipeline.java | 36 ++++++++------- 2 files changed, 44 insertions(+), 37 deletions(-) diff --git a/packages/core/java/src/main/java/org/itk/wasm/Main.java b/packages/core/java/src/main/java/org/itk/wasm/Main.java index 22f98c052..d470415e9 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/Main.java +++ b/packages/core/java/src/main/java/org/itk/wasm/Main.java @@ -19,6 +19,8 @@ */ package org.itk.wasm; +import io.github.kawamuray.wasmtime.Config; +import io.github.kawamuray.wasmtime.Engine; import io.github.kawamuray.wasmtime.Extern; import io.github.kawamuray.wasmtime.Linker; import io.github.kawamuray.wasmtime.Module; @@ -39,36 +41,37 @@ public static void main(String... args) throws IOException { // `Store` structure. Note that you can also tweak configuration settings // with a `Config` and an `Engine` if desired. System.err.println("Initializing..."); + Config config = new Config(); try ( - WasiCtx wasi = new WasiCtxBuilder().inheritStdout().inheritStderr().build(); - Store store = Store.withoutData(wasi); - Linker linker = new Linker(store.engine()); - Module module = Module.fromBinary(store.engine(), readBytes("../test/data/input/stdout-stderr-test.wasi.wasm"))) + Engine engine = new Engine(config); + Linker linker = new Linker(engine); + Module module = Module.fromBinary(engine, readBytes("../test/data/input/stdout-stderr-test.wasi.wasm"))) { // Here we handle the imports of the module, which in this case is our // `HelloCallback` type and its associated implementation of `Callback. System.err.println("Creating callback..."); - WasiCtx.addToLinker(linker); - //linker.define("xyz", "poll_word", Extern.fromFunc(pollWordFn)); - String moduleName = "instance1"; - linker.module(store, moduleName, module); - Extern extern = linker.get(store, moduleName, "").get(); - Consumer0 doWork = WasmFunctions.consumer(store, extern.func()); - doWork.accept(); + WasiCtx wasi = new WasiCtxBuilder().inheritStdout().inheritStderr().build(); + Store store = new Store<>(null, engine, wasi); + WasiCtx.addToLinker(linker); + String moduleName = "this-can-be-anything"; + linker.module(store, moduleName, module); + Extern extern = linker.get(store, moduleName, "").get(); + Consumer0 doWork = WasmFunctions.consumer(store, extern.func()); + doWork.accept(); } } private static byte[] readBytes(String filename) throws IOException { - //try (InputStream is = Main.class.getResourceAsStream(filename)) { - try (InputStream is = new FileInputStream(filename)) { - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - int nRead; - byte[] buf = new byte[16384]; - while ((nRead = is.read(buf, 0, buf.length)) != -1) { - buffer.write(buf, 0, nRead); - } - return buffer.toByteArray(); - } + //try (InputStream is = Main.class.getResourceAsStream(filename)) { + try (InputStream is = new FileInputStream(filename)) { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int nRead; + byte[] buf = new byte[16384]; + while ((nRead = is.read(buf, 0, buf.length)) != -1) { + buffer.write(buf, 0, nRead); + } + return buffer.toByteArray(); + } } } diff --git a/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java b/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java index b609c036c..1bb80d12f 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java +++ b/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java @@ -61,6 +61,8 @@ public class Pipeline { + private static int instanceID = 0; + private Config config; private Engine engine; private Linker linker; @@ -86,6 +88,8 @@ public Pipeline(byte[] wasmBytes) { linker = new Linker(engine); //linker.allowShadowing(true); module = new Module(engine, wasmBytes); + + WasiCtx.addToLinker(linker); } public List> run(List args) { @@ -148,34 +152,34 @@ private static byte[] readBytes(String filename) throws IOException { } } - private Extern extern(Store store, String name) { + private Extern extern(Store store, String name) { return linker.get(store, moduleName, name).get(); } - private Consumer0 consumer0(Store store, String name) { + private Consumer0 consumer0(Store store, String name) { return WasmFunctions.consumer(store, extern(store, name).func()); } - private Consumer1 consumer1(Store store, String name) { + private Consumer1 consumer1(Store store, String name) { return WasmFunctions.consumer(store, extern(store, name).func(), I32); } - private Function0 func0(Store store, String name) { + private Function0 func0(Store store, String name) { return WasmFunctions.func(store, extern(store, name).func(), I32); } - private Function1 func1(Store store, String name) { + private Function1 func1(Store store, String name) { return WasmFunctions.func(store, extern(store, name).func(), I32, I32); } - private Function2 func2(Store store, String name) { + private Function2 func2(Store store, String name) { return WasmFunctions.func(store, extern(store, name).func(), I32, I32, I32); } - private Function3 func3(Store store, String name) { + private Function3 func3(Store store, String name) { return WasmFunctions.func(store, extern(store, name).func(), I32, I32, I32, I32); } - private Function4 func4(Store store, String name) { + private Function4 func4(Store store, String name) { return WasmFunctions.func(store, extern(store, name).func(), I32, I32, I32, I32, I32); } public class RunInstance implements AutoCloseable { - private Store store; + private final Store store; private Consumer0 main; private Consumer0 initialize; @@ -194,11 +198,13 @@ public RunInstance(List args, List> outputs, List> inputs) { WasiCtx wasiConfig = new WasiCtxBuilder() - .inheritEnv() + //.inheritEnv() .inheritStderr() - .inheritStdin() + //.inheritStdin() .inheritStdout() - .args(args).build(); + //.args(args) + .build(); + Set preopenDirectories = new HashSet<>(); for (PipelineInput input : inputs) { @@ -227,12 +233,10 @@ public RunInstance(List args, List> outputs, wasiConfig.pushPreopenDir(p, preopen); } - store = new Store<>(engine, wasiConfig); - - WasiCtx.addToLinker(linker); + store = new Store<>(null, engine, wasiConfig); // TODO: Decide how to name this more appropriately. - moduleName = "instance1"; + moduleName = "instance" + instanceID++; linker.module(store, moduleName, module); From d1cda25ce2d1884786391c4394a2875515090eb6 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 9 Jun 2023 12:40:42 +0200 Subject: [PATCH 23/24] Migrate more pipeline test functions --- .../main/java/org/itk/wasm/BinaryFile.java | 18 +- .../java/src/main/java/org/itk/wasm/IO.java | 29 +++ .../src/main/java/org/itk/wasm/Pipeline.java | 114 ++++----- .../main/java/org/itk/wasm/PipelineInput.java | 7 +- .../java/org/itk/wasm/PipelineOutput.java | 3 + .../src/main/java/org/itk/wasm/TextFile.java | 18 +- .../test/java/org/itk/wasm/TestPipeline.java | 219 ++++++++++++++++-- 7 files changed, 305 insertions(+), 103 deletions(-) create mode 100644 packages/core/java/src/main/java/org/itk/wasm/IO.java diff --git a/packages/core/java/src/main/java/org/itk/wasm/BinaryFile.java b/packages/core/java/src/main/java/org/itk/wasm/BinaryFile.java index 447fb741a..769f1ab48 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/BinaryFile.java +++ b/packages/core/java/src/main/java/org/itk/wasm/BinaryFile.java @@ -1,7 +1,15 @@ package org.itk.wasm; + +import java.nio.file.Path; + public class BinaryFile { - public PurePosixPath path; - public BinaryFile(PurePosixPath path) { - this.path = path; - } -} \ No newline at end of file + public PurePosixPath path; + + public BinaryFile(PurePosixPath path) { + this.path = path; + } + + public BinaryFile(Path path) { + this(new PurePosixPath(path)); + } +} diff --git a/packages/core/java/src/main/java/org/itk/wasm/IO.java b/packages/core/java/src/main/java/org/itk/wasm/IO.java new file mode 100644 index 000000000..002b4bf6c --- /dev/null +++ b/packages/core/java/src/main/java/org/itk/wasm/IO.java @@ -0,0 +1,29 @@ +package org.itk.wasm; + +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +public final class IO { + + public IO() { } + + public static byte[] readBytes(String path) throws IOException { + //try (InputStream is = Main.class.getResourceAsStream(filename)) { + try (InputStream is = new FileInputStream(path)) { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int nRead; + byte[] buf = new byte[16384]; + while ((nRead = is.read(buf, 0, buf.length)) != -1) { + buffer.write(buf, 0, nRead); + } + return buffer.toByteArray(); + } + } + + public static String readString(String path) throws IOException { + return new String(readBytes(path), StandardCharsets.UTF_8); + } +} diff --git a/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java b/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java index 1bb80d12f..d3911c542 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java +++ b/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java @@ -44,10 +44,7 @@ import io.github.kawamuray.wasmtime.wasi.WasiCtx; import io.github.kawamuray.wasmtime.wasi.WasiCtxBuilder; -import java.io.ByteArrayOutputStream; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.nio.file.Path; @@ -60,22 +57,19 @@ import java.util.Set; public class Pipeline { - private static int instanceID = 0; private Config config; private Engine engine; private Linker linker; private Module module; - private String moduleName; - /** TEMP */ - public Pipeline() throws IOException { - this("../python/itkwasm/test/input/stdout-stderr-test.wasi.wasm"); + public Pipeline(Path path) throws IOException { + this(path.toString()); } public Pipeline(String path) throws IOException { - this(readBytes(path)); + this(IO.readBytes(path)); } public Pipeline(byte[] wasmBytes) { @@ -99,6 +93,7 @@ public List> run(List args) { public List> run(List args, List> outputs, List> inputs) { try (RunInstance ri = new RunInstance(args, outputs, inputs)) { int returnCode = ri.delayedStart(); + if (returnCode != 0) throw new RuntimeException("Non-zero return code: " + returnCode); //TEMP List> populatedOutputs = new ArrayList<>(); if (!outputs.isEmpty() && returnCode == 0) { @@ -127,11 +122,6 @@ public List> run(List args, List> ou } } - private static String purePosixPath(Path p) { - // TODO -- ensure path is POSIX style, not Windows - return p.toString(); - } - private static String str(byte[] bytes) { return new String(bytes, StandardCharsets.UTF_8); } @@ -139,60 +129,23 @@ private static byte[] bytes(String str) { return str.getBytes(StandardCharsets.UTF_8); } - private static byte[] readBytes(String filename) throws IOException { - //try (InputStream is = Main.class.getResourceAsStream(filename)) { - try (InputStream is = new FileInputStream(filename)) { - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - int nRead; - byte[] buf = new byte[16384]; - while ((nRead = is.read(buf, 0, buf.length)) != -1) { - buffer.write(buf, 0, nRead); - } - return buffer.toByteArray(); - } - } - - private Extern extern(Store store, String name) { - return linker.get(store, moduleName, name).get(); - } - private Consumer0 consumer0(Store store, String name) { - return WasmFunctions.consumer(store, extern(store, name).func()); - } - private Consumer1 consumer1(Store store, String name) { - return WasmFunctions.consumer(store, extern(store, name).func(), I32); - } - private Function0 func0(Store store, String name) { - return WasmFunctions.func(store, extern(store, name).func(), I32); - } - private Function1 func1(Store store, String name) { - return WasmFunctions.func(store, extern(store, name).func(), I32, I32); - } - private Function2 func2(Store store, String name) { - return WasmFunctions.func(store, extern(store, name).func(), I32, I32, I32); - } - private Function3 func3(Store store, String name) { - return WasmFunctions.func(store, extern(store, name).func(), I32, I32, I32, I32); - } - private Function4 func4(Store store, String name) { - return WasmFunctions.func(store, extern(store, name).func(), I32, I32, I32, I32, I32); - } - - public class RunInstance implements AutoCloseable { + private class RunInstance implements AutoCloseable { private final Store store; - - private Consumer0 main; - private Consumer0 initialize; - private Function0 delayedStart; - private Consumer1 delayedExit; - private Function4 inputArrayAlloc; - private Function3 inputJsonAlloc; - private Function2 outputJsonAddress; - private Function2 outputJsonSize; - private Function3 outputArrayAddress; - private Function3 outputArraySize; - private Consumer0 freeAll; - private Memory memory; + private final String moduleName; + + private final Consumer0 main; + private final Consumer0 initialize; + private final Function0 delayedStart; + private final Consumer1 delayedExit; + private final Function4 inputArrayAlloc; + private final Function3 inputJsonAlloc; + private final Function2 outputJsonAddress; + private final Function2 outputJsonSize; + private final Function3 outputArrayAddress; + private final Function3 outputArraySize; + private final Consumer0 freeAll; + private final Memory memory; public RunInstance(List args, List> outputs, List> inputs) @@ -233,11 +186,9 @@ public RunInstance(List args, List> outputs, wasiConfig.pushPreopenDir(p, preopen); } + // Instantiate the module. store = new Store<>(null, engine, wasiConfig); - - // TODO: Decide how to name this more appropriately. moduleName = "instance" + instanceID++; - linker.module(store, moduleName, module); main = consumer0(store, ""); @@ -321,5 +272,30 @@ private Map getOutputJson(int outputIndex) { Gson gson = new GsonBuilder().create(); return gson.fromJson(jsonString, new TypeToken>() {}); } + + private Extern extern(Store store, String name) { + return linker.get(store, moduleName, name).get(); + } + private Consumer0 consumer0(Store store, String name) { + return WasmFunctions.consumer(store, extern(store, name).func()); + } + private Consumer1 consumer1(Store store, String name) { + return WasmFunctions.consumer(store, extern(store, name).func(), I32); + } + private Function0 func0(Store store, String name) { + return WasmFunctions.func(store, extern(store, name).func(), I32); + } + private Function1 func1(Store store, String name) { + return WasmFunctions.func(store, extern(store, name).func(), I32, I32); + } + private Function2 func2(Store store, String name) { + return WasmFunctions.func(store, extern(store, name).func(), I32, I32, I32); + } + private Function3 func3(Store store, String name) { + return WasmFunctions.func(store, extern(store, name).func(), I32, I32, I32, I32); + } + private Function4 func4(Store store, String name) { + return WasmFunctions.func(store, extern(store, name).func(), I32, I32, I32, I32, I32); + } } } diff --git a/packages/core/java/src/main/java/org/itk/wasm/PipelineInput.java b/packages/core/java/src/main/java/org/itk/wasm/PipelineInput.java index d92882b7f..3e284bda5 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/PipelineInput.java +++ b/packages/core/java/src/main/java/org/itk/wasm/PipelineInput.java @@ -20,7 +20,12 @@ package org.itk.wasm; public class PipelineInput { - public InterfaceTypes type; + public InterfaceTypes type; public T data; public String path; + + public PipelineInput(InterfaceTypes type, T data) { + this.type = type; + this.data = data; + } } diff --git a/packages/core/java/src/main/java/org/itk/wasm/PipelineOutput.java b/packages/core/java/src/main/java/org/itk/wasm/PipelineOutput.java index d706273a3..9d46c333d 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/PipelineOutput.java +++ b/packages/core/java/src/main/java/org/itk/wasm/PipelineOutput.java @@ -24,6 +24,9 @@ public class PipelineOutput { public T data; public String path; + public PipelineOutput(InterfaceTypes type) { + this.type = type; + } public PipelineOutput(InterfaceTypes type, T data) { this.type = type; this.data = data; diff --git a/packages/core/java/src/main/java/org/itk/wasm/TextFile.java b/packages/core/java/src/main/java/org/itk/wasm/TextFile.java index 3057a3ee4..f501ceeff 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/TextFile.java +++ b/packages/core/java/src/main/java/org/itk/wasm/TextFile.java @@ -1,7 +1,15 @@ package org.itk.wasm; + +import java.nio.file.Path; + public class TextFile { - public PurePosixPath path; - public TextFile(PurePosixPath path) { - this.path = path; - } -} \ No newline at end of file + public PurePosixPath path; + + public TextFile(PurePosixPath path) { + this.path = path; + } + + public TextFile(Path path) { + this(new PurePosixPath(path)); + } +} diff --git a/packages/core/java/src/test/java/org/itk/wasm/TestPipeline.java b/packages/core/java/src/test/java/org/itk/wasm/TestPipeline.java index 9d8aa5658..f669b069d 100644 --- a/packages/core/java/src/test/java/org/itk/wasm/TestPipeline.java +++ b/packages/core/java/src/test/java/org/itk/wasm/TestPipeline.java @@ -19,10 +19,13 @@ */ package org.itk.wasm; +import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledOnOs; @@ -30,20 +33,19 @@ public class TestPipeline { - private final Path test_data_dir; - private final Path test_input_dir; - private final Path test_baseline_dir; + private final Path testDataDir; + private final Path testInputDir; + private final Path testBaselineDir; public TestPipeline() { - test_data_dir = Paths.get("..", "test", "data").toAbsolutePath(); - test_input_dir = test_data_dir.resolve("input"); - test_baseline_dir = test_data_dir.resolve("baseline"); + testDataDir = Paths.get("..", "test", "data").toAbsolutePath(); + testInputDir = testDataDir.resolve("input"); + testBaselineDir = testDataDir.resolve("baseline"); } @Test public void testStdoutStderr() throws IOException { - // Test logic goes here - Pipeline pipeline = new Pipeline(test_input_dir.resolve("stdout-stderr-test.wasi.wasm").toString()); + Pipeline pipeline = new Pipeline(testInputDir.resolve("stdout-stderr-test.wasi.wasm").toString()); pipeline.run(new ArrayList<>()); // Test re-run @@ -51,33 +53,204 @@ public void testStdoutStderr() throws IOException { } @Test - public void test_pipeline_bytes() { - // Test logic goes here - } + public void testPipelineInputOutputStreams() throws IOException { + Pipeline pipeline = new Pipeline(testInputDir.resolve("input-output-files-test.wasi.wasm").toString()); - @Test - public void test_pipeline_input_output_streams() { - // Test logic goes here + List> pipelineInputs = new ArrayList<>(); + pipelineInputs.add(new PipelineInput<>(InterfaceTypes.TextStream, new TextStream("The answer is 42."))); + pipelineInputs.add(new PipelineInput<>(InterfaceTypes.BinaryStream, new BinaryStream(new byte[]{(byte) 222, (byte) 173, (byte) 190, (byte) 239}))); + + List> pipelineOutputs = new ArrayList<>(); + pipelineOutputs.add(new PipelineOutput<>(InterfaceTypes.TextStream)); + pipelineOutputs.add(new PipelineOutput<>(InterfaceTypes.BinaryStream)); + + List args = Arrays.asList( + "--memory-io", + "--input-text-stream", "0", + "--input-binary-stream", "1", + "--output-text-stream", "0", + "--output-binary-stream", "1" + ); + + List> outputs = pipeline.run(args, pipelineOutputs, pipelineInputs); + + // Test re-run + outputs = pipeline.run(args, pipelineOutputs, pipelineInputs); + + assert outputs.get(0).type == InterfaceTypes.TextStream; + assert ((TextStream) outputs.get(0).data).data.equals("The answer is 42."); + + byte[] binaryData = ((BinaryStream) outputs.get(1).data).data; + assert binaryData[0] == (byte) 222; + assert binaryData[1] == (byte) 173; + assert binaryData[2] == (byte) 190; + assert binaryData[3] == (byte) 239; } - @EnabledOnOs(OS.LINUX) // Skip this test on Windows platform + @EnabledOnOs({OS.LINUX, OS.MAC}) // Skip this test on Windows platform @Test - public void test_pipeline_input_output_files() { - // Test logic goes here + public void testPipelineInputOutputFiles() throws IOException { + Pipeline pipeline = new Pipeline(testInputDir.resolve("input-output-files-test.wasi.wasm")); + Path inputTextFile = testInputDir.resolve("input.txt"); + Path inputBinaryFile = testInputDir.resolve("input.bin"); + + File outputTextFile = File.createTempFile("output", ".txt"); + File outputBinaryFile = File.createTempFile("output", ".bin"); + + List> pipelineInputs = new ArrayList<>(); + pipelineInputs.add(new PipelineInput<>(InterfaceTypes.TextFile, new TextFile(inputTextFile))); + pipelineInputs.add(new PipelineInput<>(InterfaceTypes.BinaryFile, new BinaryFile(inputBinaryFile))); + + List> pipelineOutputs = new ArrayList<>(); + pipelineOutputs.add(new PipelineOutput<>(InterfaceTypes.TextFile, new TextFile(outputTextFile.toPath()))); + pipelineOutputs.add(new PipelineOutput<>(InterfaceTypes.BinaryFile, new BinaryFile(outputBinaryFile.toPath()))); + + List args = Arrays.asList( + "--memory-io", + "--use-files", + "--input-text-file", inputTextFile.toString(), + "--input-binary-file", inputBinaryFile.toString(), + "--output-text-file", outputTextFile.toString(), + "--output-binary-file", outputBinaryFile.toString() + ); + + List> outputs = pipeline.run(args, pipelineOutputs, pipelineInputs); + + // Test re-run + outputs = pipeline.run(args, pipelineOutputs, pipelineInputs); + + assert outputs.get(0).type == InterfaceTypes.TextFile; + PurePosixPath outputPath1 = ((TextFile) outputs.get(0).data).path; + String content1 = IO.readString(outputPath1.toString()); + assert content1.equals("The answer is 42."); + + assert outputs.get(1).type == InterfaceTypes.BinaryFile; + PurePosixPath outputPath2 = ((BinaryFile) outputs.get(1).data).path; + byte[] content2 = IO.readBytes(outputPath2.toString()); + assert content2[0] == (byte) 222; + assert content2[1] == (byte) 173; + assert content2[2] == (byte) 190; + assert content2[3] == (byte) 239; } + /* @Test - public void test_pipeline_write_read_image() { - // Test logic goes here + public void testPipelineWriteReadImage() throws IOException { + Pipeline pipeline = new Pipeline(testInputDir.resolve("median-filter-test.wasi.wasm")); + + Path data = testInputDir.resolve("cthead1.png"); + itk.Image itkImage = itk.imread(data, itk.UC); + Map itkImageDict = itk.dict_from_image(itkImage); + Image itkwasmImage = new Image(itkImageDict); + + List> pipelineInputs = new ArrayList<>(); + pipelineInputs.add(new PipelineInput<>(InterfaceTypes.Image, itkwasmImage)); + + List> pipelineOutputs = new ArrayList<>(); + pipelineOutputs.add(new PipelineOutput<>(InterfaceTypes.Image)); + + String[] args = { + "--memory-io", + "0", + "0", + "--radius", "2" + }; + + List> outputs = pipeline.run(args, pipelineOutputs, pipelineInputs); + + itk.Image outImage = itk.image_from_dict(asdict(outputs.get(0).getData())); + // To be addressed in itk-5.3.1 + outImage.SetRegions(new int[]{256, 256}); + + itk.Image baseline = itk.imread(testBaselineDir.resolve("test_pipeline_write_read_image.png")); + + double difference = np.sum(itk.comparison_image_filter(outImage, baseline)); + assert difference == 0.0; } @Test - public void test_pipeline_write_read_mesh() { - // Test logic goes here + public void testPipelineWriteReadMesh() { + Pipeline pipeline = new Pipeline(testInputDir.resolve("mesh-read-write-test.wasi.wasm")); + + Path data = testInputDir.resolve("cow.vtk"); + itk.Mesh itkMesh = itk.meshread(data); + Map itkMeshDict = itk.dict_from_mesh(itkMesh); + Mesh itkwasmMesh = new Mesh(itkMeshDict); + + List> pipelineInputs = new ArrayList<>(); + pipelineInputs.add(new PipelineInput<>(InterfaceTypes.Mesh, itkwasmMesh)); + + List> pipelineOutputs = new ArrayList<>(); + pipelineOutputs.add(new PipelineOutput<>(InterfaceTypes.Mesh)); + + List args = Arrays.asList( + "--memory-io", + "0", + "0" + ); + + List> outputs = pipeline.run(args, pipelineOutputs, pipelineInputs); + + Map outMeshDict = asdict(outputs.get(0).data); + // Native ITK Python binaries require uint64 + outMeshDict.put("cells", ((int[]) outMeshDict.get("cells")).astype(np.uint64)); + outMeshDict.get("meshType").put("cellComponentType", "uint64"); + itk.Mesh outMesh = itk.mesh_from_dict(outMeshDict); + + assert outMesh.GetNumberOfPoints() == 2903; + assert outMesh.GetNumberOfCells() == 3263; } @Test - public void test_pipeline_write_read_polydata() { - // Test logic goes here + public void testPipelineWriteReadPolyData() { + Pipeline pipeline = new Pipeline(testInputDir.resolve("mesh-to-poly-data.wasi.wasm")); + + Path data = testInputDir.resolve("cow.vtk"); + itk.Mesh itkMesh = itk.meshread(data); + Map itkMeshDict = itk.dict_from_mesh(itkMesh); + Mesh itkwasmMesh = new Mesh(itkMeshDict); + + List> pipelineInputs = new ArrayList<>(); + pipelineInputs.add(new PipelineInput<>(InterfaceTypes.Mesh, itkwasmMesh)); + + List> pipelineOutputs = new ArrayList<>(); + pipelineOutputs.add(new PipelineOutput<>(InterfaceTypes.PolyData)); + + List args = Arrays.asList( + "--memory-io", + "0", + "0" + ); + + List> outputs = pipeline.run(args, pipelineOutputs, pipelineInputs); + PolyData polydata = (PolyData) outputs.get(0).data; + + pipeline = new Pipeline(testInputDir.resolve("poly-data-to-mesh.wasi.wasm")); + + pipelineInputs = new ArrayList<>(); + pipelineInputs.add(new PipelineInput<>(InterfaceTypes.PolyData, polydata)); + + pipelineOutputs = new ArrayList<>(); + pipelineOutputs.add(new PipelineOutput<>(InterfaceTypes.Mesh)); + + args = Arrays.asList( + "--memory-io", + "0", + "0" + ); + + outputs = pipeline.run(args, pipelineOutputs, pipelineInputs); + + Map outMeshDict = asdict(outputs.get(0).data); + + // native itk python binaries require uint64 + outMeshDict.put("cells", ((int[]) outMeshDict.get("cells")).astype(np.uint64)); + outMeshDict.get("meshType").put("cellComponentType", "uint64"); + assert np.isclose(outMeshDict.get("points")[0], 3.71636); + itk.Mesh outMesh = itk.mesh_from_dict(outMeshDict); + + assert outMesh.GetNumberOfPoints() == 2903; + assert outMesh.GetNumberOfCells() == 3263; } + */ } From 15ea8774c7bbd6c7b4556cbac1c8bfc5b4399fce Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 9 Jun 2023 16:13:44 +0200 Subject: [PATCH 24/24] WIP: start adding image support and related code And begin reworking the enum types. Nothing new works yet. --- .../src/main/java/org/itk/wasm/Image.java | 69 +++++++++++++++++++ .../src/main/java/org/itk/wasm/ImageType.java | 17 +++++ ...InterfaceTypes.java => InterfaceType.java} | 27 +++----- .../src/main/java/org/itk/wasm/Pipeline.java | 16 ++--- .../main/java/org/itk/wasm/PipelineInput.java | 4 +- .../java/org/itk/wasm/PipelineOutput.java | 6 +- .../src/main/java/org/itk/wasm/PixelType.java | 20 ++++++ .../main/java/org/itk/wasm/PixelTypes.java | 31 --------- .../test/java/org/itk/wasm/TestPipeline.java | 22 +++--- 9 files changed, 139 insertions(+), 73 deletions(-) create mode 100644 packages/core/java/src/main/java/org/itk/wasm/Image.java create mode 100644 packages/core/java/src/main/java/org/itk/wasm/ImageType.java rename packages/core/java/src/main/java/org/itk/wasm/{InterfaceTypes.java => InterfaceType.java} (60%) create mode 100644 packages/core/java/src/main/java/org/itk/wasm/PixelType.java delete mode 100644 packages/core/java/src/main/java/org/itk/wasm/PixelTypes.java diff --git a/packages/core/java/src/main/java/org/itk/wasm/Image.java b/packages/core/java/src/main/java/org/itk/wasm/Image.java new file mode 100644 index 000000000..3039fc2e0 --- /dev/null +++ b/packages/core/java/src/main/java/org/itk/wasm/Image.java @@ -0,0 +1,69 @@ +package org.itk.wasm; + +import java.util.*; + +public class Image { + public ImageType imageType; + public String name; + public List origin; + public List spacing; + public double[][] direction; + public List size; + public Map metadata; + public double[] data; + + public Image() { + this.imageType = new ImageType(); + this.name = "Image"; + this.origin = new ArrayList<>(); + this.spacing = new ArrayList<>(); + this.direction = new double[0][0]; + this.size = new ArrayList<>(); + this.metadata = new HashMap<>(); + this.data = null; + } + + public void postInit() { + if (imageType instanceof Map) { + imageType = new ImageType(); + Map imageTypeMap = asdict(imageType); + // Set values from the map to the corresponding fields in ImageType + // Example: imageType.setDimension((int) imageTypeMap.get("dimension")); + // Add similar code for other fields + } + + int dimension = imageType.dimension; + if (origin.isEmpty()) { + for (int i = 0; i < dimension; i++) { + origin.add(0.0); + } + } + + if (spacing.isEmpty()) { + for (int i = 0; i < dimension; i++) { + spacing.add(1.0); + } + } + + if (direction.length == 0) { + direction = new double[dimension][dimension]; + for (int i = 0; i < dimension; i++) { + for (int j = 0; j < dimension; j++) { + if (i == j) { + direction[i][j] = 1.0; + } else { + direction[i][j] = 0.0; + } + } + } + } + + if (size.isEmpty()) { + for (int i = 0; i < dimension; i++) { + size.add(1); + } + } + } + + // Add getters and setters for the fields +} diff --git a/packages/core/java/src/main/java/org/itk/wasm/ImageType.java b/packages/core/java/src/main/java/org/itk/wasm/ImageType.java new file mode 100644 index 000000000..b2e74479e --- /dev/null +++ b/packages/core/java/src/main/java/org/itk/wasm/ImageType.java @@ -0,0 +1,17 @@ +package org.itk.wasm; + +public class ImageType { + public int dimension; + public IntTypes componentType; + public PixelType pixelType; + public int components; + + public ImageType() { + this.dimension = 2; + this.componentType = IntTypes.UInt8; + this.pixelType = PixelType.Scalar; + this.components = 1; + } + + // Add getters and setters for the fields +} diff --git a/packages/core/java/src/main/java/org/itk/wasm/InterfaceTypes.java b/packages/core/java/src/main/java/org/itk/wasm/InterfaceType.java similarity index 60% rename from packages/core/java/src/main/java/org/itk/wasm/InterfaceTypes.java rename to packages/core/java/src/main/java/org/itk/wasm/InterfaceType.java index fcb513ee7..bad6226e1 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/InterfaceTypes.java +++ b/packages/core/java/src/main/java/org/itk/wasm/InterfaceType.java @@ -18,23 +18,14 @@ * #L% */ package org.itk.wasm; -public enum InterfaceTypes { - TextFile("InterfaceTextFile"), - BinaryFile("InterfaceBinaryFile"), - TextStream("InterfaceTextStream"), - BinaryStream("InterfaceBinaryStream"), - Image("InterfaceImage"), - Mesh("InterfaceMesh"), - PolyData("InterfacePolyData"), - JsonObject("InterfaceJsonObject"); - private String value; - - private InterfaceTypes(String value) { - this.value = value; - } - - public String getValue() { - return value; - } +public enum InterfaceType { + TextFile, + BinaryFile, + TextStream, + BinaryStream, + Image, + Mesh, + PolyData, + JsonObject } diff --git a/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java b/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java index d3911c542..6b90d419f 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java +++ b/packages/core/java/src/main/java/org/itk/wasm/Pipeline.java @@ -99,19 +99,19 @@ public List> run(List args, List> ou if (!outputs.isEmpty() && returnCode == 0) { for (int index = 0; index < outputs.size(); index++) { PipelineOutput output = outputs.get(index); - if (output.type == InterfaceTypes.TextStream) { + if (output.type == InterfaceType.TextStream) { int dataPtr = ri.outputArrayAddress(0, index, 0); int dataLen = ri.outputArraySize(0, index, 0); byte[] dataBytes = ri.wasmTimeLift(dataPtr, dataLen); String dataString = str(dataBytes); TextStream textStream = new TextStream(dataString); - populatedOutputs.add(new PipelineOutput<>(InterfaceTypes.TextStream, textStream)); - } else if (output.type == InterfaceTypes.BinaryStream) { + populatedOutputs.add(new PipelineOutput<>(InterfaceType.TextStream, textStream)); + } else if (output.type == InterfaceType.BinaryStream) { int dataPtr = ri.outputArrayAddress(0, index, 0); int dataLen = ri.outputArraySize(0, index, 0); byte[] dataBytes = ri.wasmTimeLift(dataPtr, dataLen); BinaryStream binaryStream = new BinaryStream(dataBytes); - populatedOutputs.add(new PipelineOutput<>(InterfaceTypes.BinaryStream, binaryStream)); + populatedOutputs.add(new PipelineOutput<>(InterfaceType.BinaryStream, binaryStream)); } else { throw new IllegalArgumentException("Unexpected/not yet supported output.type " + output.type); } @@ -161,21 +161,21 @@ public RunInstance(List args, List> outputs, Set preopenDirectories = new HashSet<>(); for (PipelineInput input : inputs) { - if (input.type == InterfaceTypes.TextFile) { + if (input.type == InterfaceType.TextFile) { PurePosixPath path = ((TextFile) input.data).path; preopenDirectories.add(path.getParent().toString()); } - if (input.type == InterfaceTypes.BinaryFile) { + if (input.type == InterfaceType.BinaryFile) { PurePosixPath path = ((BinaryFile) input.data).path; preopenDirectories.add(path.getParent().toString()); } } for (PipelineOutput output : outputs) { - if (output.type == InterfaceTypes.TextFile) { + if (output.type == InterfaceType.TextFile) { PurePosixPath path = ((TextFile) output.data).path; preopenDirectories.add(path.getParent().toString()); } - if (output.type == InterfaceTypes.BinaryFile) { + if (output.type == InterfaceType.BinaryFile) { PurePosixPath path = ((BinaryFile) output.data).path; preopenDirectories.add(path.getParent().toString()); } diff --git a/packages/core/java/src/main/java/org/itk/wasm/PipelineInput.java b/packages/core/java/src/main/java/org/itk/wasm/PipelineInput.java index 3e284bda5..61954dcc4 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/PipelineInput.java +++ b/packages/core/java/src/main/java/org/itk/wasm/PipelineInput.java @@ -20,11 +20,11 @@ package org.itk.wasm; public class PipelineInput { - public InterfaceTypes type; + public InterfaceType type; public T data; public String path; - public PipelineInput(InterfaceTypes type, T data) { + public PipelineInput(InterfaceType type, T data) { this.type = type; this.data = data; } diff --git a/packages/core/java/src/main/java/org/itk/wasm/PipelineOutput.java b/packages/core/java/src/main/java/org/itk/wasm/PipelineOutput.java index 9d46c333d..4fc8ef5d7 100644 --- a/packages/core/java/src/main/java/org/itk/wasm/PipelineOutput.java +++ b/packages/core/java/src/main/java/org/itk/wasm/PipelineOutput.java @@ -20,14 +20,14 @@ package org.itk.wasm; public class PipelineOutput { - public InterfaceTypes type; + public InterfaceType type; public T data; public String path; - public PipelineOutput(InterfaceTypes type) { + public PipelineOutput(InterfaceType type) { this.type = type; } - public PipelineOutput(InterfaceTypes type, T data) { + public PipelineOutput(InterfaceType type, T data) { this.type = type; this.data = data; } diff --git a/packages/core/java/src/main/java/org/itk/wasm/PixelType.java b/packages/core/java/src/main/java/org/itk/wasm/PixelType.java new file mode 100644 index 000000000..fc2b12bca --- /dev/null +++ b/packages/core/java/src/main/java/org/itk/wasm/PixelType.java @@ -0,0 +1,20 @@ +package org.itk.wasm; + +public enum PixelType { + Unknown, + Scalar, + RGB, + RGBA, + Offset, + Vector, + Point, + CovariantVector, + SymmetricSecondRankTensor, + DiffusionTensor3D, + Complex, + FixedArray, + Array, + Matrix, + VariableLengthVector, + VariableSizeMatrix +} diff --git a/packages/core/java/src/main/java/org/itk/wasm/PixelTypes.java b/packages/core/java/src/main/java/org/itk/wasm/PixelTypes.java deleted file mode 100644 index 28273f7c2..000000000 --- a/packages/core/java/src/main/java/org/itk/wasm/PixelTypes.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.itk.wasm; - -public enum PixelTypes { - Unknown("Unknown"), - Scalar("Scalar"), - RGB("RGB"), - RGBA("RGBA"), - Offset("Offset"), - Vector("Vector"), - Point("Point"), - CovariantVector("CovariantVector"), - SymmetricSecondRankTensor("SymmetricSecondRankTensor"), - DiffusionTensor3D("DiffusionTensor3D"), - Complex("Complex"), - FixedArray("FixedArray"), - Array("Array"), - Matrix("Matrix"), - VariableLengthVector("VariableLengthVector"), - VariableSizeMatrix("VariableSizeMatrix"); - - private final String value; - - PixelTypes(String value) { - this.value = value; - } - - @Override - public String toString() { - return value; - } -} diff --git a/packages/core/java/src/test/java/org/itk/wasm/TestPipeline.java b/packages/core/java/src/test/java/org/itk/wasm/TestPipeline.java index f669b069d..bdcdbe0fe 100644 --- a/packages/core/java/src/test/java/org/itk/wasm/TestPipeline.java +++ b/packages/core/java/src/test/java/org/itk/wasm/TestPipeline.java @@ -57,12 +57,12 @@ public void testPipelineInputOutputStreams() throws IOException { Pipeline pipeline = new Pipeline(testInputDir.resolve("input-output-files-test.wasi.wasm").toString()); List> pipelineInputs = new ArrayList<>(); - pipelineInputs.add(new PipelineInput<>(InterfaceTypes.TextStream, new TextStream("The answer is 42."))); - pipelineInputs.add(new PipelineInput<>(InterfaceTypes.BinaryStream, new BinaryStream(new byte[]{(byte) 222, (byte) 173, (byte) 190, (byte) 239}))); + pipelineInputs.add(new PipelineInput<>(InterfaceType.TextStream, new TextStream("The answer is 42."))); + pipelineInputs.add(new PipelineInput<>(InterfaceType.BinaryStream, new BinaryStream(new byte[]{(byte) 222, (byte) 173, (byte) 190, (byte) 239}))); List> pipelineOutputs = new ArrayList<>(); - pipelineOutputs.add(new PipelineOutput<>(InterfaceTypes.TextStream)); - pipelineOutputs.add(new PipelineOutput<>(InterfaceTypes.BinaryStream)); + pipelineOutputs.add(new PipelineOutput<>(InterfaceType.TextStream)); + pipelineOutputs.add(new PipelineOutput<>(InterfaceType.BinaryStream)); List args = Arrays.asList( "--memory-io", @@ -77,7 +77,7 @@ public void testPipelineInputOutputStreams() throws IOException { // Test re-run outputs = pipeline.run(args, pipelineOutputs, pipelineInputs); - assert outputs.get(0).type == InterfaceTypes.TextStream; + assert outputs.get(0).type == InterfaceType.TextStream; assert ((TextStream) outputs.get(0).data).data.equals("The answer is 42."); byte[] binaryData = ((BinaryStream) outputs.get(1).data).data; @@ -98,12 +98,12 @@ public void testPipelineInputOutputFiles() throws IOException { File outputBinaryFile = File.createTempFile("output", ".bin"); List> pipelineInputs = new ArrayList<>(); - pipelineInputs.add(new PipelineInput<>(InterfaceTypes.TextFile, new TextFile(inputTextFile))); - pipelineInputs.add(new PipelineInput<>(InterfaceTypes.BinaryFile, new BinaryFile(inputBinaryFile))); + pipelineInputs.add(new PipelineInput<>(InterfaceType.TextFile, new TextFile(inputTextFile))); + pipelineInputs.add(new PipelineInput<>(InterfaceType.BinaryFile, new BinaryFile(inputBinaryFile))); List> pipelineOutputs = new ArrayList<>(); - pipelineOutputs.add(new PipelineOutput<>(InterfaceTypes.TextFile, new TextFile(outputTextFile.toPath()))); - pipelineOutputs.add(new PipelineOutput<>(InterfaceTypes.BinaryFile, new BinaryFile(outputBinaryFile.toPath()))); + pipelineOutputs.add(new PipelineOutput<>(InterfaceType.TextFile, new TextFile(outputTextFile.toPath()))); + pipelineOutputs.add(new PipelineOutput<>(InterfaceType.BinaryFile, new BinaryFile(outputBinaryFile.toPath()))); List args = Arrays.asList( "--memory-io", @@ -119,12 +119,12 @@ public void testPipelineInputOutputFiles() throws IOException { // Test re-run outputs = pipeline.run(args, pipelineOutputs, pipelineInputs); - assert outputs.get(0).type == InterfaceTypes.TextFile; + assert outputs.get(0).type == InterfaceType.TextFile; PurePosixPath outputPath1 = ((TextFile) outputs.get(0).data).path; String content1 = IO.readString(outputPath1.toString()); assert content1.equals("The answer is 42."); - assert outputs.get(1).type == InterfaceTypes.BinaryFile; + assert outputs.get(1).type == InterfaceType.BinaryFile; PurePosixPath outputPath2 = ((BinaryFile) outputs.get(1).data).path; byte[] content2 = IO.readBytes(outputPath2.toString()); assert content2[0] == (byte) 222;