From 34fbe76659fd2432a9f15f3895e30d4ec2a53c1c Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Wed, 13 Jan 2016 12:28:49 +0000 Subject: [PATCH 1/4] std.stdio: Refactor LockingTextWriter.handle_ field into property --- std/stdio.d | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/std/stdio.d b/std/stdio.d index 60d98ed6319..7b1da668813 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -2423,8 +2423,13 @@ $(D Range) that locks the file and allows fast writing to it. { private: import std.range.primitives : ElementType, isInfinite, isInputRange; - FILE* fps_; // the shared file handle - _iobuf* handle_; // the unshared version of fps + // the shared file handle + FILE* fps_; + + // the unshared version of fps + @property _iobuf* handle_() @trusted { return cast(_iobuf*)fps_; } + + // the file's orientation (byte- or wide-oriented) int orientation_; public: deprecated("accessing fps/handle/orientation directly can break LockingTextWriter integrity") @@ -2443,7 +2448,6 @@ $(D Range) that locks the file and allows fast writing to it. fps_ = f._p.handle; orientation_ = fwide(fps_, 0); FLOCK(fps_); - handle_ = cast(_iobuf*)fps_; } ~this() @trusted @@ -2452,7 +2456,6 @@ $(D Range) that locks the file and allows fast writing to it. { FUNLOCK(fps_); fps_ = null; - handle_ = null; } } From 60448be39ac6f8fa6c23aea337068ea96b705af6 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Wed, 13 Jan 2016 12:29:08 +0000 Subject: [PATCH 2/4] std.stdio: Add lockingBinaryWriter --- std/stdio.d | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) diff --git a/std/stdio.d b/std/stdio.d index 7b1da668813..217836aed16 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -2606,6 +2606,213 @@ See $(LREF byChunk) for an example. return LockingTextWriter(this); } + // An output range which optionally locks the file and puts it into + // binary mode (similar to rawWrite). Because it needs to restore + // the file mode on destruction, it is RefCounted on Windows. + struct BinaryWriterImpl(bool locking) + { + private: + FILE* fps; + string name; + + version(Windows) + { + int fd, oldMode; + version(DIGITAL_MARS_STDIO) + ubyte oldInfo; + } + + package: + this(ref File f) + { + import std.exception : enforce; + + enforce(f._p && f._p.handle); + name = f._name; + fps = f._p.handle; + static if (locking) + FLOCK(fps); + + version(Windows) + { + .fflush(fps); // before changing translation mode + fd = ._fileno(fps); + oldMode = ._setmode(fd, _O_BINARY); + version(DIGITAL_MARS_STDIO) + { + import core.atomic; + + // @@@BUG@@@ 4243 + oldInfo = __fhnd_info[fd]; + atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT); + } + } + } + + public: + ~this() + { + if(fps) + { + version(Windows) + { + .fflush(fps); // before restoring translation mode + version(DIGITAL_MARS_STDIO) + { + // @@@BUG@@@ 4243 + __fhnd_info[fd] = oldInfo; + } + ._setmode(fd, oldMode); + } + + FUNLOCK(fps); + fps = null; + } + } + + void rawWrite(T)(in T[] buffer) + { + import std.conv : text; + import std.exception : errnoEnforce; + + auto result = + .fwrite(buffer.ptr, T.sizeof, buffer.length, fps); + if (result == result.max) result = 0; + errnoEnforce(result == buffer.length, + text("Wrote ", result, " instead of ", buffer.length, + " objects of type ", T.stringof, " to file `", + name, "'")); + } + + version (Windows) + { + @disable this(this); + } + else + { + this(this) + { + if(fps) + { + FLOCK(fps); + } + } + } + + void put(T)(auto ref in T value) + if (!hasIndirections!T + && !isInputRange!T) + { + rawWrite((&value)[0..1]); + } + + void put(T)(in T[] array) + if (!hasIndirections!T + && !isInputRange!T) + { + rawWrite(array); + } + } + +/** Returns an output range that locks the file and allows fast writing to it. + +Example: +Produce a grayscale image of the $(LUCKY Mandelbrot set) +in binary $(LUCKY Netpbm format) to standard output. +--- +import std.algorithm, std.range, std.stdio; + +void main() +{ + enum size = 500; + writef("P5\n%d %d %d\n", size, size, ubyte.max); + + iota(-1, 3, 2.0/size).map!(y => + iota(-1.5, 0.5, 2.0/size).map!(x => + cast(ubyte)(1+ + recurrence!((a, n) => x + y*1i + a[n-1]^^2)(0+0i) + .take(ubyte.max) + .countUntil!(z => z.re^^2 + z.im^^2 > 4)) + ) + ) + .copy(stdout.lockingBinaryWriter); +} +--- +*/ + auto lockingBinaryWriter() + { + alias LockingBinaryWriterImpl = BinaryWriterImpl!true; + + version(Windows) + { + import std.typecons : RefCounted; + alias LockingBinaryWriter = RefCounted!LockingBinaryWriterImpl; + } + else + alias LockingBinaryWriter = LockingBinaryWriterImpl; + + return LockingBinaryWriter(this); + } + + unittest + { + import std.algorithm : copy, reverse; + static import std.file; + import std.exception : collectException; + import std.range : only, retro, put; + import std.string : format; + + auto deleteme = testFilename(); + scope(exit) collectException(std.file.remove(deleteme)); + auto output = File(deleteme, "wb"); + auto writer = output.lockingBinaryWriter(); + auto input = File(deleteme, "rb"); + + T[] readExact(T)(T[] buf) + { + auto result = input.rawRead(buf); + assert(result.length == buf.length, + "Read %d out of %d bytes" + .format(result.length, buf.length)); + return result; + } + + // test raw values + ubyte byteIn = 42; + byteIn.only.copy(writer); output.flush(); + ubyte byteOut = readExact(new ubyte[1])[0]; + assert(byteIn == byteOut); + + // test arrays + ubyte[] bytesIn = [1, 2, 3, 4, 5]; + bytesIn.copy(writer); output.flush(); + ubyte[] bytesOut = readExact(new ubyte[bytesIn.length]); + scope(failure) .writeln(bytesOut); + assert(bytesIn == bytesOut); + + // test ranges of values + bytesIn.retro.copy(writer); output.flush(); + bytesOut = readExact(bytesOut); + bytesOut.reverse(); + assert(bytesIn == bytesOut); + + // test string + "foobar".copy(writer); output.flush(); + char[] charsOut = readExact(new char[6]); + assert(charsOut == "foobar"); + + // test ranges of arrays + only("foo", "bar").copy(writer); output.flush(); + charsOut = readExact(charsOut); + assert(charsOut == "foobar"); + + // test that we are writing arrays as is, + // without UTF-8 transcoding + "foo"d.copy(writer); output.flush(); + dchar[] dcharsOut = readExact(new dchar[3]); + assert(dcharsOut == "foo"); + } + /// Get the size of the file, ulong.max if file is not searchable, but still throws if an actual error occurs. @property ulong size() @safe { From 458c1ceb357361307399cc3b40087afed7653ba9 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Wed, 13 Jan 2016 12:29:18 +0000 Subject: [PATCH 3/4] fix Issue 12379 - Add toFile function which writes its first argument to a file --- std/file.d | 2 ++ std/stdio.d | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/std/file.d b/std/file.d index 569b674011f..f7fe138dd0e 100644 --- a/std/file.d +++ b/std/file.d @@ -404,6 +404,8 @@ Params: buffer = data to be written to file Throws: $(D FileException) on error. + +See_also: $(XREF stdio,toFile) */ void write(R)(R name, const void[] buffer) if (isInputRange!R && isSomeChar!(ElementEncodingType!R) || isSomeString!R) diff --git a/std/stdio.d b/std/stdio.d index 217836aed16..c58bca1830f 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -4122,6 +4122,32 @@ unittest f.close(); } + +/** +Writes an array or range to a file. +Shorthand for $(D data.copy(File(fileName, "wb").lockingBinaryWriter)). +Similar to $(XREF file,write), strings are written as-is, +rather than encoded according to the $(D File)'s $(WEB +en.cppreference.com/w/c/io#Narrow_and_wide_orientation, +orientation). +*/ +void toFile(T)(T data, string fileName) + if (is(typeof(std.algorithm.mutation.copy(data, stdout.lockingBinaryWriter)))) +{ + std.algorithm.mutation.copy(data, File(fileName, "wb").lockingBinaryWriter); +} + +unittest +{ + static import std.file; + + auto deleteme = testFilename(); + scope(exit) { std.file.remove(deleteme); } + + "Test".toFile(deleteme); + assert(std.file.readText(deleteme) == "Test"); +} + /********************* * Thrown if I/O errors happen. */ From c697f6464065b0f88c2ad3a18c73d2f94c0d1fde Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Sun, 27 Mar 2016 05:42:42 +0000 Subject: [PATCH 4/4] std.stdio: Fix style nits --- std/stdio.d | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/std/stdio.d b/std/stdio.d index c58bca1830f..28714a2eae9 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -2427,7 +2427,7 @@ $(D Range) that locks the file and allows fast writing to it. FILE* fps_; // the unshared version of fps - @property _iobuf* handle_() @trusted { return cast(_iobuf*)fps_; } + @property _iobuf* handle_() @trusted { return cast(_iobuf*) fps_; } // the file's orientation (byte- or wide-oriented) int orientation_; @@ -2615,10 +2615,10 @@ See $(LREF byChunk) for an example. FILE* fps; string name; - version(Windows) + version (Windows) { int fd, oldMode; - version(DIGITAL_MARS_STDIO) + version (DIGITAL_MARS_STDIO) ubyte oldInfo; } @@ -2633,12 +2633,12 @@ See $(LREF byChunk) for an example. static if (locking) FLOCK(fps); - version(Windows) + version (Windows) { .fflush(fps); // before changing translation mode fd = ._fileno(fps); oldMode = ._setmode(fd, _O_BINARY); - version(DIGITAL_MARS_STDIO) + version (DIGITAL_MARS_STDIO) { import core.atomic; @@ -2652,22 +2652,22 @@ See $(LREF byChunk) for an example. public: ~this() { - if(fps) + if (!fps) + return; + + version (Windows) { - version(Windows) + .fflush(fps); // before restoring translation mode + version (DIGITAL_MARS_STDIO) { - .fflush(fps); // before restoring translation mode - version(DIGITAL_MARS_STDIO) - { - // @@@BUG@@@ 4243 - __fhnd_info[fd] = oldInfo; - } - ._setmode(fd, oldMode); + // @@@BUG@@@ 4243 + __fhnd_info[fd] = oldInfo; } - - FUNLOCK(fps); - fps = null; + ._setmode(fd, oldMode); } + + FUNLOCK(fps); + fps = null; } void rawWrite(T)(in T[] buffer) @@ -2692,7 +2692,7 @@ See $(LREF byChunk) for an example. { this(this) { - if(fps) + if (fps) { FLOCK(fps); } @@ -2700,15 +2700,15 @@ See $(LREF byChunk) for an example. } void put(T)(auto ref in T value) - if (!hasIndirections!T - && !isInputRange!T) + if (!hasIndirections!T && + !isInputRange!T) { rawWrite((&value)[0..1]); } void put(T)(in T[] array) - if (!hasIndirections!T - && !isInputRange!T) + if (!hasIndirections!T && + !isInputRange!T) { rawWrite(array); } @@ -2743,7 +2743,7 @@ void main() { alias LockingBinaryWriterImpl = BinaryWriterImpl!true; - version(Windows) + version (Windows) { import std.typecons : RefCounted; alias LockingBinaryWriter = RefCounted!LockingBinaryWriterImpl;