From 18f6f7881ac92a45262aa63e1d2c75ae08adcc79 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 17 Oct 2013 14:40:21 -0500 Subject: [PATCH 1/9] RandomAccessFile: support opening for read/write So far, we only allowed opening in read-only mode. Now, we also support read/write mode in addition. Signed-off-by: Johannes Schindelin --- classpath/java-io.cpp | 7 ++++--- classpath/java/io/RandomAccessFile.java | 10 ++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/classpath/java-io.cpp b/classpath/java-io.cpp index c8a0c8419f..462bed8ada 100644 --- a/classpath/java-io.cpp +++ b/classpath/java-io.cpp @@ -803,17 +803,18 @@ Java_java_io_FileOutputStream_close(JNIEnv* e, jclass, jint fd) extern "C" JNIEXPORT void JNICALL Java_java_io_RandomAccessFile_open(JNIEnv* e, jclass, jstring path, - jlongArray result) + jboolean allowWrite, jlongArray result) { string_t chars = getChars(e, path); if (chars) { jlong peer = 0; jlong length = 0; + int flags = (allowWrite ? O_RDWR | O_CREAT : O_RDONLY) | OPEN_MASK; #if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) #if defined(PLATFORM_WINDOWS) - int fd = ::_wopen(chars, O_RDONLY | OPEN_MASK); + int fd = ::_wopen(chars, flags); #else - int fd = ::open((const char*)chars, O_RDONLY | OPEN_MASK); + int fd = ::open((const char*)chars, flags, 0644); #endif releaseChars(e, path, chars); if (fd == -1) { diff --git a/classpath/java/io/RandomAccessFile.java b/classpath/java/io/RandomAccessFile.java index aeedbd460c..2ab7d6e96f 100644 --- a/classpath/java/io/RandomAccessFile.java +++ b/classpath/java/io/RandomAccessFile.java @@ -17,23 +17,25 @@ public class RandomAccessFile { private File file; private long position = 0; private long length; + private boolean allowWrite; public RandomAccessFile(String name, String mode) throws FileNotFoundException { - if (! mode.equals("r")) throw new IllegalArgumentException(); file = new File(name); + if (mode.equals("rw")) allowWrite = true; + else if (! mode.equals("r")) throw new IllegalArgumentException(); open(); } private void open() throws FileNotFoundException { long[] result = new long[2]; - open(file.getPath(), result); + open(file.getPath(), allowWrite, result); peer = result[0]; length = result[1]; } - private static native void open(String name, long[] result) + private static native void open(String name, boolean allowWrite, long[] result) throws FileNotFoundException; private void refresh() throws IOException { @@ -53,7 +55,7 @@ public class RandomAccessFile { } public void seek(long position) throws IOException { - if (position < 0 || position > length()) throw new IOException(); + if (position < 0 || (!allowWrite && position > length())) throw new IOException(); this.position = position; } From 905ddfe61357b83eaabc089bd22454afbb1077a2 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 17 Oct 2013 14:46:30 -0500 Subject: [PATCH 2/9] Add the RandomAccessFile(File file, String mode) constructor As per the Java API. Signed-off-by: Johannes Schindelin --- classpath/java/io/RandomAccessFile.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/classpath/java/io/RandomAccessFile.java b/classpath/java/io/RandomAccessFile.java index 2ab7d6e96f..c2398b414b 100644 --- a/classpath/java/io/RandomAccessFile.java +++ b/classpath/java/io/RandomAccessFile.java @@ -22,9 +22,16 @@ public class RandomAccessFile { public RandomAccessFile(String name, String mode) throws FileNotFoundException { - file = new File(name); + this(new File(name), mode); + } + + public RandomAccessFile(File file, String mode) + throws FileNotFoundException + { + if (file == null) throw new NullPointerException(); if (mode.equals("rw")) allowWrite = true; else if (! mode.equals("r")) throw new IllegalArgumentException(); + this.file = file; open(); } From 2c0a1c726dd0520607ef6478b9cd7998165fdd56 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 17 Oct 2013 14:46:00 -0500 Subject: [PATCH 3/9] Add write support for RandomAccessFile Signed-off-by: Johannes Schindelin --- classpath/java-io.cpp | 46 +++++++++++++++++++++++++ classpath/java/io/RandomAccessFile.java | 8 +++++ 2 files changed, 54 insertions(+) diff --git a/classpath/java-io.cpp b/classpath/java-io.cpp index 462bed8ada..d461bb7b0c 100644 --- a/classpath/java-io.cpp +++ b/classpath/java-io.cpp @@ -898,6 +898,52 @@ Java_java_io_RandomAccessFile_readBytes(JNIEnv* e, jclass, jlong peer, return (jint)bytesRead; } +extern "C" JNIEXPORT jint JNICALL +Java_java_io_RandomAccessFile_writeBytes(JNIEnv* e, jclass, jlong peer, + jlong position, jbyteArray buffer, + int offset, int length) +{ +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + int fd = (int)peer; + if(::lseek(fd, position, SEEK_SET) == -1) { + throwNewErrno(e, "java/io/IOException"); + return -1; + } + + uint8_t* dst = reinterpret_cast + (e->GetPrimitiveArrayCritical(buffer, 0)); + + int64_t bytesWritten = ::write(fd, dst + offset, length); + e->ReleasePrimitiveArrayCritical(buffer, dst, 0); + + if(bytesWritten == -1) { + throwNewErrno(e, "java/io/IOException"); + return -1; + } +#else + HANDLE hFile = (HANDLE)peer; + LARGE_INTEGER lPos; + lPos.QuadPart = position; + if(!SetFilePointerEx(hFile, lPos, nullptr, FILE_BEGIN)) { + throwNewErrno(e, "java/io/IOException"); + return -1; + } + + uint8_t* dst = reinterpret_cast + (e->GetPrimitiveArrayCritical(buffer, 0)); + + DWORD bytesWritten = 0; + if(!WriteFile(hFile, dst + offset, length, &bytesWritten, nullptr)) { + e->ReleasePrimitiveArrayCritical(buffer, dst, 0); + throwNewErrno(e, "java/io/IOException"); + return -1; + } + e->ReleasePrimitiveArrayCritical(buffer, dst, 0); +#endif + + return (jint)bytesWritten; +} + extern "C" JNIEXPORT void JNICALL Java_java_io_RandomAccessFile_close(JNIEnv* /* e*/, jclass, jlong peer) { diff --git a/classpath/java/io/RandomAccessFile.java b/classpath/java/io/RandomAccessFile.java index c2398b414b..efb1de6ab2 100644 --- a/classpath/java/io/RandomAccessFile.java +++ b/classpath/java/io/RandomAccessFile.java @@ -131,6 +131,14 @@ public class RandomAccessFile { private static native int readBytes(long peer, long position, byte[] buffer, int offset, int length); + public void write(int b) throws IOException { + int count = writeBytes(peer, position, new byte[] { (byte)b }, 0, 1); + if (count > 0) position += count; + } + + private static native int writeBytes(long peer, long position, byte[] buffer, + int offset, int length); + public void close() throws IOException { if (peer != 0) { close(peer); From 8084cb63988d1fc11c7307f2b6a071129a0e5be3 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 21 Oct 2013 10:50:09 -0500 Subject: [PATCH 4/9] Fix indentation in java-io.cpp This was noticed when copy-editing readBytes into writeBytes. Signed-off-by: Johannes Schindelin --- classpath/java-io.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/classpath/java-io.cpp b/classpath/java-io.cpp index d461bb7b0c..a332272afb 100644 --- a/classpath/java-io.cpp +++ b/classpath/java-io.cpp @@ -619,7 +619,7 @@ Java_java_io_File_openDir(JNIEnv* e, jclass, jstring path) #if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) d->handle = FindFirstFileW(RUNTIME_ARRAY_BODY(buffer), &(d->data)); #else - d->handle = FindFirstFileExW(RUNTIME_ARRAY_BODY(buffer), FindExInfoStandard, &(d->data), FindExSearchNameMatch, NULL, 0); + d->handle = FindFirstFileExW(RUNTIME_ARRAY_BODY(buffer), FindExInfoStandard, &(d->data), FindExSearchNameMatch, NULL, 0); #endif if (d->handle == INVALID_HANDLE_VALUE) { d->dispose(); @@ -816,16 +816,16 @@ Java_java_io_RandomAccessFile_open(JNIEnv* e, jclass, jstring path, #else int fd = ::open((const char*)chars, flags, 0644); #endif - releaseChars(e, path, chars); - if (fd == -1) { + releaseChars(e, path, chars); + if (fd == -1) { throwNewErrno(e, "java/io/IOException"); - return; + return; } - struct ::stat fileStats; - if(::fstat(fd, &fileStats) == -1) { - ::close(fd); + struct ::stat fileStats; + if(::fstat(fd, &fileStats) == -1) { + ::close(fd); throwNewErrno(e, "java/io/IOException"); - return; + return; } peer = fd; length = fileStats.st_size; @@ -861,8 +861,8 @@ Java_java_io_RandomAccessFile_readBytes(JNIEnv* e, jclass, jlong peer, #if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) int fd = (int)peer; if(::lseek(fd, position, SEEK_SET) == -1) { - throwNewErrno(e, "java/io/IOException"); - return -1; + throwNewErrno(e, "java/io/IOException"); + return -1; } uint8_t* dst = reinterpret_cast @@ -872,16 +872,16 @@ Java_java_io_RandomAccessFile_readBytes(JNIEnv* e, jclass, jlong peer, e->ReleasePrimitiveArrayCritical(buffer, dst, 0); if(bytesRead == -1) { - throwNewErrno(e, "java/io/IOException"); - return -1; + throwNewErrno(e, "java/io/IOException"); + return -1; } #else HANDLE hFile = (HANDLE)peer; LARGE_INTEGER lPos; lPos.QuadPart = position; if(!SetFilePointerEx(hFile, lPos, nullptr, FILE_BEGIN)) { - throwNewErrno(e, "java/io/IOException"); - return -1; + throwNewErrno(e, "java/io/IOException"); + return -1; } uint8_t* dst = reinterpret_cast @@ -889,8 +889,8 @@ Java_java_io_RandomAccessFile_readBytes(JNIEnv* e, jclass, jlong peer, DWORD bytesRead = 0; if(!ReadFile(hFile, dst + offset, length, &bytesRead, nullptr)) { - throwNewErrno(e, "java/io/IOException"); - return -1; + throwNewErrno(e, "java/io/IOException"); + return -1; } e->ReleasePrimitiveArrayCritical(buffer, dst, 0); #endif From 320fc511dcb07374b780f2a5090dd072e4e6336a Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 21 Oct 2013 10:50:37 -0500 Subject: [PATCH 5/9] Fix potential memory leak in RandomAccessFile#readBytes This was noticed when copy-editing writeBytes. Signed-off-by: Johannes Schindelin --- classpath/java-io.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/classpath/java-io.cpp b/classpath/java-io.cpp index a332272afb..083df06133 100644 --- a/classpath/java-io.cpp +++ b/classpath/java-io.cpp @@ -889,6 +889,7 @@ Java_java_io_RandomAccessFile_readBytes(JNIEnv* e, jclass, jlong peer, DWORD bytesRead = 0; if(!ReadFile(hFile, dst + offset, length, &bytesRead, nullptr)) { + e->ReleasePrimitiveArrayCritical(buffer, dst, 0); throwNewErrno(e, "java/io/IOException"); return -1; } From 4f83f8dd98150bb0179061da4f60e688666aef31 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 17 Oct 2013 14:52:38 -0500 Subject: [PATCH 6/9] ByteBuffer: add missing order() methods Avian's ByteBuffer implementation is actually fixed to big endian. So let's throw an exception if the user tries to change that. Signed-off-by: Johannes Schindelin --- classpath/java/nio/ByteBuffer.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/classpath/java/nio/ByteBuffer.java b/classpath/java/nio/ByteBuffer.java index 71c3c7f09a..ee3767f7de 100644 --- a/classpath/java/nio/ByteBuffer.java +++ b/classpath/java/nio/ByteBuffer.java @@ -244,4 +244,13 @@ public abstract class ByteBuffer protected void checkGet(int position, int amount) { if (amount > limit-position) throw new IndexOutOfBoundsException(); } + + public ByteBuffer order(ByteOrder order) { + if (order != ByteOrder.BIG_ENDIAN) throw new UnsupportedOperationException(); + return this; + } + + public ByteOrder order() { + return ByteOrder.BIG_ENDIAN; + } } From caec6eba67f4d2a3a3cb3e293252efd0c47415e5 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 17 Oct 2013 14:58:13 -0500 Subject: [PATCH 7/9] Add the BufferUnderflowException Pre-compiled code might expect the class to exist... Signed-off-by: Johannes Schindelin --- classpath/java/nio/BufferUnderflowException.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 classpath/java/nio/BufferUnderflowException.java diff --git a/classpath/java/nio/BufferUnderflowException.java b/classpath/java/nio/BufferUnderflowException.java new file mode 100644 index 0000000000..d8b6a6fe88 --- /dev/null +++ b/classpath/java/nio/BufferUnderflowException.java @@ -0,0 +1,14 @@ +/* Copyright (c) 2008-2013, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio; + +public class BufferUnderflowException extends RuntimeException { +} From 3a67f81b5024e963b99d3063424fe8ca8f12efe0 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 17 Oct 2013 14:58:58 -0500 Subject: [PATCH 8/9] Add the FileChannel class Signed-off-by: Johannes Schindelin --- classpath/java/nio/channels/FileChannel.java | 33 ++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 classpath/java/nio/channels/FileChannel.java diff --git a/classpath/java/nio/channels/FileChannel.java b/classpath/java/nio/channels/FileChannel.java new file mode 100644 index 0000000000..e94641086a --- /dev/null +++ b/classpath/java/nio/channels/FileChannel.java @@ -0,0 +1,33 @@ +/* Copyright (c) 2008-2013, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.ByteBuffer; + +public abstract class FileChannel implements Channel { + + public static enum MapMode { + PRIVATE, READ_ONLY, READ_WRITE + }; + + public abstract int read(ByteBuffer dst) throws IOException; + + public abstract int read(ByteBuffer dst, long position) throws IOException; + + public abstract int write(ByteBuffer dst) throws IOException; + + public abstract int write(ByteBuffer dst, long position) throws IOException; + + public abstract long position() throws IOException; + + public abstract FileChannel position(long position) throws IOException; +} From 3681ae508eef88554a8282f6417304e59665b381 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 17 Oct 2013 14:50:43 -0500 Subject: [PATCH 9/9] Implement a rudimentary RandomAccessFile#getChannel This implementation is by no means intended to be complete, just enough to support running http://http://loci.wisc.edu/software/bio-formats's loci.formats.tools.ImageConverter tool. Signed-off-by: Johannes Schindelin --- classpath/java/io/RandomAccessFile.java | 48 +++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/classpath/java/io/RandomAccessFile.java b/classpath/java/io/RandomAccessFile.java index efb1de6ab2..f411bbe748 100644 --- a/classpath/java/io/RandomAccessFile.java +++ b/classpath/java/io/RandomAccessFile.java @@ -11,6 +11,8 @@ package java.io; import java.lang.IllegalArgumentException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; public class RandomAccessFile { private long peer; @@ -147,4 +149,50 @@ public class RandomAccessFile { } private static native void close(long peer); + + public FileChannel getChannel() { + return new FileChannel() { + public void close() { + if (peer != 0) RandomAccessFile.close(peer); + } + + public boolean isOpen() { + return peer != 0; + } + + public int read(ByteBuffer dst, long position) throws IOException { + if (!dst.hasArray()) throw new IOException("Cannot handle " + dst.getClass()); + // TODO: this needs to be synchronized on the Buffer, no? + byte[] array = dst.array(); + return readBytes(peer, position, array, dst.position(), dst.remaining()); + } + + public int read(ByteBuffer dst) throws IOException { + int count = read(dst, position); + if (count > 0) position += count; + return count; + } + + public int write(ByteBuffer src, long position) throws IOException { + if (!src.hasArray()) throw new IOException("Cannot handle " + src.getClass()); + byte[] array = src.array(); + return writeBytes(peer, position, array, src.position(), src.remaining()); + } + + public int write(ByteBuffer src) throws IOException { + int count = write(src, position); + if (count > 0) position += count; + return count; + } + + public long position() throws IOException { + return getFilePointer(); + } + + public FileChannel position(long position) throws IOException { + seek(position); + return this; + } + }; + } }