From 92e4585e53e5c802bd43e2268a86c93eea3bc4ac Mon Sep 17 00:00:00 2001 From: summer-alice Date: Sun, 27 Aug 2023 21:10:23 +1000 Subject: [PATCH 1/3] Add java.io.FileDescriptor/SyncFailedException Down the line this will allow FileOutputStream.this(FileDescriptor) to work for STDIN, STDOUT, and STDERR System.out and co. outline: https://stackoverflow.com/a/14435324 --- base/src/java/io/FileDescriptor.d | 165 +++++++++++++++++++++++++ base/src/java/io/SyncFailedException.d | 22 ++++ 2 files changed, 187 insertions(+) create mode 100644 base/src/java/io/FileDescriptor.d create mode 100644 base/src/java/io/SyncFailedException.d diff --git a/base/src/java/io/FileDescriptor.d b/base/src/java/io/FileDescriptor.d new file mode 100644 index 0000000..2468779 --- /dev/null +++ b/base/src/java/io/FileDescriptor.d @@ -0,0 +1,165 @@ +module java.io.FileDescriptor; + +version(Posix) { + import core.stdc.errno : errno; + import core.stdc.string : strerror; + import core.sys.posix.unistd : fsync; +} else version(Windows) { + import core.sys.windows.winbase; +} + +import std.string : fromStringz; + +import java.io.SyncFailedException; + +/** + * Instances of the file descriptor class serve as an opaque handle to the + * underlying machine-specific structure representing an open file, an open + * socket, or another source or sink of bytes. + * + * The main practical use for a file descriptor is to create a + * `FileInputStream` or `FileOutputStream` to contain it. + * + * Applications should not create their own file descriptors. + * + * See_Also: FileInputStream, FileOutputStream + */ +public final class FileDescriptor +{ + private const int fFD; + private const void* fHandle; + + /// UNIX file descriptor + package int getFD() + { + return fFD; + } + + /// Windows file HANDLE + package const(void*) getHandle() + { + return fHandle; + } + + /** + * A handle to the standard input stream. + * + * Usually, this file descriptor is not used directly, but rather via the + * output stream known as `System.in`. + */ + public static immutable FileDescriptor in_; + + /** + * A handle to the standard output stream. + * + * Usually, this file descriptor is not used directly, but rather via the + * output stream known as `System.out`. + */ + public static immutable FileDescriptor out_; + + /** + * A handle to the standard error stream. + * + * Usually, this file descriptor is not used directly, but rather via the + * output stream known as `System.err`. + */ + public static immutable FileDescriptor err; + + shared static this() + { + in_ = new immutable FileDescriptor(0); + out_ = new immutable FileDescriptor(1); + err = new immutable FileDescriptor(2); + } + + /** + * Construct an (invalid) FileDescriptor object. + */ + public this() + { + fFD = -1; + fHandle = null; + } + + private immutable this(int fd) + { + fFD = fd; + version (Posix) { + fHandle = null; + } else version(Windows) { + if (fd == 0) { + fHandle = GetStdHandle(STD_INPUT_HANDLE); + } else if (fd == 1) { + fHandle = GetStdHandle(STD_OUTPUT_HANDLE); + } else if (fd == 2) { + fHandle = GetStdHandle(STD_ERROR_HANDLE); + } + } + } + + /** + * Tests if this file descriptor object is valid. + * + * Returns: `true` if the file descriptor object represents a valid, open + * file, socket, or other active I/O connection; `false` otherwise. + */ + public bool valid() const + { + version (Posix) { + return fFD >= 0; + } else version (Windows) { + return (fFD >= 0) && (fHandle !is null); + } + } + + /** + * Force all system buffers to syncrhonize with the underlying device. + * + * This method returns after all modified data and attributes of this + * FileDescriptor have been written to the relevant device(s). In + * particular, if this FileDescriptor refers to a physical storage medium, + * such as a file in a file system, sync will not return until all + * in-memory modified copies of buffers associated with this FileDescriptor + * have been written to the physical medium. sync is meant to be used by + * code that requires physical storage (such as a file) to be in a known + * state. For example, a class that provided a simple transaction facility + * might use sync to ensure that all changes to a file caused by a given + * transaction were recorded on a storage medium. sync only affects + * buffers downstream of this FileDescriptor. If any in-memory buffering + * is being done by the application (for example, by a BufferedOutputStream + * object), those buffers must be flushed into the FileDescriptor (for + * example, by invoking OutputStream.flush) before that data will be + * affected by sync. + * + * Throws: SyncFailedException when the buffer cannot be flushed, or + * because the system cannot guarantee that all buffers have been + * synchronized with physical media. + */ + public void sync() const + { + version(Posix) { + int res = fsync(fFD); + if (res != 0) { + throw new SyncFailedException(getLastError()); + } + } else version(Windows) { + BOOL res = FlushFileBuffers(fHandle); + if (res == 0) { + throw new SyncFailedException(getLastError()); + } + } + } + + + version(Posix) { + private string getLastError() const + { + return fromStringz(strerror(errno)).dup; + } + } else version(Windows) { + private string getLastError() const + { + return fromStringz(GetLastError()).dup; + } + } +} diff --git a/base/src/java/io/SyncFailedException.d b/base/src/java/io/SyncFailedException.d new file mode 100644 index 0000000..70cfa13 --- /dev/null +++ b/base/src/java/io/SyncFailedException.d @@ -0,0 +1,22 @@ +module java.io.SyncFailedException; + +import java.lang.exceptions : IOException; + +/** + * Signals that a sync operation has failed. + */ +public class SyncFailedException : IOException +{ + /** + * Constructs a SyncFailedException with a detail message. + * + * A detail message is a String that describes this particular exception. + * + * Params: + * desc = a String describing the exception. + */ + this(string desc) + { + super(msg); + } +} From c96891988ec24f366c5c80e8bc2e784573efc5de Mon Sep 17 00:00:00 2001 From: summer-alice Date: Sat, 9 Sep 2023 13:26:36 +1000 Subject: [PATCH 2/3] Fix FileDescriptor on Windows 'const' from fFD and fHandle were removed to allow 'setting' in the future. --- base/src/java/io/FileDescriptor.d | 45 ++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/base/src/java/io/FileDescriptor.d b/base/src/java/io/FileDescriptor.d index 2468779..57617d0 100644 --- a/base/src/java/io/FileDescriptor.d +++ b/base/src/java/io/FileDescriptor.d @@ -5,7 +5,7 @@ version(Posix) { import core.stdc.string : strerror; import core.sys.posix.unistd : fsync; } else version(Windows) { - import core.sys.windows.winbase; + import core.sys.windows.core; } import std.string : fromStringz; @@ -26,8 +26,8 @@ import java.io.SyncFailedException; */ public final class FileDescriptor { - private const int fFD; - private const void* fHandle; + private int fFD; + private void* fHandle; /// UNIX file descriptor package int getFD() @@ -36,7 +36,7 @@ public final class FileDescriptor } /// Windows file HANDLE - package const(void*) getHandle() + package void* getHandle() { return fHandle; } @@ -47,7 +47,7 @@ public final class FileDescriptor * Usually, this file descriptor is not used directly, but rather via the * output stream known as `System.in`. */ - public static immutable FileDescriptor in_; + public static const FileDescriptor in_; /** * A handle to the standard output stream. @@ -55,7 +55,7 @@ public final class FileDescriptor * Usually, this file descriptor is not used directly, but rather via the * output stream known as `System.out`. */ - public static immutable FileDescriptor out_; + public static const FileDescriptor out_; /** * A handle to the standard error stream. @@ -63,13 +63,13 @@ public final class FileDescriptor * Usually, this file descriptor is not used directly, but rather via the * output stream known as `System.err`. */ - public static immutable FileDescriptor err; + public static const FileDescriptor err; shared static this() { - in_ = new immutable FileDescriptor(0); - out_ = new immutable FileDescriptor(1); - err = new immutable FileDescriptor(2); + in_ = new FileDescriptor(0); + out_ = new FileDescriptor(1); + err = new FileDescriptor(2); } /** @@ -81,7 +81,7 @@ public final class FileDescriptor fHandle = null; } - private immutable this(int fd) + private this(int fd) { fFD = fd; version (Posix) { @@ -143,7 +143,7 @@ public final class FileDescriptor throw new SyncFailedException(getLastError()); } } else version(Windows) { - BOOL res = FlushFileBuffers(fHandle); + const res = FlushFileBuffers(cast(void*)fHandle); if (res == 0) { throw new SyncFailedException(getLastError()); } @@ -159,7 +159,26 @@ public final class FileDescriptor } else version(Windows) { private string getLastError() const { - return fromStringz(GetLastError()).dup; + import java.lang.String : String_valueOf, fromString16z; + + LPWSTR buffer; + DWORD errorCode = GetLastError(); + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + null, + errorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + cast(LPWSTR)&buffer, + 0, + null); + + wstring result = fromString16z(buffer); + LocalFree(buffer); + + return String_valueOf(result); } } } From cd49f2a69fb85144125fe99ed20ed8eaed77ce7b Mon Sep 17 00:00:00 2001 From: summer-alice Date: Sat, 9 Sep 2023 18:53:45 +1000 Subject: [PATCH 3/3] Drop 'shared' from static this Was used when in_, out_, and err were immutable but isn't required now. --- base/src/java/io/FileDescriptor.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/java/io/FileDescriptor.d b/base/src/java/io/FileDescriptor.d index 57617d0..11f3ce5 100644 --- a/base/src/java/io/FileDescriptor.d +++ b/base/src/java/io/FileDescriptor.d @@ -65,7 +65,7 @@ public final class FileDescriptor */ public static const FileDescriptor err; - shared static this() + static this() { in_ = new FileDescriptor(0); out_ = new FileDescriptor(1);