- * {@code strlen} interprets the string as a single-byte character string, - * so its return value is always equal to the number of bytes, - * even if the string contains multibyte characters. - * - * @param str Null-terminated string. - * @return the number of characters in {@code str}, excluding the terminal null. - * No return value is reserved to indicate an error. - */ - public static long strlen(MemorySegment str) { - try { - return (long) strlen.invokeExact(str); - } catch (Throwable e) { - throw new AssertionError("should not reach here", e); - } - } - /** * Creates a segment allocator with the given arena. * diff --git a/modules/overrungl.nfd/src/main/java/overrungl/nfd/NFD.java b/modules/overrungl.nfd/src/main/java/overrungl/nfd/NFD.java index e935fa06..d067b159 100644 --- a/modules/overrungl.nfd/src/main/java/overrungl/nfd/NFD.java +++ b/modules/overrungl.nfd/src/main/java/overrungl/nfd/NFD.java @@ -84,7 +84,7 @@ * var filterItem = NFDNFilterItem.create(allocator, * new Pair<>("Source code", "java"), * new Pair<>("Image file", "png,jpg")); - * } + *} *
* A file filter is a pair of strings comprising the friendly name and the specification * (multiple file extensions are comma-separated). @@ -573,7 +573,7 @@ default NFDResult pathSetEnumNextN(@NativeType("nfdpathsetenum_t*") MemorySegmen if (result == NFDResult.OKAY) { final MemorySegment path = seg.get(Unmarshal.STR_LAYOUT, 0); if (!Unmarshal.isNullPointer(path)) { - Unmarshal.copy(path, outPath, NFDInternal.nfdCharset); + outPath[0] = path.getString(0L, NFDInternal.nfdCharset); pathSetFreePathN(path); } } diff --git a/modules/overrungl.stb/src/main/java/overrungl/stb/STBImage.java b/modules/overrungl.stb/src/main/java/overrungl/stb/STBImage.java index 0fa0fb59..e287f1ff 100644 --- a/modules/overrungl.stb/src/main/java/overrungl/stb/STBImage.java +++ b/modules/overrungl.stb/src/main/java/overrungl/stb/STBImage.java @@ -255,11 +255,11 @@ default MemorySegment loadFromMemory(MemorySegment buffer, MemorySegment x, Memo } @Entrypoint("stbi_load_from_memory") - MemorySegment loadFromMemory(SegmentAllocator allocator, int len, byte[] buffer, @Ref int[] x, @Ref int[] y, @Ref int[] channelsInFile, int desiredChannels); + MemorySegment loadFromMemory(SegmentAllocator allocator, byte[] buffer, int len, @Ref int[] x, @Ref int[] y, @Ref int[] channelsInFile, int desiredChannels); @Skip default MemorySegment loadFromMemory(SegmentAllocator allocator, byte[] buffer, @Ref int[] x, @Ref int[] y, @Ref int[] channelsInFile, int desiredChannels) { - return loadFromMemory(allocator, buffer.length, buffer, x, y, channelsInFile, desiredChannels); + return loadFromMemory(allocator, buffer, buffer.length, x, y, channelsInFile, desiredChannels); } @Entrypoint("stbi_load_gif_from_memory") diff --git a/modules/overrungl.stb/src/main/java/overrungl/stb/STBPerlin.java b/modules/overrungl.stb/src/main/java/overrungl/stb/STBPerlin.java index 6f9aac62..b727fbc2 100644 --- a/modules/overrungl.stb/src/main/java/overrungl/stb/STBPerlin.java +++ b/modules/overrungl.stb/src/main/java/overrungl/stb/STBPerlin.java @@ -17,6 +17,7 @@ package overrungl.stb; import overrun.marshal.Downcall; +import overrun.marshal.gen.Entrypoint; /** * The STB perlin noise generator. @@ -51,6 +52,7 @@ public interface STBPerlin { * @param wrapZ wrap z * @return the value */ + @Entrypoint("stb_perlin_noise3") float noise3(float x, float y, float z, int wrapX, int wrapY, int wrapZ); /** @@ -67,6 +69,7 @@ public interface STBPerlin { * @param seed the seed * @return the value */ + @Entrypoint("stb_perlin_noise3_seed") float noise3seed(float x, float y, float z, int wrapX, int wrapY, int wrapZ, int seed); /** @@ -84,6 +87,7 @@ public interface STBPerlin { * @param octaves = 6 -- number of "octaves" of noise3() to sum * @return the value */ + @Entrypoint("stb_perlin_ridge_noise3") float ridgeNoise3(float x, float y, float z, float lacunarity, float gain, float offset, int octaves); /** @@ -100,6 +104,7 @@ public interface STBPerlin { * @param octaves = 6 -- number of "octaves" of noise3() to sum * @return the value */ + @Entrypoint("stb_perlin_fbm_noise3") float fbmNoise3(float x, float y, float z, float lacunarity, float gain, int octaves); /** @@ -116,6 +121,7 @@ public interface STBPerlin { * @param octaves = 6 -- number of "octaves" of noise3() to sum * @return the value */ + @Entrypoint("stb_perlin_turbulence_noise3") float turbulenceNoise3(float x, float y, float z, float lacunarity, float gain, int octaves); /** @@ -129,5 +135,6 @@ public interface STBPerlin { * @param wrapZ wrapZ * @param seed seed */ + @Entrypoint("stb_perlin_noise3_wrap_nonpow2") float noise3wrapNonpow2(float x, float y, float z, int wrapX, int wrapY, int wrapZ, byte seed); } diff --git a/modules/samples/src/test/java/overrungl/demo/glfw/GLFWJoystickTest.java b/modules/samples/src/test/java/overrungl/demo/glfw/GLFWJoystickTest.java index cf5e508c..14e3e0dd 100644 --- a/modules/samples/src/test/java/overrungl/demo/glfw/GLFWJoystickTest.java +++ b/modules/samples/src/test/java/overrungl/demo/glfw/GLFWJoystickTest.java @@ -16,11 +16,11 @@ package overrungl.demo.glfw; -import overrungl.glfw.GLFWCallbacks; +import overrun.marshal.Unmarshal; import overrungl.glfw.GLFW; +import overrungl.glfw.GLFWCallbacks; import overrungl.glfw.GLFWErrorCallback; import overrungl.glfw.GLFWGamepadState; -import overrungl.util.CheckUtil; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; @@ -48,13 +48,13 @@ public void run() { private void init() { GLFWErrorCallback.createPrint().set(); - CheckUtil.check(glfw.init(), "Unable to initialize GLFW"); + if (!glfw.init()) throw new IllegalStateException("Unable to initialize GLFW"); glfw.defaultWindowHints(); glfw.windowHint(GLFW.VISIBLE, false); glfw.windowHint(GLFW.RESIZABLE, true); glfw.windowHint(GLFW.CLIENT_API, GLFW.NO_API); window = glfw.createWindow(200, 100, "Holder", MemorySegment.NULL, MemorySegment.NULL); - CheckUtil.checkNotNullptr(window, "Failed to create the GLFW window"); + if (Unmarshal.isNullPointer(window)) throw new IllegalStateException("Failed to create the GLFW window"); glfw.setKeyCallback(window, (_, key, _, action, _) -> { if (key == GLFW.KEY_ESCAPE && action == GLFW.RELEASE) { glfw.setWindowShouldClose(window, true); diff --git a/modules/samples/src/test/java/overrungl/demo/glfw/GLFWWindowIconTest.java b/modules/samples/src/test/java/overrungl/demo/glfw/GLFWWindowIconTest.java index eebb8566..191fb2a2 100644 --- a/modules/samples/src/test/java/overrungl/demo/glfw/GLFWWindowIconTest.java +++ b/modules/samples/src/test/java/overrungl/demo/glfw/GLFWWindowIconTest.java @@ -16,15 +16,15 @@ package overrungl.demo.glfw; +import overrun.marshal.Unmarshal; import overrungl.demo.util.IOUtil; -import overrungl.glfw.GLFWCallbacks; import overrungl.glfw.GLFW; +import overrungl.glfw.GLFWCallbacks; import overrungl.glfw.GLFWErrorCallback; import overrungl.glfw.GLFWImage; import overrungl.opengl.GL; import overrungl.opengl.GLLoader; import overrungl.stb.STBImage; -import overrungl.util.CheckUtil; import java.io.IOException; import java.lang.foreign.Arena; @@ -60,12 +60,12 @@ public void run() { private void init(Arena arena) { GLFWErrorCallback.createPrint().set(); - CheckUtil.check(glfw.init(), "Unable to initialize GLFW"); + if (!glfw.init()) throw new IllegalStateException("Unable to initialize GLFW"); glfw.defaultWindowHints(); glfw.windowHint(GLFW.VISIBLE, false); glfw.windowHint(GLFW.RESIZABLE, true); window = glfw.createWindow(300, 300, "Hello World!", MemorySegment.NULL, MemorySegment.NULL); - CheckUtil.checkNotNullptr(window, "Failed to create the GLFW window"); + if (Unmarshal.isNullPointer(window)) throw new IllegalStateException("Failed to create the GLFW window"); try { final STBImage stbImage = STBImage.INSTANCE; @@ -73,7 +73,7 @@ private void init(Arena arena) { var py = arena.allocate(JAVA_INT); var pc = arena.allocate(JAVA_INT); var data = stbImage.loadFromMemory( - IOUtil.ioResourceToSegment(arena, "image.png", 256), + IOUtil.ioResourceToSegment(arena, "image.png"), px, py, pc, STBImage.RGB_ALPHA ); final GLFWImage image = new GLFWImage(arena); diff --git a/modules/samples/src/test/java/overrungl/demo/opengl/GL10Test.java b/modules/samples/src/test/java/overrungl/demo/opengl/GL10Test.java index 1bf236b4..c2e611c5 100644 --- a/modules/samples/src/test/java/overrungl/demo/opengl/GL10Test.java +++ b/modules/samples/src/test/java/overrungl/demo/opengl/GL10Test.java @@ -16,13 +16,13 @@ package overrungl.demo.opengl; +import overrun.marshal.Unmarshal; import overrungl.glfw.GLFW; import overrungl.glfw.GLFWCallbacks; import overrungl.glfw.GLFWErrorCallback; import overrungl.opengl.GL; import overrungl.opengl.GLLegacy; import overrungl.opengl.GLLoader; -import overrungl.util.CheckUtil; import java.lang.foreign.MemorySegment; import java.util.Objects; @@ -52,12 +52,12 @@ public void run() { private void init() { GLFWErrorCallback.createPrint().set(); - CheckUtil.check(glfw.init(), "Unable to initialize GLFW"); + if (!glfw.init()) throw new IllegalStateException("Unable to initialize GLFW"); glfw.defaultWindowHints(); glfw.windowHint(GLFW.VISIBLE, false); glfw.windowHint(GLFW.RESIZABLE, true); window = glfw.createWindow(300, 300, "Hello World!", MemorySegment.NULL, MemorySegment.NULL); - CheckUtil.checkNotNullptr(window, "Failed to create the GLFW window"); + if (Unmarshal.isNullPointer(window)) throw new IllegalStateException("Failed to create the GLFW window"); glfw.setKeyCallback(window, (_, key, _, action, _) -> { if (key == GLFW.KEY_ESCAPE && action == GLFW.RELEASE) { glfw.setWindowShouldClose(window, true); diff --git a/modules/samples/src/test/java/overrungl/demo/opengl/GL15Test.java b/modules/samples/src/test/java/overrungl/demo/opengl/GL15Test.java index c6a7f462..ed20f585 100644 --- a/modules/samples/src/test/java/overrungl/demo/opengl/GL15Test.java +++ b/modules/samples/src/test/java/overrungl/demo/opengl/GL15Test.java @@ -16,16 +16,16 @@ package overrungl.demo.opengl; +import overrun.marshal.Unmarshal; import overrungl.demo.util.IOUtil; -import overrungl.glfw.GLFWCallbacks; import overrungl.glfw.GLFW; +import overrungl.glfw.GLFWCallbacks; import overrungl.glfw.GLFWErrorCallback; import overrungl.opengl.GL; import overrungl.opengl.GL11; import overrungl.opengl.GLLegacy; import overrungl.opengl.GLLoader; import overrungl.stb.STBImage; -import overrungl.util.CheckUtil; import java.io.IOException; import java.lang.foreign.Arena; @@ -65,12 +65,12 @@ public void run() { private void init() { GLFWErrorCallback.createPrint().set(); - CheckUtil.check(glfw.init(), "Unable to initialize GLFW"); + if (!glfw.init()) throw new IllegalStateException("Unable to initialize GLFW"); glfw.defaultWindowHints(); glfw.windowHint(GLFW.VISIBLE, false); glfw.windowHint(GLFW.RESIZABLE, true); window = glfw.createWindow(640, 480, "OpenGL 1.5", MemorySegment.NULL, MemorySegment.NULL); - CheckUtil.checkNotNullptr(window, "Failed to create the GLFW window"); + if (Unmarshal.isNullPointer(window)) throw new IllegalStateException("Failed to create the GLFW window"); glfw.setKeyCallback(window, (_, key, _, action, _) -> { if (key == GLFW.KEY_ESCAPE && action == GLFW.RELEASE) { glfw.setWindowShouldClose(window, true); @@ -120,9 +120,12 @@ private void load(Arena arena) { var py = arena.allocate(JAVA_INT); var pc = arena.allocate(JAVA_INT); var data = stbImage.loadFromMemory( - IOUtil.ioResourceToSegment(arena, "image.png", 256, 128), + IOUtil.ioResourceToSegment(arena, "image.png"), px, py, pc, STBImage.RGB ); + if (Unmarshal.isNullPointer(data)) { + System.err.println(STR."Failed to load image.png: \{stbImage.failureReason()}"); + } gl.texImage2D(GL.TEXTURE_2D, 0, GL.RGB, @@ -149,7 +152,7 @@ private void loop() { gl.enableClientState(GL11.VERTEX_ARRAY); gl.enableClientState(GL11.COLOR_ARRAY); gl.enableClientState(GL11.TEXTURE_COORD_ARRAY); - // 8 double words = 32 bytes + // 8 floats = 32 bytes final int stride = 8 << 2; gl.vertexPointer(3, GL.FLOAT, stride, MemorySegment.NULL); gl.colorPointer(3, GL.FLOAT, stride, MemorySegment.ofAddress(3 << 2)); diff --git a/modules/samples/src/test/java/overrungl/demo/opengl/GL30Test.java b/modules/samples/src/test/java/overrungl/demo/opengl/GL30Test.java index be22c150..fd09db64 100644 --- a/modules/samples/src/test/java/overrungl/demo/opengl/GL30Test.java +++ b/modules/samples/src/test/java/overrungl/demo/opengl/GL30Test.java @@ -17,6 +17,7 @@ package overrungl.demo.opengl; import overrun.marshal.MemoryStack; +import overrun.marshal.Unmarshal; import overrungl.demo.util.IOUtil; import overrungl.glfw.GLFW; import overrungl.glfw.GLFWCallbacks; @@ -24,7 +25,6 @@ import overrungl.opengl.GL; import overrungl.opengl.GLLoader; import overrungl.stb.STBImage; -import overrungl.util.CheckUtil; import java.io.IOException; import java.lang.foreign.Arena; @@ -69,12 +69,12 @@ public void run() { private void init() { GLFWErrorCallback.createPrint().set(); - CheckUtil.check(glfw.init(), "Unable to initialize GLFW"); + if (!glfw.init()) throw new IllegalStateException("Unable to initialize GLFW"); glfw.defaultWindowHints(); glfw.windowHint(GLFW.VISIBLE, false); glfw.windowHint(GLFW.RESIZABLE, true); window = glfw.createWindow(640, 480, "OpenGL 3.0", MemorySegment.NULL, MemorySegment.NULL); - CheckUtil.checkNotNullptr(window, "Failed to create the GLFW window"); + if (Unmarshal.isNullPointer(window)) throw new IllegalStateException("Failed to create the GLFW window"); glfw.setKeyCallback(window, (_, key, _, action, _) -> { if (key == GLFW.KEY_ESCAPE && action == GLFW.RELEASE) { glfw.setWindowShouldClose(window, true); @@ -113,7 +113,7 @@ private void load(Arena arena) { var py = stack.allocate(JAVA_INT); var pc = stack.allocate(JAVA_INT); var data = stbImage.loadFromMemory( - IOUtil.ioResourceToSegment(arena, "image.png", 256), + IOUtil.ioResourceToSegment(arena, "image.png"), px, py, pc, STBImage.RGB ); gl.texImage2D(GL.TEXTURE_2D, diff --git a/modules/samples/src/test/java/overrungl/demo/opengl/GL33Test.java b/modules/samples/src/test/java/overrungl/demo/opengl/GL33Test.java index 04da4825..8bf74611 100644 --- a/modules/samples/src/test/java/overrungl/demo/opengl/GL33Test.java +++ b/modules/samples/src/test/java/overrungl/demo/opengl/GL33Test.java @@ -18,6 +18,7 @@ import org.joml.Matrix4f; import org.overrun.timer.Timer; +import overrun.marshal.Unmarshal; import overrungl.glfw.GLFW; import overrungl.glfw.GLFWCallbacks; import overrungl.glfw.GLFWErrorCallback; @@ -26,7 +27,6 @@ import overrungl.opengl.GLFlags; import overrungl.opengl.GLLoader; import overrungl.opengl.GLUtil; -import overrungl.util.CheckUtil; import java.lang.foreign.Arena; import java.lang.foreign.MemoryLayout; @@ -76,7 +76,7 @@ public void run() { private void init() { GLFWErrorCallback.createPrint().set(); - CheckUtil.check(glfw.init(), "Unable to initialize GLFW"); + if (!glfw.init()) throw new IllegalStateException("Unable to initialize GLFW"); glfw.defaultWindowHints(); glfw.windowHint(GLFW.VISIBLE, false); glfw.windowHint(GLFW.RESIZABLE, true); @@ -85,7 +85,7 @@ private void init() { glfw.windowHint(GLFW.OPENGL_PROFILE, GLFW.OPENGL_CORE_PROFILE); glfw.windowHint(GLFW.OPENGL_DEBUG_CONTEXT, true); window = glfw.createWindow(640, 480, WND_TITLE, MemorySegment.NULL, MemorySegment.NULL); - CheckUtil.checkNotNullptr(window, "Failed to create the GLFW window"); + if (Unmarshal.isNullPointer(window)) throw new IllegalStateException("Failed to create the GLFW window"); glfw.setKeyCallback(window, (_, key, _, action, _) -> { if (key == GLFW.KEY_ESCAPE && action == GLFW.RELEASE) { glfw.setWindowShouldClose(window, true); diff --git a/modules/samples/src/test/java/overrungl/demo/util/IOUtil.java b/modules/samples/src/test/java/overrungl/demo/util/IOUtil.java index 089e04df..6af56545 100644 --- a/modules/samples/src/test/java/overrungl/demo/util/IOUtil.java +++ b/modules/samples/src/test/java/overrungl/demo/util/IOUtil.java @@ -37,21 +37,15 @@ public final class IOUtil { private IOUtil() { } - private static MemorySegment resizeSegment(Arena arena, MemorySegment segment, long newCapacity) { - return arena.allocate(newCapacity).copyFrom(segment); - } - /** * Reads the specified resource and returns the raw data as a {@link MemorySegment}. * - * @param arena the arena. must be {@link MemorySegment.Scope#isAlive() alive} until the data is no longer used. - * @param resource the resource to read. - * @param segmentSize the initial segment size. - * @param bufferSize the buffer size for reading file. + * @param arena the arena. must be {@link MemorySegment.Scope#isAlive() alive} until the data is no longer used. + * @param resource the resource to read. * @return the resource data. * @throws IOException if an IO error occurs. */ - public static MemorySegment ioResourceToSegment(Arena arena, String resource, long segmentSize, int bufferSize) throws IOException { + public static MemorySegment ioResourceToSegment(Arena arena, String resource) throws IOException { final boolean isHttp = resource.startsWith("http"); final Path path = isHttp ? null : Path.of(resource); @@ -69,41 +63,9 @@ public static MemorySegment ioResourceToSegment(Arena arena, String resource, lo Objects.requireNonNull(IOUtil.class.getClassLoader().getResourceAsStream(resource), STR."Failed to load resource '\{resource}'!") ) { - Arena readArena = Arena.ofConfined(); - MemorySegment segment = readArena.allocate(segmentSize); - - // Creates a byte array to avoid creating it each loop - final byte[] bytes = new byte[bufferSize]; - long pos = 0; - int count; - while ((count = is.read(bytes)) > 0) { - if (pos + count >= segment.byteSize()) { - Arena newArena = Arena.ofConfined(); - segment = resizeSegment(newArena, segment, Math.ceilDiv(segment.byteSize() * 3, 2)); // 50% - readArena.close(); - readArena = newArena; - } - MemorySegment.copy(bytes, 0, segment, ValueLayout.JAVA_BYTE, pos, count); - pos += count; - } - readArena.close(); - - return segment.asSlice(0, pos).reinterpret(arena, null); + return arena.allocateFrom(ValueLayout.JAVA_BYTE, is.readAllBytes()); } catch (URISyntaxException e) { throw new IllegalArgumentException(STR."Illegal URI: \{resource}", e); } } - - /** - * Reads the specified resource and returns the raw data as a {@link MemorySegment}. - * - * @param arena the arena. must be {@link MemorySegment.Scope#isAlive() alive} until the data is no longer used. - * @param resource the resource to read. - * @param segmentSize the initial segment size. - * @return the resource data. - * @throws IOException if an IO error occurs. - */ - public static MemorySegment ioResourceToSegment(Arena arena, String resource, long segmentSize) throws IOException { - return ioResourceToSegment(arena, resource, segmentSize, 8192); - } }