diff --git a/source/dyaml/dumper.d b/source/dyaml/dumper.d index b3e7d61..54c57e1 100644 --- a/source/dyaml/dumper.d +++ b/source/dyaml/dumper.d @@ -12,9 +12,10 @@ module dyaml.dumper; -import std.stream; +//import std.stream; import std.typecons; +import dyaml.stream; import dyaml.anchor; import dyaml.emitter; import dyaml.encoding; @@ -59,7 +60,7 @@ import dyaml.tagdirective; * Write to memory: * -------------------- * import std.stream; - * auto stream = new MemoryStream(); + * auto stream = new YMemoryStream(); * auto node = Node([1, 2, 3, 4, 5]); * Dumper(stream).dump(node); * -------------------- @@ -83,20 +84,20 @@ struct Dumper unittest { auto node = Node([1, 2, 3, 4, 5]); - Dumper(new MemoryStream()).dump(node); + Dumper(new YMemoryStream()).dump(node); } unittest { auto node1 = Node([1, 2, 3, 4, 5]); auto node2 = Node("This document contains only one string"); - Dumper(new MemoryStream()).dump(node1, node2); + Dumper(new YMemoryStream()).dump(node1, node2); } unittest { - import std.stream; - auto stream = new MemoryStream(); + //import std.stream; + auto stream = new YMemoryStream(); auto node = Node([1, 2, 3, 4, 5]); Dumper(stream).dump(node); } @@ -106,7 +107,7 @@ struct Dumper auto node = Node([1, 2, 3, 4, 5]); auto representer = new Representer(); auto resolver = new Resolver(); - auto dumper = Dumper(new MemoryStream()); + auto dumper = Dumper(new YMemoryStream()); dumper.representer = representer; dumper.resolver = resolver; dumper.dump(node); @@ -119,7 +120,7 @@ struct Dumper Representer representer_; //Stream to write to. - Stream stream_; + YStream stream_; //True if this Dumper owns stream_ and needs to destroy it in the destructor. bool weOwnStream_ = false; @@ -160,8 +161,10 @@ struct Dumper this(string filename) @trusted { name_ = filename; - try{this(new File(filename, FileMode.OutNew));} - catch(StreamException e) + //try{this(new File(filename, FileMode.OutNew));} + try{this(new YFile(filename));} + //catch(StreamException e) + catch(Exception e) { throw new YAMLException("Unable to open file " ~ filename ~ " for YAML dumping: " ~ e.msg); @@ -171,7 +174,7 @@ struct Dumper } ///Construct a Dumper writing to a _stream. This is useful to e.g. write to memory. - this(Stream stream) @safe + this(YStream stream) @safe { resolver_ = new Resolver(); representer_ = new Representer(); diff --git a/source/dyaml/emitter.d b/source/dyaml/emitter.d index e3aea51..9f8e991 100644 --- a/source/dyaml/emitter.d +++ b/source/dyaml/emitter.d @@ -18,12 +18,12 @@ import std.conv; import std.exception; import std.format; import std.range; -import std.stream; import std.string; import std.system; import std.typecons; import std.utf; +import dyaml.stream; import dyaml.anchor; import dyaml.encoding; import dyaml.escapes; @@ -81,7 +81,7 @@ struct Emitter [TagDirective("!", "!"), TagDirective("!!", "tag:yaml.org,2002:")]; ///Stream to write to. - Stream stream_; + YStream stream_; ///Encoding can be overriden by STREAM-START. Encoding encoding_ = Encoding.UTF_8; @@ -163,12 +163,12 @@ struct Emitter /** * Construct an emitter. * - * Params: stream = Stream to write to. Must be writable. + * Params: stream = YStream to write to. Must be writable. * canonical = Write scalars in canonical form? * indent = Indentation width. * lineBreak = Line break character/s. */ - this(Stream stream, const bool canonical, const int indent, const int width, + this(YStream stream, const bool canonical, const int indent, const int width, const LineBreak lineBreak) @trusted in{assert(stream.writeable, "Can't emit YAML to a non-writable stream");} body @@ -252,7 +252,7 @@ struct Emitter stream_.writeExact(buffer.ptr, buffer.length * dchar.sizeof); break; } - catch(WriteException e) + catch(Exception e) { throw new Error("Unable to write to stream: " ~ e.msg); } @@ -331,7 +331,7 @@ struct Emitter void expectStreamStart() @trusted { enforce(eventTypeIs(EventID.StreamStart), - new Error("Expected StreamStart, but got " ~ event_.idString)); + new Error("Expected YStreamStart, but got " ~ event_.idString)); encoding_ = event_.encoding; writeStreamStart(); @@ -350,7 +350,7 @@ struct Emitter void expectDocumentStart(Flag!"first" first)() @trusted { enforce(eventTypeIs(EventID.DocumentStart) || eventTypeIs(EventID.StreamEnd), - new Error("Expected DocumentStart or StreamEnd, but got " + new Error("Expected DocumentStart or YStreamEnd, but got " ~ event_.idString)); if(event_.id == EventID.DocumentStart) diff --git a/source/dyaml/loader.d b/source/dyaml/loader.d index 4c1bf9d..1839c82 100644 --- a/source/dyaml/loader.d +++ b/source/dyaml/loader.d @@ -24,7 +24,7 @@ import dyaml.scanner; import dyaml.token; -/** Loads YAML documents from files or streams. +/** Loads YAML documents from files or char[]. * * User specified Constructor and/or Resolver can be used to support new * tags / data types. @@ -140,12 +140,6 @@ struct Loader } } - deprecated("Loader.fromString(string) is deprecated. Use Loader.fromString(char[]) instead.") - static Loader fromString(string data) - { - return Loader(cast(ubyte[])data.dup); - } - /** Construct a Loader to load YAML from a string (char []). * * Params: data = String to load YAML from. $(B will) be overwritten during @@ -168,25 +162,6 @@ struct Loader assert(Loader.fromString(cast(char[])"42").load().as!int == 42); } - import std.stream; - deprecated("Loader(Stream) is deprecated. Use Loader(ubyte[]) instead.") - this(Stream stream) @safe - { - try - { - import dyaml.streamcompat; - auto streamBytes = streamToBytesGC(stream); - reader_ = new Reader(streamBytes); - scanner_ = new Scanner(reader_); - parser_ = new Parser(scanner_); - } - catch(YAMLException e) - { - throw new YAMLException("Unable to open stream %s for YAML loading: %s" - .format(name_, e.msg)); - } - } - /** Construct a Loader to load YAML from a buffer. * * Params: yamlData = Buffer with YAML data to load. This may be e.g. a file diff --git a/source/dyaml/reader.d b/source/dyaml/reader.d index 1f1d074..b2a25a4 100644 --- a/source/dyaml/reader.d +++ b/source/dyaml/reader.d @@ -993,7 +993,7 @@ void testEndian(R)() void testPeekPrefixForward(R)() { - import std.stream; + import dyaml.stream; writeln(typeid(R).toString() ~ ": peek/prefix/forward unittest"); ubyte[] data = ByteOrderMarks[BOM.UTF8] ~ cast(ubyte[])"data"; auto reader = new R(data); @@ -1011,7 +1011,7 @@ void testPeekPrefixForward(R)() void testUTF(R)() { - import std.stream; + import dyaml.stream; writeln(typeid(R).toString() ~ ": UTF formats unittest"); dchar[] data = cast(dchar[])"data"; void utf_test(T)(T[] data, BOM bom) diff --git a/source/dyaml/representer.d b/source/dyaml/representer.d index 36b53c1..daedb0b 100644 --- a/source/dyaml/representer.d +++ b/source/dyaml/representer.d @@ -676,7 +676,7 @@ Node representMyClass(ref Node node, Representer representer) @system return representer.representScalar("!myclass.tag", scalar); } -import std.stream; +import dyaml.stream; unittest { @@ -684,7 +684,7 @@ unittest &representMyStructSeq, &representMyStructMap]) { - auto dumper = Dumper(new MemoryStream()); + auto dumper = Dumper(new YMemoryStream()); auto representer = new Representer; representer.addRepresenter!MyStruct(r); dumper.representer = representer; @@ -694,7 +694,7 @@ unittest unittest { - auto dumper = Dumper(new MemoryStream()); + auto dumper = Dumper(new YMemoryStream()); auto representer = new Representer; representer.addRepresenter!MyClass(&representMyClass); dumper.representer = representer; diff --git a/source/dyaml/stream.d b/source/dyaml/stream.d new file mode 100644 index 0000000..01fd4af --- /dev/null +++ b/source/dyaml/stream.d @@ -0,0 +1,74 @@ +module dyaml.stream; + +enum BOM { + UTF8, /// UTF-8 + UTF16LE, /// UTF-16 Little Endian + UTF16BE, /// UTF-16 Big Endian + UTF32LE, /// UTF-32 Little Endian + UTF32BE, /// UTF-32 Big Endian +} + +import std.system; + +private enum int NBOMS = 5; +immutable Endian[NBOMS] BOMEndian = +[ std.system.endian, + Endian.littleEndian, Endian.bigEndian, + Endian.littleEndian, Endian.bigEndian + ]; + +immutable ubyte[][NBOMS] ByteOrderMarks = +[ [0xEF, 0xBB, 0xBF], + [0xFF, 0xFE], + [0xFE, 0xFF], + [0xFF, 0xFE, 0x00, 0x00], + [0x00, 0x00, 0xFE, 0xFF] + ]; + +interface YStream { + void writeExact(const void* buffer, size_t size); + size_t write(const(ubyte)[] buffer); + void flush(); + @property bool writeable(); +} + +class YMemoryStream : YStream { + ubyte[] data; + + void writeExact(const void* buffer, size_t size) { + data ~= cast(ubyte[])buffer[0 .. size]; + } + + size_t write(const(ubyte)[] buffer) { + data ~= buffer; + return buffer.length; + } + + void flush() {} + + @property bool writeable() { return true; } +} + +class YFile : YStream { + static import std.stdio; + std.stdio.File file; + + this(string fn) { + this.file = std.stdio.File(fn, "w"); + } + + void writeExact(const void* buffer, size_t size) { + this.file.write(cast(const ubyte[])buffer[0 .. size]); + } + + size_t write(const(ubyte)[] buffer) { + this.file.write(buffer); + return buffer.length; + } + + void flush() { + this.file.flush(); + } + + @property bool writeable() { return true; } +} diff --git a/source/dyaml/streamcompat.d b/source/dyaml/streamcompat.d deleted file mode 100644 index fa035fd..0000000 --- a/source/dyaml/streamcompat.d +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright Ferdinand Majerech 2014. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -/// Backward compatibility with std.stream (which was used by D:YAML API in the past). -module dyaml.streamcompat; - - -import std.stream; - -import tinyendian; - - -/// A streamToBytes wrapper that allocates memory using the GC. -/// -/// See_Also: streamToBytes -ubyte[] streamToBytesGC(Stream stream) @trusted nothrow -{ - try return stream.streamToBytes(new ubyte[stream.available]); - catch(Exception e) - { - assert(false, "Unexpected exception in streamToBytesGC: " ~ e.msg); - } -} - -/// Read all data from a Stream into an array of bytes. -/// -/// Params: -/// -/// stream = Stream to read from. Must be readable and seekable. -/// memory = Memory to use. Must be long enough to store the entire stream -/// (memory.length >= stream.available). -/// -/// Returns: A slice of memory containing all contents of the stream on success. -/// NULL if unable to read the entire stream. -ubyte[] streamToBytes(Stream stream, ubyte[] memory) @system nothrow -{ - try - { - assert(stream.readable && stream.seekable, - "Can't read YAML from a stream that is not readable and seekable"); - assert(memory.length >= stream.available, - "Not enough memory passed to streamToBytes"); - - auto buffer = memory[0 .. stream.available]; - size_t bytesRead = 0; - for(; bytesRead < buffer.length;) - { - // Returns 0 on eof - const bytes = stream.readBlock(&buffer[bytesRead], buffer.length - bytesRead); - // Reached EOF before reading buffer.length bytes. - if(bytes == 0) { return null; } - bytesRead += bytes; - } - return buffer; - } - catch(Exception e) - { - assert(false, "Unexpected exception in streamToBytes " ~ e.msg); - } -} diff --git a/source/dyaml/testcommon.d b/source/dyaml/testcommon.d index 57e0b19..d64e282 100644 --- a/source/dyaml/testcommon.d +++ b/source/dyaml/testcommon.d @@ -11,7 +11,6 @@ version(unittest) public import std.conv; public import std.stdio; -public import std.stream; public import dyaml.all; import core.exception; @@ -24,8 +23,6 @@ import std.typecons; package: -alias std.stream.File File; - /** * Run an unittest. * diff --git a/source/dyaml/testemitter.d b/source/dyaml/testemitter.d index 1ed8932..fd320ec 100644 --- a/source/dyaml/testemitter.d +++ b/source/dyaml/testemitter.d @@ -15,6 +15,7 @@ import std.file; import std.range; import std.typecons; +import dyaml.stream; import dyaml.dumper; import dyaml.event; import dyaml.testcommon; @@ -83,7 +84,7 @@ void testEmitterOnData(bool verbose, string dataFilename, string canonicalFilena //Must exist due to Anchor, Tags reference counts. auto loader = Loader(dataFilename); auto events = cast(Event[])loader.parse(); - auto emitStream = new MemoryStream; + auto emitStream = new YMemoryStream; Dumper(emitStream).emit(events); if(verbose) @@ -114,7 +115,7 @@ void testEmitterOnCanonical(bool verbose, string canonicalFilename) auto events = cast(Event[])loader.parse(); foreach(canonical; [false, true]) { - auto emitStream = new MemoryStream; + auto emitStream = new YMemoryStream; auto dumper = Dumper(emitStream); dumper.canonical = canonical; dumper.emit(events); @@ -174,7 +175,7 @@ void testEmitterStyles(bool verbose, string dataFilename, string canonicalFilena } styledEvents ~= event; } - auto emitStream = new MemoryStream; + auto emitStream = new YMemoryStream; Dumper(emitStream).emit(styledEvents); if(verbose) { diff --git a/source/dyaml/testinputoutput.d b/source/dyaml/testinputoutput.d index c16aa28..1c3427e 100644 --- a/source/dyaml/testinputoutput.d +++ b/source/dyaml/testinputoutput.d @@ -15,6 +15,7 @@ import std.file; import std.system; import dyaml.testcommon; +import dyaml.stream; alias std.system.endian endian; diff --git a/source/dyaml/testrepresenter.d b/source/dyaml/testrepresenter.d index ee60ec9..c5e7717 100644 --- a/source/dyaml/testrepresenter.d +++ b/source/dyaml/testrepresenter.d @@ -48,7 +48,9 @@ void testRepresenterTypes(bool verbose, string codeFilename) } } - auto emitStream = new MemoryStream; + import dyaml.stream; + + auto emitStream = new YMemoryStream; auto representer = new Representer; representer.addRepresenter!TestClass(&representClass); representer.addRepresenter!TestStruct(&representStruct);