1 // Written in the D programming language
2 
3 /**
4  * $(RED Deprecated: This module is considered out-dated and not up to Phobos'
5  *       current standards.)
6  *
7  * Source:    $(PHOBOSSRC std/_stream.d)
8  * Macros:
9  *      WIKI = Phobos/StdStream
10  */
11 
12 /*
13  * Copyright (c) 2001-2005
14  * Pavel "EvilOne" Minayev
15  *  with buffering and endian support added by Ben Hinkle
16  *  with buffered readLine performance improvements by Dave Fladebo
17  *  with opApply inspired by (and mostly copied from) Regan Heath
18  *  with bug fixes and MemoryStream/SliceStream enhancements by Derick Eddington
19  *
20  * Permission to use, copy, modify, distribute and sell this software
21  * and its documentation for any purpose is hereby granted without fee,
22  * provided that the above copyright notice appear in all copies and
23  * that both that copyright notice and this permission notice appear
24  * in supporting documentation.  Author makes no representations about
25  * the suitability of this software for any purpose. It is provided
26  * "as is" without express or implied warranty.
27  */
28 module undead.stream;
29 
30 import std.internal.cstring;
31 
32 /* Class structure:
33  *  InputStream       interface for reading
34  *  OutputStream      interface for writing
35  *  Stream            abstract base of stream implementations
36  *    File            an OS file stream
37  *    FilterStream    a base-class for wrappers around another stream
38  *      BufferedStream  a buffered stream wrapping another stream
39  *        BufferedFile  a buffered File
40  *      EndianStream    a wrapper stream for swapping byte order and BOMs
41  *      SliceStream     a portion of another stream
42  *    MemoryStream    a stream entirely stored in main memory
43  *    TArrayStream    a stream wrapping an array-like buffer
44  */
45 
46 /// A base class for stream exceptions.
47 class StreamException: Exception {
48   /// Construct a StreamException with given error message.
49   this(string msg) { super(msg); }
50 }
51 
52 /// Thrown when unable to read data from Stream.
53 class ReadException: StreamException {
54   /// Construct a ReadException with given error message.
55   this(string msg) { super(msg); }
56 }
57 
58 /// Thrown when unable to write data to Stream.
59 class WriteException: StreamException {
60   /// Construct a WriteException with given error message.
61   this(string msg) { super(msg); }
62 }
63 
64 /// Thrown when unable to move Stream pointer.
65 class SeekException: StreamException {
66   /// Construct a SeekException with given error message.
67   this(string msg) { super(msg); }
68 }
69 
70 // seek whence...
71 enum SeekPos {
72   Set,
73   Current,
74   End
75 }
76 
77 private {
78   import std.conv;
79   import std.algorithm;
80   import std.ascii;
81   //import std.format;
82   import std.system;    // for Endian enumeration
83   import std.utf;
84   import undead.utf;
85   import core.bitop; // for bswap
86   import core.vararg;
87   static import std.file;
88   import undead.internal.file;
89   import undead.doformat;
90 }
91 
92 /// InputStream is the interface for readable streams.
93 
94 interface InputStream {
95 
96   /***
97    * Read exactly size bytes into the buffer.
98    *
99    * Throws a ReadException if it is not correct.
100    */
101   void readExact(void* buffer, size_t size);
102 
103   /***
104    * Read a block of data big enough to fill the given array buffer.
105    *
106    * Returns: the actual number of bytes read. Unfilled bytes are not modified.
107    */
108   size_t read(ubyte[] buffer);
109 
110   /***
111    * Read a basic type or counted string.
112    *
113    * Throw a ReadException if it could not be read.
114    * Outside of byte, ubyte, and char, the format is
115    * implementation-specific and should not be used except as opposite actions
116    * to write.
117    */
118   void read(out byte x);
119   void read(out ubyte x);       /// ditto
120   void read(out short x);       /// ditto
121   void read(out ushort x);      /// ditto
122   void read(out int x);         /// ditto
123   void read(out uint x);        /// ditto
124   void read(out long x);        /// ditto
125   void read(out ulong x);       /// ditto
126   void read(out float x);       /// ditto
127   void read(out double x);      /// ditto
128   void read(out real x);        /// ditto
129   void read(out char x);        /// ditto
130   void read(out wchar x);       /// ditto
131   void read(out dchar x);       /// ditto
132 
133   // reads a string, written earlier by write()
134   void read(out char[] s);      /// ditto
135 
136   // reads a Unicode string, written earlier by write()
137   void read(out wchar[] s);     /// ditto
138 
139   /***
140    * Read a line that is terminated with some combination of carriage return and
141    * line feed or end-of-file.
142    *
143    * The terminators are not included. The wchar version
144    * is identical. The optional buffer parameter is filled (reallocating
145    * it if necessary) and a slice of the result is returned.
146    */
147   char[] readLine();
148   char[] readLine(char[] result);       /// ditto
149   wchar[] readLineW();                  /// ditto
150   wchar[] readLineW(wchar[] result);    /// ditto
151 
152   /***
153    * Overload foreach statements to read the stream line by line and call the
154    * supplied delegate with each line or with each line with line number.
155    *
156    * The string passed in line may be reused between calls to the delegate.
157    * Line numbering starts at 1.
158    * Breaking out of the foreach will leave the stream
159    * position at the beginning of the next line to be read.
160    * For example, to echo a file line-by-line with line numbers run:
161    * ------------------------------------
162    * Stream file = new BufferedFile("sample.txt");
163    * foreach(ulong n, char[] line; file)
164    * {
165    *     writefln("line %d: %s", n, line);
166    * }
167    * file.close();
168    * ------------------------------------
169    */
170 
171   // iterate through the stream line-by-line
172   int opApply(scope int delegate(ref char[] line) dg);
173   int opApply(scope int delegate(ref ulong n, ref char[] line) dg);  /// ditto
174   int opApply(scope int delegate(ref wchar[] line) dg);            /// ditto
175   int opApply(scope int delegate(ref ulong n, ref wchar[] line) dg); /// ditto
176 
177   /// Read a string of the given length,
178   /// throwing ReadException if there was a problem.
179   char[] readString(size_t length);
180 
181   /***
182    * Read a string of the given length, throwing ReadException if there was a
183    * problem.
184    *
185    * The file format is implementation-specific and should not be used
186    * except as opposite actions to <b>write</b>.
187    */
188 
189   wchar[] readStringW(size_t length);
190 
191 
192   /***
193    * Read and return the next character in the stream.
194    *
195    * This is the only method that will handle ungetc properly.
196    * getcw's format is implementation-specific.
197    * If EOF is reached then getc returns char.init and getcw returns wchar.init.
198    */
199 
200   char getc();
201   wchar getcw(); /// ditto
202 
203   /***
204    * Push a character back onto the stream.
205    *
206    * They will be returned in first-in last-out order from getc/getcw.
207    * Only has effect on further calls to getc() and getcw().
208    */
209   char ungetc(char c);
210   wchar ungetcw(wchar c); /// ditto
211 
212   /***
213    * Scan a string from the input using a similar form to C's scanf
214    * and <a href="std_format.html">std.format</a>.
215    *
216    * An argument of type string is interpreted as a format string.
217    * All other arguments must be pointer types.
218    * If a format string is not present a default will be supplied computed from
219    * the base type of the pointer type. An argument of type string* is filled
220    * (possibly with appending characters) and a slice of the result is assigned
221    * back into the argument. For example the following readf statements
222    * are equivalent:
223    * --------------------------
224    * int x;
225    * double y;
226    * string s;
227    * file.readf(&x, " hello ", &y, &s);
228    * file.readf("%d hello %f %s", &x, &y, &s);
229    * file.readf("%d hello %f", &x, &y, "%s", &s);
230    * --------------------------
231    */
232   int vreadf(TypeInfo[] arguments, va_list args);
233   int readf(...); /// ditto
234 
235   /// Retrieve the number of bytes available for immediate reading.
236   @property size_t available();
237 
238   /***
239    * Return whether the current file position is the same as the end of the
240    * file.
241    *
242    * This does not require actually reading past the end, as with stdio. For
243    * non-seekable streams this might only return true after attempting to read
244    * past the end.
245    */
246 
247   @property bool eof();
248 
249   @property bool isOpen();        /// Return true if the stream is currently open.
250 }
251 
252 /// Interface for writable streams.
253 interface OutputStream {
254 
255   /***
256    * Write exactly size bytes from buffer, or throw a WriteException if that
257    * could not be done.
258    */
259   void writeExact(const void* buffer, size_t size);
260 
261   /***
262    * Write as much of the buffer as possible,
263    * returning the number of bytes written.
264    */
265   size_t write(const(ubyte)[] buffer);
266 
267   /***
268    * Write a basic type.
269    *
270    * Outside of byte, ubyte, and char, the format is implementation-specific
271    * and should only be used in conjunction with read.
272    * Throw WriteException on error.
273    */
274   void write(byte x);
275   void write(ubyte x);          /// ditto
276   void write(short x);          /// ditto
277   void write(ushort x);         /// ditto
278   void write(int x);            /// ditto
279   void write(uint x);           /// ditto
280   void write(long x);           /// ditto
281   void write(ulong x);          /// ditto
282   void write(float x);          /// ditto
283   void write(double x);         /// ditto
284   void write(real x);           /// ditto
285   void write(char x);           /// ditto
286   void write(wchar x);          /// ditto
287   void write(dchar x);          /// ditto
288 
289   /***
290    * Writes a string, together with its length.
291    *
292    * The format is implementation-specific
293    * and should only be used in conjunction with read.
294    * Throw WriteException on error.
295    */
296     void write(const(char)[] s);
297     void write(const(wchar)[] s); /// ditto
298 
299   /***
300    * Write a line of text,
301    * appending the line with an operating-system-specific line ending.
302    *
303    * Throws WriteException on error.
304    */
305   void writeLine(const(char)[] s);
306 
307   /***
308    * Write a line of text,
309    * appending the line with an operating-system-specific line ending.
310    *
311    * The format is implementation-specific.
312    * Throws WriteException on error.
313    */
314     void writeLineW(const(wchar)[] s);
315 
316   /***
317    * Write a string of text.
318    *
319    * Throws WriteException if it could not be fully written.
320    */
321     void writeString(const(char)[] s);
322 
323   /***
324    * Write a string of text.
325    *
326    * The format is implementation-specific.
327    * Throws WriteException if it could not be fully written.
328    */
329   void writeStringW(const(wchar)[] s);
330 
331   /***
332    * Print a formatted string into the stream using printf-style syntax,
333    * returning the number of bytes written.
334    */
335   size_t vprintf(const(char)[] format, va_list args);
336   size_t printf(const(char)[] format, ...);    /// ditto
337 
338   /***
339    * Print a formatted string into the stream using writef-style syntax.
340    * References: <a href="std_format.html">std.format</a>.
341    * Returns: self to chain with other stream commands like flush.
342    *
343    * NOTE: not supported in GDC, since it uses features unimplemented in that
344    * compiler.
345    */
346   OutputStream writef(...);
347   OutputStream writefln(...); /// ditto
348   OutputStream writefx(TypeInfo[] arguments, va_list argptr, int newline = false);  /// ditto
349 
350   void flush(); /// Flush pending output if appropriate.
351   void close(); /// Close the stream, flushing output if appropriate.
352   @property bool isOpen(); /// Return true if the stream is currently open.
353 }
354 
355 
356 /***
357  * Stream is the base abstract class from which the other stream classes derive.
358  *
359  * Stream's byte order is the format native to the computer.
360  *
361  * Reading:
362  * These methods require that the readable flag be set.
363  * Problems with reading result in a ReadException being thrown.
364  * Stream implements the InputStream interface in addition to the
365  * readBlock method.
366  *
367  * Writing:
368  * These methods require that the writeable flag be set. Problems with writing
369  * result in a WriteException being thrown. Stream implements the OutputStream
370  * interface in addition to the following methods:
371  * writeBlock
372  * copyFrom
373  * copyFrom
374  *
375  * Seeking:
376  * These methods require that the seekable flag be set.
377  * Problems with seeking result in a SeekException being thrown.
378  * seek, seekSet, seekCur, seekEnd, position, size, toString, toHash
379  */
380 
381 // not really abstract, but its instances will do nothing useful
382 class Stream : InputStream, OutputStream {
383   private import std.string, std.digest.crc, core.stdc.stdlib, core.stdc.stdio;
384 
385   // stream abilities
386   bool readable = false;        /// Indicates whether this stream can be read from.
387   bool writeable = false;       /// Indicates whether this stream can be written to.
388   bool seekable = false;        /// Indicates whether this stream can be sought within.
389   protected bool isopen = true; /// Indicates whether this stream is open.
390 
391   protected bool readEOF = false; /** Indicates whether this stream is at eof
392                                    * after the last read attempt.
393                                    */
394 
395   protected bool prevCr = false; /** For a non-seekable stream indicates that
396                                   * the last readLine or readLineW ended on a
397                                   * '\r' character.
398                                   */
399 
400   this() {}
401 
402   /***
403    * Read up to size bytes into the buffer and return the number of bytes
404    * actually read. A return value of 0 indicates end-of-file.
405    */
406   abstract size_t readBlock(void* buffer, size_t size);
407 
408   // reads block of data of specified size,
409   // throws ReadException on error
410   void readExact(void* buffer, size_t size) {
411     for(;;) {
412       if (!size) return;
413       size_t readsize = readBlock(buffer, size); // return 0 on eof
414       if (readsize == 0) break;
415       buffer += readsize;
416       size -= readsize;
417     }
418     if (size != 0)
419       throw new ReadException("not enough data in stream");
420   }
421 
422   // reads block of data big enough to fill the given
423   // array, returns actual number of bytes read
424   size_t read(ubyte[] buffer) {
425     return readBlock(buffer.ptr, buffer.length);
426   }
427 
428   // read a single value of desired type,
429   // throw ReadException on error
430   void read(out byte x) { readExact(&x, x.sizeof); }
431   void read(out ubyte x) { readExact(&x, x.sizeof); }
432   void read(out short x) { readExact(&x, x.sizeof); }
433   void read(out ushort x) { readExact(&x, x.sizeof); }
434   void read(out int x) { readExact(&x, x.sizeof); }
435   void read(out uint x) { readExact(&x, x.sizeof); }
436   void read(out long x) { readExact(&x, x.sizeof); }
437   void read(out ulong x) { readExact(&x, x.sizeof); }
438   void read(out float x) { readExact(&x, x.sizeof); }
439   void read(out double x) { readExact(&x, x.sizeof); }
440   void read(out real x) { readExact(&x, x.sizeof); }
441   void read(out char x) { readExact(&x, x.sizeof); }
442   void read(out wchar x) { readExact(&x, x.sizeof); }
443   void read(out dchar x) { readExact(&x, x.sizeof); }
444 
445   // reads a string, written earlier by write()
446   void read(out char[] s) {
447     size_t len;
448     read(len);
449     s = readString(len);
450   }
451 
452   // reads a Unicode string, written earlier by write()
453   void read(out wchar[] s) {
454     size_t len;
455     read(len);
456     s = readStringW(len);
457   }
458 
459   // reads a line, terminated by either CR, LF, CR/LF, or EOF
460   char[] readLine() {
461     return readLine(null);
462   }
463 
464   // reads a line, terminated by either CR, LF, CR/LF, or EOF
465   // reusing the memory in buffer if result will fit and otherwise
466   // allocates a new string
467   char[] readLine(char[] result) {
468     size_t strlen = 0;
469     char ch = getc();
470     while (readable) {
471       switch (ch) {
472       case '\r':
473         if (seekable) {
474           ch = getc();
475           if (ch != '\n')
476             ungetc(ch);
477         } else {
478           prevCr = true;
479         }
480         goto case;
481       case '\n':
482       case char.init:
483         result.length = strlen;
484         return result;
485 
486       default:
487         if (strlen < result.length) {
488           result[strlen] = ch;
489         } else {
490           result ~= ch;
491         }
492         strlen++;
493       }
494       ch = getc();
495     }
496     result.length = strlen;
497     return result;
498   }
499 
500   // reads a Unicode line, terminated by either CR, LF, CR/LF,
501   // or EOF; pretty much the same as the above, working with
502   // wchars rather than chars
503   wchar[] readLineW() {
504     return readLineW(null);
505   }
506 
507   // reads a Unicode line, terminated by either CR, LF, CR/LF,
508   // or EOF;
509   // fills supplied buffer if line fits and otherwise allocates a new string.
510   wchar[] readLineW(wchar[] result) {
511     size_t strlen = 0;
512     wchar c = getcw();
513     while (readable) {
514       switch (c) {
515       case '\r':
516         if (seekable) {
517           c = getcw();
518           if (c != '\n')
519             ungetcw(c);
520         } else {
521           prevCr = true;
522         }
523         goto case;
524       case '\n':
525       case wchar.init:
526         result.length = strlen;
527         return result;
528 
529       default:
530         if (strlen < result.length) {
531           result[strlen] = c;
532         } else {
533           result ~= c;
534         }
535         strlen++;
536       }
537       c = getcw();
538     }
539     result.length = strlen;
540     return result;
541   }
542 
543   // iterate through the stream line-by-line - due to Regan Heath
544   int opApply(scope int delegate(ref char[] line) dg) {
545     int res = 0;
546     char[128] buf;
547     while (!eof) {
548       char[] line = readLine(buf);
549       res = dg(line);
550       if (res) break;
551     }
552     return res;
553   }
554 
555   // iterate through the stream line-by-line with line count and string
556   int opApply(scope int delegate(ref ulong n, ref char[] line) dg) {
557     int res = 0;
558     ulong n = 1;
559     char[128] buf;
560     while (!eof) {
561       auto line = readLine(buf);
562       res = dg(n,line);
563       if (res) break;
564       n++;
565     }
566     return res;
567   }
568 
569   // iterate through the stream line-by-line with wchar[]
570   int opApply(scope int delegate(ref wchar[] line) dg) {
571     int res = 0;
572     wchar[128] buf;
573     while (!eof) {
574       auto line = readLineW(buf);
575       res = dg(line);
576       if (res) break;
577     }
578     return res;
579   }
580 
581   // iterate through the stream line-by-line with line count and wchar[]
582   int opApply(scope int delegate(ref ulong n, ref wchar[] line) dg) {
583     int res = 0;
584     ulong n = 1;
585     wchar[128] buf;
586     while (!eof) {
587       auto line = readLineW(buf);
588       res = dg(n,line);
589       if (res) break;
590       n++;
591     }
592     return res;
593   }
594 
595   // reads a string of given length, throws
596   // ReadException on error
597   char[] readString(size_t length) {
598     char[] result = new char[length];
599     readExact(result.ptr, length);
600     return result;
601   }
602 
603   // reads a Unicode string of given length, throws
604   // ReadException on error
605   wchar[] readStringW(size_t length) {
606     auto result = new wchar[length];
607     readExact(result.ptr, result.length * wchar.sizeof);
608     return result;
609   }
610 
611   // unget buffer
612   private wchar[] unget;
613   final bool ungetAvailable() { return unget.length > 1; }
614 
615   // reads and returns next character from the stream,
616   // handles characters pushed back by ungetc()
617   // returns char.init on eof.
618   char getc() {
619     char c;
620     if (prevCr) {
621       prevCr = false;
622       c = getc();
623       if (c != '\n')
624         return c;
625     }
626     if (unget.length > 1) {
627       c = cast(char)unget[unget.length - 1];
628       unget.length = unget.length - 1;
629     } else {
630       readBlock(&c,1);
631     }
632     return c;
633   }
634 
635   // reads and returns next Unicode character from the
636   // stream, handles characters pushed back by ungetc()
637   // returns wchar.init on eof.
638   wchar getcw() {
639     wchar c;
640     if (prevCr) {
641       prevCr = false;
642       c = getcw();
643       if (c != '\n')
644         return c;
645     }
646     if (unget.length > 1) {
647       c = unget[unget.length - 1];
648       unget.length = unget.length - 1;
649     } else {
650       void* buf = &c;
651       size_t n = readBlock(buf,2);
652       if (n == 1 && readBlock(buf+1,1) == 0)
653           throw new ReadException("not enough data in stream");
654     }
655     return c;
656   }
657 
658   // pushes back character c into the stream; only has
659   // effect on further calls to getc() and getcw()
660   char ungetc(char c) {
661     if (c == c.init) return c;
662     // first byte is a dummy so that we never set length to 0
663     if (unget.length == 0)
664       unget.length = 1;
665     unget ~= c;
666     return c;
667   }
668 
669   // pushes back Unicode character c into the stream; only
670   // has effect on further calls to getc() and getcw()
671   wchar ungetcw(wchar c) {
672     if (c == c.init) return c;
673     // first byte is a dummy so that we never set length to 0
674     if (unget.length == 0)
675       unget.length = 1;
676     unget ~= c;
677     return c;
678   }
679 
680   int vreadf(TypeInfo[] arguments, va_list args) {
681     string fmt;
682     int j = 0;
683     int count = 0, i = 0;
684     char c;
685     bool firstCharacter = true;
686     while ((j < arguments.length || i < fmt.length) && !eof) {
687       if(firstCharacter) {
688         c = getc();
689         firstCharacter = false;
690       }
691       if (fmt.length == 0 || i == fmt.length) {
692         i = 0;
693         if (arguments[j] is typeid(string) || arguments[j] is typeid(char[])
694             || arguments[j] is typeid(const(char)[])) {
695           fmt = va_arg!(string)(args);
696           j++;
697           continue;
698         } else if (arguments[j] is typeid(int*) ||
699                    arguments[j] is typeid(byte*) ||
700                    arguments[j] is typeid(short*) ||
701                    arguments[j] is typeid(long*)) {
702           fmt = "%d";
703         } else if (arguments[j] is typeid(uint*) ||
704                    arguments[j] is typeid(ubyte*) ||
705                    arguments[j] is typeid(ushort*) ||
706                    arguments[j] is typeid(ulong*)) {
707           fmt = "%d";
708         } else if (arguments[j] is typeid(float*) ||
709                    arguments[j] is typeid(double*) ||
710                    arguments[j] is typeid(real*)) {
711           fmt = "%f";
712         } else if (arguments[j] is typeid(char[]*) ||
713                    arguments[j] is typeid(wchar[]*) ||
714                    arguments[j] is typeid(dchar[]*)) {
715           fmt = "%s";
716         } else if (arguments[j] is typeid(char*)) {
717           fmt = "%c";
718         }
719       }
720       if (fmt[i] == '%') {      // a field
721         i++;
722         bool suppress = false;
723         if (fmt[i] == '*') {    // suppress assignment
724           suppress = true;
725           i++;
726         }
727         // read field width
728         int width = 0;
729         while (isDigit(fmt[i])) {
730           width = width * 10 + (fmt[i] - '0');
731           i++;
732         }
733         if (width == 0)
734           width = -1;
735         // skip any modifier if present
736         if (fmt[i] == 'h' || fmt[i] == 'l' || fmt[i] == 'L')
737           i++;
738         // check the typechar and act accordingly
739         switch (fmt[i]) {
740         case 'd':       // decimal/hexadecimal/octal integer
741         case 'D':
742         case 'u':
743         case 'U':
744         case 'o':
745         case 'O':
746         case 'x':
747         case 'X':
748         case 'i':
749         case 'I':
750           {
751             while (isWhite(c)) {
752               c = getc();
753               count++;
754             }
755             bool neg = false;
756             if (c == '-') {
757               neg = true;
758               c = getc();
759               count++;
760             } else if (c == '+') {
761               c = getc();
762               count++;
763             }
764             char ifmt = cast(char)(fmt[i] | 0x20);
765             if (ifmt == 'i')    { // undetermined base
766               if (c == '0')     { // octal or hex
767                 c = getc();
768                 count++;
769                 if (c == 'x' || c == 'X')       { // hex
770                   ifmt = 'x';
771                   c = getc();
772                   count++;
773                 } else {        // octal
774                   ifmt = 'o';
775                 }
776               }
777               else      // decimal
778                 ifmt = 'd';
779             }
780             long n = 0;
781             switch (ifmt)
782             {
783                 case 'd':       // decimal
784                 case 'u': {
785                   while (isDigit(c) && width) {
786                     n = n * 10 + (c - '0');
787                     width--;
788                     c = getc();
789                     count++;
790                   }
791                 } break;
792 
793                 case 'o': {     // octal
794                   while (isOctalDigit(c) && width) {
795                     n = n * 8 + (c - '0');
796                     width--;
797                     c = getc();
798                     count++;
799                   }
800                 } break;
801 
802                 case 'x': {     // hexadecimal
803                   while (isHexDigit(c) && width) {
804                     n *= 0x10;
805                     if (isDigit(c))
806                       n += c - '0';
807                     else
808                       n += 0xA + (c | 0x20) - 'a';
809                     width--;
810                     c = getc();
811                     count++;
812                   }
813                 } break;
814 
815                 default:
816                     assert(0);
817             }
818             if (neg)
819               n = -n;
820             if (arguments[j] is typeid(int*)) {
821               int* p = va_arg!(int*)(args);
822               *p = cast(int)n;
823             } else if (arguments[j] is typeid(short*)) {
824               short* p = va_arg!(short*)(args);
825               *p = cast(short)n;
826             } else if (arguments[j] is typeid(byte*)) {
827               byte* p = va_arg!(byte*)(args);
828               *p = cast(byte)n;
829             } else if (arguments[j] is typeid(long*)) {
830               long* p = va_arg!(long*)(args);
831               *p = n;
832             } else if (arguments[j] is typeid(uint*)) {
833               uint* p = va_arg!(uint*)(args);
834               *p = cast(uint)n;
835             } else if (arguments[j] is typeid(ushort*)) {
836               ushort* p = va_arg!(ushort*)(args);
837               *p = cast(ushort)n;
838             } else if (arguments[j] is typeid(ubyte*)) {
839               ubyte* p = va_arg!(ubyte*)(args);
840               *p = cast(ubyte)n;
841             } else if (arguments[j] is typeid(ulong*)) {
842               ulong* p = va_arg!(ulong*)(args);
843               *p = cast(ulong)n;
844             }
845             j++;
846             i++;
847           } break;
848 
849         case 'f':       // float
850         case 'F':
851         case 'e':
852         case 'E':
853         case 'g':
854         case 'G':
855           {
856             while (isWhite(c)) {
857               c = getc();
858               count++;
859             }
860             bool neg = false;
861             if (c == '-') {
862               neg = true;
863               c = getc();
864               count++;
865             } else if (c == '+') {
866               c = getc();
867               count++;
868             }
869             real r = 0;
870             while (isDigit(c) && width) {
871               r = r * 10 + (c - '0');
872               width--;
873               c = getc();
874               count++;
875             }
876             if (width && c == '.') {
877               width--;
878               c = getc();
879               count++;
880               double frac = 1;
881               while (isDigit(c) && width) {
882                 r = r * 10 + (c - '0');
883                 frac *= 10;
884                 width--;
885                 c = getc();
886                 count++;
887               }
888               r /= frac;
889             }
890             if (width && (c == 'e' || c == 'E')) {
891               width--;
892               c = getc();
893               count++;
894               if (width) {
895                 bool expneg = false;
896                 if (c == '-') {
897                   expneg = true;
898                   width--;
899                   c = getc();
900                   count++;
901                 } else if (c == '+') {
902                   width--;
903                   c = getc();
904                   count++;
905                 }
906                 real exp = 0;
907                 while (isDigit(c) && width) {
908                   exp = exp * 10 + (c - '0');
909                   width--;
910                   c = getc();
911                   count++;
912                 }
913                 if (expneg) {
914                   while (exp--)
915                     r /= 10;
916                 } else {
917                   while (exp--)
918                     r *= 10;
919                 }
920               }
921             }
922             if(width && (c == 'n' || c == 'N')) {
923               width--;
924               c = getc();
925               count++;
926               if(width && (c == 'a' || c == 'A')) {
927                 width--;
928                 c = getc();
929                 count++;
930                 if(width && (c == 'n' || c == 'N')) {
931                   width--;
932                   c = getc();
933                   count++;
934                   r = real.nan;
935                 }
936               }
937             }
938             if(width && (c == 'i' || c == 'I')) {
939               width--;
940               c = getc();
941               count++;
942               if(width && (c == 'n' || c == 'N')) {
943                 width--;
944                 c = getc();
945                 count++;
946                 if(width && (c == 'f' || c == 'F')) {
947                   width--;
948                   c = getc();
949                   count++;
950                   r = real.infinity;
951                 }
952               }
953             }
954             if (neg)
955               r = -r;
956             if (arguments[j] is typeid(float*)) {
957               float* p = va_arg!(float*)(args);
958               *p = r;
959             } else if (arguments[j] is typeid(double*)) {
960               double* p = va_arg!(double*)(args);
961               *p = r;
962             } else if (arguments[j] is typeid(real*)) {
963               real* p = va_arg!(real*)(args);
964               *p = r;
965             }
966             j++;
967             i++;
968           } break;
969 
970         case 's': {     // string
971           while (isWhite(c)) {
972             c = getc();
973             count++;
974           }
975           char[] s;
976           char[]* p;
977           size_t strlen;
978           if (arguments[j] is typeid(char[]*)) {
979             p = va_arg!(char[]*)(args);
980             s = *p;
981           }
982           while (!isWhite(c) && c != char.init) {
983             if (strlen < s.length) {
984               s[strlen] = c;
985             } else {
986               s ~= c;
987             }
988             strlen++;
989             c = getc();
990             count++;
991           }
992           s = s[0 .. strlen];
993           if (arguments[j] is typeid(char[]*)) {
994             *p = s;
995           } else if (arguments[j] is typeid(char*)) {
996             s ~= 0;
997             auto q = va_arg!(char*)(args);
998             q[0 .. s.length] = s[];
999           } else if (arguments[j] is typeid(wchar[]*)) {
1000             auto q = va_arg!(const(wchar)[]*)(args);
1001             *q = toUTF16(s);
1002           } else if (arguments[j] is typeid(dchar[]*)) {
1003             auto q = va_arg!(const(dchar)[]*)(args);
1004             *q = toUTF32(s);
1005           }
1006           j++;
1007           i++;
1008         } break;
1009 
1010         case 'c': {     // character(s)
1011           char* s = va_arg!(char*)(args);
1012           if (width < 0)
1013             width = 1;
1014           else
1015             while (isWhite(c)) {
1016             c = getc();
1017             count++;
1018           }
1019           while (width-- && !eof) {
1020             *(s++) = c;
1021             c = getc();
1022             count++;
1023           }
1024           j++;
1025           i++;
1026         } break;
1027 
1028         case 'n': {     // number of chars read so far
1029           int* p = va_arg!(int*)(args);
1030           *p = count;
1031           j++;
1032           i++;
1033         } break;
1034 
1035         default:        // read character as is
1036           goto nws;
1037         }
1038       } else if (isWhite(fmt[i])) {     // skip whitespace
1039         while (isWhite(c))
1040           c = getc();
1041         i++;
1042       } else {  // read character as is
1043       nws:
1044         if (fmt[i] != c)
1045           break;
1046         c = getc();
1047         i++;
1048       }
1049     }
1050     ungetc(c);
1051     return count;
1052   }
1053 
1054   int readf(...) {
1055     return vreadf(_arguments, _argptr);
1056   }
1057 
1058   // returns estimated number of bytes available for immediate reading
1059   @property size_t available() { return 0; }
1060 
1061   /***
1062    * Write up to size bytes from buffer in the stream, returning the actual
1063    * number of bytes that were written.
1064    */
1065   abstract size_t writeBlock(const void* buffer, size_t size);
1066 
1067   // writes block of data of specified size,
1068   // throws WriteException on error
1069   void writeExact(const void* buffer, size_t size) {
1070     const(void)* p = buffer;
1071     for(;;) {
1072       if (!size) return;
1073       size_t writesize = writeBlock(p, size);
1074       if (writesize == 0) break;
1075       p += writesize;
1076       size -= writesize;
1077     }
1078     if (size != 0)
1079       throw new WriteException("unable to write to stream");
1080   }
1081 
1082   // writes the given array of bytes, returns
1083   // actual number of bytes written
1084   size_t write(const(ubyte)[] buffer) {
1085     return writeBlock(buffer.ptr, buffer.length);
1086   }
1087 
1088   // write a single value of desired type,
1089   // throw WriteException on error
1090   void write(byte x) { writeExact(&x, x.sizeof); }
1091   void write(ubyte x) { writeExact(&x, x.sizeof); }
1092   void write(short x) { writeExact(&x, x.sizeof); }
1093   void write(ushort x) { writeExact(&x, x.sizeof); }
1094   void write(int x) { writeExact(&x, x.sizeof); }
1095   void write(uint x) { writeExact(&x, x.sizeof); }
1096   void write(long x) { writeExact(&x, x.sizeof); }
1097   void write(ulong x) { writeExact(&x, x.sizeof); }
1098   void write(float x) { writeExact(&x, x.sizeof); }
1099   void write(double x) { writeExact(&x, x.sizeof); }
1100   void write(real x) { writeExact(&x, x.sizeof); }
1101   void write(char x) { writeExact(&x, x.sizeof); }
1102   void write(wchar x) { writeExact(&x, x.sizeof); }
1103   void write(dchar x) { writeExact(&x, x.sizeof); }
1104 
1105   // writes a string, together with its length
1106   void write(const(char)[] s) {
1107     write(s.length);
1108     writeString(s);
1109   }
1110 
1111   // writes a Unicode string, together with its length
1112   void write(const(wchar)[] s) {
1113     write(s.length);
1114     writeStringW(s);
1115   }
1116 
1117   // writes a line, throws WriteException on error
1118   void writeLine(const(char)[] s) {
1119     writeString(s);
1120     version (Windows)
1121       writeString("\r\n");
1122     else version (Mac)
1123       writeString("\r");
1124     else
1125       writeString("\n");
1126   }
1127 
1128   // writes a Unicode line, throws WriteException on error
1129   void writeLineW(const(wchar)[] s) {
1130     writeStringW(s);
1131     version (Windows)
1132       writeStringW("\r\n");
1133     else version (Mac)
1134       writeStringW("\r");
1135     else
1136       writeStringW("\n");
1137   }
1138 
1139   // writes a string, throws WriteException on error
1140   void writeString(const(char)[] s) {
1141     writeExact(s.ptr, s.length);
1142   }
1143 
1144   // writes a Unicode string, throws WriteException on error
1145   void writeStringW(const(wchar)[] s) {
1146     writeExact(s.ptr, s.length * wchar.sizeof);
1147   }
1148 
1149   // writes data to stream using vprintf() syntax,
1150   // returns number of bytes written
1151   size_t vprintf(const(char)[] format, va_list args) {
1152     // shamelessly stolen from OutBuffer,
1153     // by Walter's permission
1154     char[1024] buffer;
1155     char* p = buffer.ptr;
1156     // Can't use `tempCString()` here as it will result in compilation error:
1157     // "cannot mix core.std.stdlib.alloca() and exception handling".
1158     auto f = toStringz(format);
1159     size_t psize = buffer.length;
1160     size_t count;
1161     while (true) {
1162       version (Windows) {
1163         count = vsnprintf(p, psize, f, args);
1164         if (count != -1)
1165           break;
1166         psize *= 2;
1167         p = cast(char*) alloca(psize);
1168       } else version (Posix) {
1169         count = vsnprintf(p, psize, f, args);
1170         if (count == -1)
1171           psize *= 2;
1172         else if (count >= psize)
1173           psize = count + 1;
1174         else
1175           break;
1176         p = cast(char*) alloca(psize);
1177       } else
1178           throw new Exception("unsupported platform");
1179     }
1180     writeString(p[0 .. count]);
1181     return count;
1182   }
1183 
1184   // writes data to stream using printf() syntax,
1185   // returns number of bytes written
1186   size_t printf(const(char)[] format, ...) {
1187     va_list ap;
1188     va_start(ap, format);
1189     auto result = vprintf(format, ap);
1190     va_end(ap);
1191     return result;
1192   }
1193 
1194   private void doFormatCallback(dchar c) {
1195     char[4] buf;
1196     auto b = undead.utf.toUTF8(buf, c);
1197     writeString(b);
1198   }
1199 
1200   // writes data to stream using writef() syntax,
1201   OutputStream writef(...) {
1202     return writefx(_arguments,_argptr,0);
1203   }
1204 
1205   // writes data with trailing newline
1206   OutputStream writefln(...) {
1207     return writefx(_arguments,_argptr,1);
1208   }
1209 
1210   // writes data with optional trailing newline
1211   OutputStream writefx(TypeInfo[] arguments, va_list argptr, int newline=false) {
1212     version (GNU)
1213     {
1214       assert(false, "GNU D compiler does not support doFormat");
1215     }
1216     else
1217     {
1218       doFormat(&doFormatCallback,arguments,argptr);
1219       if (newline)
1220         writeLine("");
1221       return this;
1222     }
1223   }
1224 
1225   /***
1226    * Copies all data from s into this stream.
1227    * This may throw ReadException or WriteException on failure.
1228    * This restores the file position of s so that it is unchanged.
1229    */
1230   void copyFrom(Stream s) {
1231     if (seekable) {
1232       ulong pos = s.position;
1233       s.position = 0;
1234       copyFrom(s, s.size);
1235       s.position = pos;
1236     } else {
1237       ubyte[128] buf;
1238       while (!s.eof) {
1239         size_t m = s.readBlock(buf.ptr, buf.length);
1240         writeExact(buf.ptr, m);
1241       }
1242     }
1243   }
1244 
1245   /***
1246    * Copy a specified number of bytes from the given stream into this one.
1247    * This may throw ReadException or WriteException on failure.
1248    * Unlike the previous form, this doesn't restore the file position of s.
1249    */
1250   void copyFrom(Stream s, ulong count) {
1251     ubyte[128] buf;
1252     while (count > 0) {
1253       size_t n = cast(size_t)(count<buf.length ? count : buf.length);
1254       s.readExact(buf.ptr, n);
1255       writeExact(buf.ptr, n);
1256       count -= n;
1257     }
1258   }
1259 
1260   /***
1261    * Change the current position of the stream. whence is either SeekPos.Set, in
1262    which case the offset is an absolute index from the beginning of the stream,
1263    SeekPos.Current, in which case the offset is a delta from the current
1264    position, or SeekPos.End, in which case the offset is a delta from the end of
1265    the stream (negative or zero offsets only make sense in that case). This
1266    returns the new file position.
1267    */
1268   abstract ulong seek(long offset, SeekPos whence);
1269 
1270   /***
1271    * Aliases for their normal seek counterparts.
1272    */
1273   ulong seekSet(long offset) { return seek (offset, SeekPos.Set); }
1274   ulong seekCur(long offset) { return seek (offset, SeekPos.Current); } /// ditto
1275   ulong seekEnd(long offset) { return seek (offset, SeekPos.End); }     /// ditto
1276 
1277   /***
1278    * Sets file position. Equivalent to calling seek(pos, SeekPos.Set).
1279    */
1280   @property void position(ulong pos) { seek(cast(long)pos, SeekPos.Set); }
1281 
1282   /***
1283    * Returns current file position. Equivalent to seek(0, SeekPos.Current).
1284    */
1285   @property ulong position() { return seek(0, SeekPos.Current); }
1286 
1287   /***
1288    * Retrieve the size of the stream in bytes.
1289    * The stream must be seekable or a SeekException is thrown.
1290    */
1291   @property ulong size() {
1292     assertSeekable();
1293     ulong pos = position, result = seek(0, SeekPos.End);
1294     position = pos;
1295     return result;
1296   }
1297 
1298   // returns true if end of stream is reached, false otherwise
1299   @property bool eof() {
1300     // for unseekable streams we only know the end when we read it
1301     if (readEOF && !ungetAvailable())
1302       return true;
1303     else if (seekable)
1304       return position == size;
1305     else
1306       return false;
1307   }
1308 
1309   // returns true if the stream is open
1310   @property bool isOpen() { return isopen; }
1311 
1312   // flush the buffer if writeable
1313   void flush() {
1314     if (unget.length > 1)
1315       unget.length = 1; // keep at least 1 so that data ptr stays
1316   }
1317 
1318   // close the stream somehow; the default just flushes the buffer
1319   void close() {
1320     if (isopen)
1321       flush();
1322     readEOF = prevCr = isopen = readable = writeable = seekable = false;
1323   }
1324 
1325   /***
1326    * Read the entire stream and return it as a string.
1327    * If the stream is not seekable the contents from the current position to eof
1328    * is read and returned.
1329    */
1330   override string toString() {
1331     if (!readable)
1332       return super.toString();
1333     try
1334     {
1335         size_t pos;
1336         size_t rdlen;
1337         size_t blockSize;
1338         char[] result;
1339         if (seekable) {
1340           ulong orig_pos = position;
1341           scope(exit) position = orig_pos;
1342           position = 0;
1343           blockSize = cast(size_t)size;
1344           result = new char[blockSize];
1345           while (blockSize > 0) {
1346             rdlen = readBlock(&result[pos], blockSize);
1347             pos += rdlen;
1348             blockSize -= rdlen;
1349           }
1350         } else {
1351           blockSize = 4096;
1352           result = new char[blockSize];
1353           while ((rdlen = readBlock(&result[pos], blockSize)) > 0) {
1354             pos += rdlen;
1355             blockSize += rdlen;
1356             result.length = result.length + blockSize;
1357           }
1358         }
1359         return cast(string) result[0 .. pos];
1360     }
1361     catch (Throwable)
1362     {
1363         return super.toString();
1364     }
1365   }
1366 
1367   /***
1368    * Get a hash of the stream by reading each byte and using it in a CRC-32
1369    * checksum.
1370    */
1371   override size_t toHash() @trusted {
1372     if (!readable || !seekable)
1373       return super.toHash();
1374     try
1375     {
1376         ulong pos = position;
1377         scope(exit) position = pos;
1378         CRC32 crc;
1379         crc.start();
1380         position = 0;
1381         ulong len = size;
1382         for (ulong i = 0; i < len; i++)
1383         {
1384           ubyte c;
1385           read(c);
1386           crc.put(c);
1387         }
1388 
1389         union resUnion
1390         {
1391             size_t hash;
1392             ubyte[4] crcVal;
1393         }
1394         resUnion res;
1395         res.crcVal = crc.finish();
1396         return res.hash;
1397     }
1398     catch (Throwable)
1399     {
1400         return super.toHash();
1401     }
1402   }
1403 
1404   // helper for checking that the stream is readable
1405   final protected void assertReadable() {
1406     if (!readable)
1407       throw new ReadException("Stream is not readable");
1408   }
1409   // helper for checking that the stream is writeable
1410   final protected void assertWriteable() {
1411     if (!writeable)
1412       throw new WriteException("Stream is not writeable");
1413   }
1414   // helper for checking that the stream is seekable
1415   final protected void assertSeekable() {
1416     if (!seekable)
1417       throw new SeekException("Stream is not seekable");
1418   }
1419 
1420   unittest { // unit test for Issue 3363
1421     import std.stdio;
1422     immutable fileName = undead.internal.file.deleteme ~ "-issue3363.txt";
1423     auto w = std.stdio.File(fileName, "w");
1424     scope (exit) std.file.remove(fileName);
1425     w.write("one two three");
1426     w.close();
1427     auto r = std.stdio.File(fileName, "r");
1428     const(char)[] constChar;
1429     string str;
1430     char[] chars;
1431     r.readf("%s %s %s", &constChar, &str, &chars);
1432     assert (constChar == "one", constChar);
1433     assert (str == "two", str);
1434     assert (chars == "three", chars);
1435   }
1436 
1437   version (GNU) {} else
1438   {
1439     unittest { //unit tests for Issue 1668
1440       void tryFloatRoundtrip(float x, string fmt = "", string pad = "") {
1441         auto s = new MemoryStream();
1442         s.writef(fmt, x, pad);
1443         s.position = 0;
1444 
1445         float f;
1446         assert(s.readf(&f));
1447         assert(x == f || (x != x && f != f)); //either equal or both NaN
1448       }
1449 
1450       tryFloatRoundtrip(1.0);
1451       tryFloatRoundtrip(1.0, "%f");
1452       tryFloatRoundtrip(1.0, "", " ");
1453       tryFloatRoundtrip(1.0, "%f", " ");
1454 
1455       tryFloatRoundtrip(3.14);
1456       tryFloatRoundtrip(3.14, "%f");
1457       tryFloatRoundtrip(3.14, "", " ");
1458       tryFloatRoundtrip(3.14, "%f", " ");
1459 
1460       float nan = float.nan;
1461       tryFloatRoundtrip(nan);
1462       tryFloatRoundtrip(nan, "%f");
1463       tryFloatRoundtrip(nan, "", " ");
1464       tryFloatRoundtrip(nan, "%f", " ");
1465 
1466       float inf = 1.0/0.0;
1467       tryFloatRoundtrip(inf);
1468       tryFloatRoundtrip(inf, "%f");
1469       tryFloatRoundtrip(inf, "", " ");
1470       tryFloatRoundtrip(inf, "%f", " ");
1471 
1472       tryFloatRoundtrip(-inf);
1473       tryFloatRoundtrip(-inf,"%f");
1474       tryFloatRoundtrip(-inf, "", " ");
1475       tryFloatRoundtrip(-inf, "%f", " ");
1476     }
1477   }
1478 }
1479 
1480 /***
1481  * A base class for streams that wrap a source stream with additional
1482  * functionality.
1483  *
1484  * The method implementations forward read/write/seek calls to the
1485  * source stream. A FilterStream can change the position of the source stream
1486  * arbitrarily and may not keep the source stream state in sync with the
1487  * FilterStream, even upon flushing and closing the FilterStream. It is
1488  * recommended to not make any assumptions about the state of the source position
1489  * and read/write state after a FilterStream has acted upon it. Specifc subclasses
1490  * of FilterStream should document how they modify the source stream and if any
1491  * invariants hold true between the source and filter.
1492  */
1493 class FilterStream : Stream {
1494   private Stream s;              // source stream
1495 
1496   /// Property indicating when this stream closes to close the source stream as
1497   /// well.
1498   /// Defaults to true.
1499   bool nestClose = true;
1500 
1501   /// Construct a FilterStream for the given source.
1502   this(Stream source) {
1503     s = source;
1504     resetSource();
1505   }
1506 
1507   // source getter/setter
1508 
1509   /***
1510    * Get the current source stream.
1511    */
1512   final Stream source(){return s;}
1513 
1514   /***
1515    * Set the current source stream.
1516    *
1517    * Setting the source stream closes this stream before attaching the new
1518    * source. Attaching an open stream reopens this stream and resets the stream
1519    * state.
1520    */
1521   void source(Stream s) {
1522     close();
1523     this.s = s;
1524     resetSource();
1525   }
1526 
1527   /***
1528    * Indicates the source stream changed state and that this stream should reset
1529    * any readable, writeable, seekable, isopen and buffering flags.
1530    */
1531   void resetSource() {
1532     if (s !is null) {
1533       readable = s.readable;
1534       writeable = s.writeable;
1535       seekable = s.seekable;
1536       isopen = s.isOpen;
1537     } else {
1538       readable = writeable = seekable = false;
1539       isopen = false;
1540     }
1541     readEOF = prevCr = false;
1542   }
1543 
1544   // read from source
1545   override size_t readBlock(void* buffer, size_t size) {
1546     size_t res = s.readBlock(buffer,size);
1547     readEOF = res == 0;
1548     return res;
1549   }
1550 
1551   // write to source
1552   override size_t writeBlock(const void* buffer, size_t size) {
1553     return s.writeBlock(buffer,size);
1554   }
1555 
1556   // close stream
1557   override void close() {
1558     if (isopen) {
1559       super.close();
1560       if (nestClose)
1561         s.close();
1562     }
1563   }
1564 
1565   // seek on source
1566   override ulong seek(long offset, SeekPos whence) {
1567     readEOF = false;
1568     return s.seek(offset,whence);
1569   }
1570 
1571   override @property size_t available() { return s.available; }
1572   override void flush() { super.flush(); s.flush(); }
1573 }
1574 
1575 /***
1576  * This subclass is for buffering a source stream.
1577  *
1578  * A buffered stream must be
1579  * closed explicitly to ensure the final buffer content is written to the source
1580  * stream. The source stream position is changed according to the block size so
1581  * reading or writing to the BufferedStream may not change the source stream
1582  * position by the same amount.
1583  */
1584 class BufferedStream : FilterStream {
1585   ubyte[] buffer;       // buffer, if any
1586   size_t bufferCurPos;    // current position in buffer
1587   size_t bufferLen;       // amount of data in buffer
1588   bool bufferDirty = false;
1589   size_t bufferSourcePos; // position in buffer of source stream position
1590   ulong streamPos;      // absolute position in source stream
1591 
1592   /* Example of relationship between fields:
1593    *
1594    *  s             ...01234567890123456789012EOF
1595    *  buffer                |--                     --|
1596    *  bufferCurPos                       |
1597    *  bufferLen             |--            --|
1598    *  bufferSourcePos                        |
1599    *
1600    */
1601 
1602   invariant() {
1603     assert(bufferSourcePos <= bufferLen);
1604     assert(bufferCurPos <= bufferLen);
1605     assert(bufferLen <= buffer.length);
1606   }
1607 
1608   enum size_t DefaultBufferSize = 8192;
1609 
1610   /***
1611    * Create a buffered stream for the stream source with the buffer size
1612    * bufferSize.
1613    */
1614   this(Stream source, size_t bufferSize = DefaultBufferSize) {
1615     super(source);
1616     if (bufferSize)
1617       buffer = new ubyte[bufferSize];
1618   }
1619 
1620   override protected void resetSource() {
1621     super.resetSource();
1622     streamPos = 0;
1623     bufferLen = bufferSourcePos = bufferCurPos = 0;
1624     bufferDirty = false;
1625   }
1626 
1627   // reads block of data of specified size using any buffered data
1628   // returns actual number of bytes read
1629   override size_t readBlock(void* result, size_t len) {
1630     if (len == 0) return 0;
1631 
1632     assertReadable();
1633 
1634     ubyte* outbuf = cast(ubyte*)result;
1635     size_t readsize = 0;
1636 
1637     if (bufferCurPos + len < bufferLen) {
1638       // buffer has all the data so copy it
1639       outbuf[0 .. len] = buffer[bufferCurPos .. bufferCurPos+len];
1640       bufferCurPos += len;
1641       readsize = len;
1642       goto ExitRead;
1643     }
1644 
1645     readsize = bufferLen - bufferCurPos;
1646     if (readsize > 0) {
1647       // buffer has some data so copy what is left
1648       outbuf[0 .. readsize] = buffer[bufferCurPos .. bufferLen];
1649       outbuf += readsize;
1650       bufferCurPos += readsize;
1651       len -= readsize;
1652     }
1653 
1654     flush();
1655 
1656     if (len >= buffer.length) {
1657       // buffer can't hold the data so fill output buffer directly
1658       size_t siz = super.readBlock(outbuf, len);
1659       readsize += siz;
1660       streamPos += siz;
1661     } else {
1662       // read a new block into buffer
1663         bufferLen = super.readBlock(buffer.ptr, buffer.length);
1664         if (bufferLen < len) len = bufferLen;
1665         outbuf[0 .. len] = buffer[0 .. len];
1666         bufferSourcePos = bufferLen;
1667         streamPos += bufferLen;
1668         bufferCurPos = len;
1669         readsize += len;
1670     }
1671 
1672   ExitRead:
1673     return readsize;
1674   }
1675 
1676   // write block of data of specified size
1677   // returns actual number of bytes written
1678   override size_t writeBlock(const void* result, size_t len) {
1679     assertWriteable();
1680 
1681     ubyte* buf = cast(ubyte*)result;
1682     size_t writesize = 0;
1683 
1684     if (bufferLen == 0) {
1685       // buffer is empty so fill it if possible
1686       if ((len < buffer.length) && (readable)) {
1687         // read in data if the buffer is currently empty
1688         bufferLen = s.readBlock(buffer.ptr, buffer.length);
1689         bufferSourcePos = bufferLen;
1690         streamPos += bufferLen;
1691 
1692       } else if (len >= buffer.length) {
1693         // buffer can't hold the data so write it directly and exit
1694         writesize = s.writeBlock(buf,len);
1695         streamPos += writesize;
1696         goto ExitWrite;
1697       }
1698     }
1699 
1700     if (bufferCurPos + len <= buffer.length) {
1701       // buffer has space for all the data so copy it and exit
1702       buffer[bufferCurPos .. bufferCurPos+len] = buf[0 .. len];
1703       bufferCurPos += len;
1704       bufferLen = bufferCurPos > bufferLen ? bufferCurPos : bufferLen;
1705       writesize = len;
1706       bufferDirty = true;
1707       goto ExitWrite;
1708     }
1709 
1710     writesize = buffer.length - bufferCurPos;
1711     if (writesize > 0) {
1712       // buffer can take some data
1713       buffer[bufferCurPos .. buffer.length] = buf[0 .. writesize];
1714       bufferCurPos = bufferLen = buffer.length;
1715       buf += writesize;
1716       len -= writesize;
1717       bufferDirty = true;
1718     }
1719 
1720     assert(bufferCurPos == buffer.length);
1721     assert(bufferLen == buffer.length);
1722 
1723     flush();
1724 
1725     writesize += writeBlock(buf,len);
1726 
1727   ExitWrite:
1728     return writesize;
1729   }
1730 
1731   override ulong seek(long offset, SeekPos whence) {
1732     assertSeekable();
1733 
1734     if ((whence != SeekPos.Current) ||
1735         (offset + bufferCurPos < 0) ||
1736         (offset + bufferCurPos >= bufferLen)) {
1737       flush();
1738       streamPos = s.seek(offset,whence);
1739     } else {
1740       bufferCurPos += offset;
1741     }
1742     readEOF = false;
1743     return streamPos-bufferSourcePos+bufferCurPos;
1744   }
1745 
1746   // Buffered readLine - Dave Fladebo
1747   // reads a line, terminated by either CR, LF, CR/LF, or EOF
1748   // reusing the memory in buffer if result will fit, otherwise
1749   // will reallocate (using concatenation)
1750   template TreadLine(T) {
1751       T[] readLine(T[] inBuffer)
1752       {
1753           size_t    lineSize = 0;
1754           bool    haveCR = false;
1755           T       c = '\0';
1756           size_t    idx = 0;
1757           ubyte*  pc = cast(ubyte*)&c;
1758 
1759         L0:
1760           for(;;) {
1761               size_t start = bufferCurPos;
1762             L1:
1763               foreach(ubyte b; buffer[start .. bufferLen]) {
1764                   bufferCurPos++;
1765                   pc[idx] = b;
1766                   if(idx < T.sizeof - 1) {
1767                       idx++;
1768                       continue L1;
1769                   } else {
1770                       idx = 0;
1771                   }
1772                   if(c == '\n' || haveCR) {
1773                       if(haveCR && c != '\n') bufferCurPos--;
1774                       break L0;
1775                   } else {
1776                       if(c == '\r') {
1777                           haveCR = true;
1778                       } else {
1779                           if(lineSize < inBuffer.length) {
1780                               inBuffer[lineSize] = c;
1781                           } else {
1782                               inBuffer ~= c;
1783                           }
1784                           lineSize++;
1785                       }
1786                   }
1787               }
1788               flush();
1789               size_t res = super.readBlock(buffer.ptr, buffer.length);
1790               if(!res) break L0; // EOF
1791               bufferSourcePos = bufferLen = res;
1792               streamPos += res;
1793           }
1794           return inBuffer[0 .. lineSize];
1795       }
1796   } // template TreadLine(T)
1797 
1798   override char[] readLine(char[] inBuffer) {
1799     if (ungetAvailable())
1800       return super.readLine(inBuffer);
1801     else
1802       return TreadLine!(char).readLine(inBuffer);
1803   }
1804   alias readLine = Stream.readLine;
1805 
1806   override wchar[] readLineW(wchar[] inBuffer) {
1807     if (ungetAvailable())
1808       return super.readLineW(inBuffer);
1809     else
1810       return TreadLine!(wchar).readLine(inBuffer);
1811   }
1812   alias readLineW = Stream.readLineW;
1813 
1814   override void flush()
1815   out {
1816     assert(bufferCurPos == 0);
1817     assert(bufferSourcePos == 0);
1818     assert(bufferLen == 0);
1819   }
1820   do {
1821     if (writeable && bufferDirty) {
1822       if (bufferSourcePos != 0 && seekable) {
1823         // move actual file pointer to front of buffer
1824         streamPos = s.seek(-bufferSourcePos, SeekPos.Current);
1825       }
1826       // write buffer out
1827       bufferSourcePos = s.writeBlock(buffer.ptr, bufferLen);
1828       if (bufferSourcePos != bufferLen) {
1829         throw new WriteException("Unable to write to stream");
1830       }
1831     }
1832     super.flush();
1833     long diff = cast(long)bufferCurPos-bufferSourcePos;
1834     if (diff != 0 && seekable) {
1835       // move actual file pointer to current position
1836       streamPos = s.seek(diff, SeekPos.Current);
1837     }
1838     // reset buffer data to be empty
1839     bufferSourcePos = bufferCurPos = bufferLen = 0;
1840     bufferDirty = false;
1841   }
1842 
1843   // returns true if end of stream is reached, false otherwise
1844   override @property bool eof() {
1845     if ((buffer.length == 0) || !readable) {
1846       return super.eof;
1847     }
1848     // some simple tests to avoid flushing
1849     if (ungetAvailable() || bufferCurPos != bufferLen)
1850       return false;
1851     if (bufferLen == buffer.length)
1852       flush();
1853     size_t res = super.readBlock(&buffer[bufferLen],buffer.length-bufferLen);
1854     bufferSourcePos +=  res;
1855     bufferLen += res;
1856     streamPos += res;
1857     return readEOF;
1858   }
1859 
1860   // returns size of stream
1861   override @property ulong size() {
1862     if (bufferDirty) flush();
1863     return s.size;
1864   }
1865 
1866   // returns estimated number of bytes available for immediate reading
1867   override @property size_t available() {
1868     return bufferLen - bufferCurPos;
1869   }
1870 }
1871 
1872 /// An exception for File errors.
1873 class StreamFileException: StreamException {
1874   /// Construct a StreamFileException with given error message.
1875   this(string msg) { super(msg); }
1876 }
1877 
1878 /// An exception for errors during File.open.
1879 class OpenException: StreamFileException {
1880   /// Construct an OpenFileException with given error message.
1881   this(string msg) { super(msg); }
1882 }
1883 
1884 /// Specifies the $(LREF File) access mode used when opening the file.
1885 enum FileMode {
1886   In = 1,     /// Opens the file for reading.
1887   Out = 2,    /// Opens the file for writing.
1888   OutNew = 6, /// Opens the file for writing, creates a new file if it doesn't exist.
1889   Append = 10 /// Opens the file for writing, appending new data to the end of the file.
1890 }
1891 
1892 version (Windows) {
1893   private import core.sys.windows.windows;
1894   extern (Windows) {
1895     void FlushFileBuffers(HANDLE hFile);
1896     DWORD  GetFileType(HANDLE hFile);
1897   }
1898 }
1899 version (Posix) {
1900   private import core.sys.posix.fcntl;
1901   private import core.sys.posix.unistd;
1902   alias HANDLE = int;
1903 }
1904 
1905 /// This subclass is for unbuffered file system streams.
1906 class File: Stream {
1907 
1908   version (Windows) {
1909     private HANDLE hFile;
1910   }
1911   version (Posix) {
1912     private HANDLE hFile = -1;
1913   }
1914 
1915   this() {
1916     super();
1917     version (Windows) {
1918       hFile = null;
1919     }
1920     version (Posix) {
1921       hFile = -1;
1922     }
1923     isopen = false;
1924   }
1925 
1926   // opens existing handle; use with care!
1927   this(HANDLE hFile, FileMode mode) {
1928     super();
1929     this.hFile = hFile;
1930     readable = cast(bool)(mode & FileMode.In);
1931     writeable = cast(bool)(mode & FileMode.Out);
1932     version(Windows) {
1933       seekable = GetFileType(hFile) == 1; // FILE_TYPE_DISK
1934     } else {
1935       auto result = lseek(hFile, 0, 0);
1936       seekable = (result != ~0);
1937     }
1938   }
1939 
1940   /***
1941    * Create the stream with no open file, an open file in read mode, or an open
1942    * file with explicit file mode.
1943    * mode, if given, is a combination of FileMode.In
1944    * (indicating a file that can be read) and FileMode.Out (indicating a file
1945    * that can be written).
1946    * Opening a file for reading that doesn't exist will error.
1947    * Opening a file for writing that doesn't exist will create the file.
1948    * The FileMode.OutNew mode will open the file for writing and reset the
1949    * length to zero.
1950    * The FileMode.Append mode will open the file for writing and move the
1951    * file position to the end of the file.
1952    */
1953   this(string filename, FileMode mode = FileMode.In)
1954   {
1955       this();
1956       open(filename, mode);
1957   }
1958 
1959 
1960   /***
1961    * Open a file for the stream, in an identical manner to the constructors.
1962    * If an error occurs an OpenException is thrown.
1963    */
1964   void open(string filename, FileMode mode = FileMode.In) {
1965     close();
1966     int access, share, createMode;
1967     parseMode(mode, access, share, createMode);
1968     seekable = true;
1969     readable = cast(bool)(mode & FileMode.In);
1970     writeable = cast(bool)(mode & FileMode.Out);
1971     version (Windows) {
1972       hFile = CreateFileW(filename.tempCString!wchar(), access, share,
1973                           null, createMode, 0, null);
1974       isopen = hFile != INVALID_HANDLE_VALUE;
1975     }
1976     version (Posix) {
1977       hFile = core.sys.posix.fcntl.open(filename.tempCString(), access | createMode, share);
1978       isopen = hFile != -1;
1979     }
1980     if (!isopen)
1981       throw new OpenException(cast(string) ("Cannot open or create file '"
1982                                             ~ filename ~ "'"));
1983     else if ((mode & FileMode.Append) == FileMode.Append)
1984       seekEnd(0);
1985   }
1986 
1987   private void parseMode(int mode,
1988                          out int access,
1989                          out int share,
1990                          out int createMode) {
1991     version (Windows) {
1992       share |= FILE_SHARE_READ | FILE_SHARE_WRITE;
1993       if (mode & FileMode.In) {
1994         access |= GENERIC_READ;
1995         createMode = OPEN_EXISTING;
1996       }
1997       if (mode & FileMode.Out) {
1998         access |= GENERIC_WRITE;
1999         createMode = OPEN_ALWAYS; // will create if not present
2000       }
2001       if ((mode & FileMode.OutNew) == FileMode.OutNew) {
2002         createMode = CREATE_ALWAYS; // resets file
2003       }
2004     }
2005     version (Posix) {
2006       share = octal!666;
2007       if (mode & FileMode.In) {
2008         access = O_RDONLY;
2009       }
2010       if (mode & FileMode.Out) {
2011         createMode = O_CREAT; // will create if not present
2012         access = O_WRONLY;
2013       }
2014       if (access == (O_WRONLY | O_RDONLY)) {
2015         access = O_RDWR;
2016       }
2017       if ((mode & FileMode.OutNew) == FileMode.OutNew) {
2018         access |= O_TRUNC; // resets file
2019       }
2020     }
2021   }
2022 
2023   /// Create a file for writing.
2024   void create(string filename) {
2025     create(filename, FileMode.OutNew);
2026   }
2027 
2028   /// ditto
2029   void create(string filename, FileMode mode) {
2030     close();
2031     open(filename, mode | FileMode.OutNew);
2032   }
2033 
2034   /// Close the current file if it is open; otherwise it does nothing.
2035   override void close() {
2036     if (isopen) {
2037       super.close();
2038       if (hFile) {
2039         version (Windows) {
2040           CloseHandle(hFile);
2041           hFile = null;
2042         } else version (Posix) {
2043           core.sys.posix.unistd.close(hFile);
2044           hFile = -1;
2045         }
2046       }
2047     }
2048   }
2049 
2050   // destructor, closes file if still opened
2051   ~this() { close(); }
2052 
2053   version (Windows) {
2054     // returns size of stream
2055     override @property ulong size() {
2056       assertSeekable();
2057       uint sizehi;
2058       uint sizelow = GetFileSize(hFile,&sizehi);
2059       return (cast(ulong)sizehi << 32) + sizelow;
2060     }
2061   }
2062 
2063   override size_t readBlock(void* buffer, size_t size) {
2064     assertReadable();
2065     version (Windows) {
2066       auto dwSize = to!DWORD(size);
2067       ReadFile(hFile, buffer, dwSize, &dwSize, null);
2068       size = dwSize;
2069     } else version (Posix) {
2070       size = core.sys.posix.unistd.read(hFile, buffer, size);
2071       if (size == -1)
2072         size = 0;
2073     }
2074     readEOF = (size == 0);
2075     return size;
2076   }
2077 
2078   override size_t writeBlock(const void* buffer, size_t size) {
2079     assertWriteable();
2080     version (Windows) {
2081       auto dwSize = to!DWORD(size);
2082       WriteFile(hFile, buffer, dwSize, &dwSize, null);
2083       size = dwSize;
2084     } else version (Posix) {
2085       size = core.sys.posix.unistd.write(hFile, buffer, size);
2086       if (size == -1)
2087         size = 0;
2088     }
2089     return size;
2090   }
2091 
2092   override ulong seek(long offset, SeekPos rel) {
2093     assertSeekable();
2094     version (Windows) {
2095       int hi = cast(int)(offset>>32);
2096       uint low = SetFilePointer(hFile, cast(int)offset, &hi, rel);
2097       if ((low == INVALID_SET_FILE_POINTER) && (GetLastError() != 0))
2098         throw new SeekException("unable to move file pointer");
2099       ulong result = (cast(ulong)hi << 32) + low;
2100     } else version (Posix) {
2101       auto result = lseek(hFile, cast(off_t)offset, rel);
2102       if (result == cast(typeof(result))-1)
2103         throw new SeekException("unable to move file pointer");
2104     }
2105     readEOF = false;
2106     return cast(ulong)result;
2107   }
2108 
2109   /***
2110    * For a seekable file returns the difference of the size and position and
2111    * otherwise returns 0.
2112    */
2113 
2114   override @property size_t available() {
2115     if (seekable) {
2116       ulong lavail = size - position;
2117       if (lavail > size_t.max) lavail = size_t.max;
2118       return cast(size_t)lavail;
2119     }
2120     return 0;
2121   }
2122 
2123   // OS-specific property, just in case somebody wants
2124   // to mess with underlying API
2125   HANDLE handle() { return hFile; }
2126 
2127   // run a few tests
2128   unittest {
2129     File file = new File;
2130     int i = 666;
2131     auto stream_file = undead.internal.file.deleteme ~ "-stream.$$$";
2132     file.create(stream_file);
2133     // should be ok to write
2134     assert(file.writeable);
2135     file.writeLine("Testing stream.d:");
2136     file.writeString("Hello, world!");
2137     file.write(i);
2138     // string#1 + string#2 + int should give exacly that
2139     version (Windows)
2140       assert(file.position == 19 + 13 + 4);
2141     version (Posix)
2142       assert(file.position == 18 + 13 + 4);
2143     // we must be at the end of file
2144     assert(file.eof);
2145     file.close();
2146     // no operations are allowed when file is closed
2147     assert(!file.readable && !file.writeable && !file.seekable);
2148     file.open(stream_file);
2149     // should be ok to read
2150     assert(file.readable);
2151     assert(file.available == file.size);
2152     char[] line = file.readLine();
2153     char[] exp = "Testing stream.d:".dup;
2154     assert(line[0] == 'T');
2155     assert(line.length == exp.length);
2156     assert(!std.algorithm.cmp(line, "Testing stream.d:"));
2157     // jump over "Hello, "
2158     file.seek(7, SeekPos.Current);
2159     version (Windows)
2160       assert(file.position == 19 + 7);
2161     version (Posix)
2162       assert(file.position == 18 + 7);
2163     assert(!std.algorithm.cmp(file.readString(6), "world!"));
2164     i = 0; file.read(i);
2165     assert(i == 666);
2166     // string#1 + string#2 + int should give exacly that
2167     version (Windows)
2168       assert(file.position == 19 + 13 + 4);
2169     version (Posix)
2170       assert(file.position == 18 + 13 + 4);
2171     // we must be at the end of file
2172     assert(file.eof);
2173     file.close();
2174     file.open(stream_file,FileMode.OutNew | FileMode.In);
2175     file.writeLine("Testing stream.d:");
2176     file.writeLine("Another line");
2177     file.writeLine("");
2178     file.writeLine("That was blank");
2179     file.position = 0;
2180     char[][] lines;
2181     foreach(char[] fileLine; file) {
2182       lines ~= fileLine.dup;
2183     }
2184     assert( lines.length == 4 );
2185     assert( lines[0] == "Testing stream.d:");
2186     assert( lines[1] == "Another line");
2187     assert( lines[2] == "");
2188     assert( lines[3] == "That was blank");
2189     file.position = 0;
2190     lines = new char[][4];
2191     foreach(ulong n, char[] fileLine; file) {
2192       lines[cast(size_t)(n-1)] = fileLine.dup;
2193     }
2194     assert( lines[0] == "Testing stream.d:");
2195     assert( lines[1] == "Another line");
2196     assert( lines[2] == "");
2197     assert( lines[3] == "That was blank");
2198     file.close();
2199     std.file.remove(stream_file);
2200   }
2201 }
2202 
2203 /***
2204  * This subclass is for buffered file system streams.
2205  *
2206  * It is a convenience class for wrapping a File in a BufferedStream.
2207  * A buffered stream must be closed explicitly to ensure the final buffer
2208  * content is written to the file.
2209  */
2210 class BufferedFile: BufferedStream {
2211 
2212   /// opens file for reading
2213   this() { super(new File()); }
2214 
2215   /// opens file in requested mode and buffer size
2216   this(string filename, FileMode mode = FileMode.In,
2217        size_t bufferSize = DefaultBufferSize) {
2218     super(new File(filename,mode),bufferSize);
2219   }
2220 
2221   /// opens file for reading with requested buffer size
2222   this(File file, size_t bufferSize = DefaultBufferSize) {
2223     super(file,bufferSize);
2224   }
2225 
2226   /// opens existing handle; use with care!
2227   this(HANDLE hFile, FileMode mode, size_t buffersize = DefaultBufferSize) {
2228     super(new File(hFile,mode),buffersize);
2229   }
2230 
2231   /// opens file in requested mode
2232   void open(string filename, FileMode mode = FileMode.In) {
2233     File sf = cast(File)s;
2234     sf.open(filename,mode);
2235     resetSource();
2236   }
2237 
2238   /// creates file in requested mode
2239   void create(string filename, FileMode mode = FileMode.OutNew) {
2240     File sf = cast(File)s;
2241     sf.create(filename,mode);
2242     resetSource();
2243   }
2244 
2245   // run a few tests same as File
2246   unittest {
2247     BufferedFile file = new BufferedFile;
2248     int i = 666;
2249     auto stream_file = undead.internal.file.deleteme ~ "-stream.$$$";
2250     file.create(stream_file);
2251     // should be ok to write
2252     assert(file.writeable);
2253     file.writeLine("Testing stream.d:");
2254     file.writeString("Hello, world!");
2255     file.write(i);
2256     // string#1 + string#2 + int should give exacly that
2257     version (Windows)
2258       assert(file.position == 19 + 13 + 4);
2259     version (Posix)
2260       assert(file.position == 18 + 13 + 4);
2261     // we must be at the end of file
2262     assert(file.eof);
2263     long oldsize = cast(long)file.size;
2264     file.close();
2265     // no operations are allowed when file is closed
2266     assert(!file.readable && !file.writeable && !file.seekable);
2267     file.open(stream_file);
2268     // should be ok to read
2269     assert(file.readable);
2270     // test getc/ungetc and size
2271     char c1 = file.getc();
2272     file.ungetc(c1);
2273     assert( file.size == oldsize );
2274     assert(!std.algorithm.cmp(file.readLine(), "Testing stream.d:"));
2275     // jump over "Hello, "
2276     file.seek(7, SeekPos.Current);
2277     version (Windows)
2278       assert(file.position == 19 + 7);
2279     version (Posix)
2280       assert(file.position == 18 + 7);
2281     assert(!std.algorithm.cmp(file.readString(6), "world!"));
2282     i = 0; file.read(i);
2283     assert(i == 666);
2284     // string#1 + string#2 + int should give exacly that
2285     version (Windows)
2286       assert(file.position == 19 + 13 + 4);
2287     version (Posix)
2288       assert(file.position == 18 + 13 + 4);
2289     // we must be at the end of file
2290     assert(file.eof);
2291     file.close();
2292     std.file.remove(stream_file);
2293   }
2294 
2295 }
2296 
2297 /// UTF byte-order-mark signatures
2298 enum BOM {
2299         UTF8,           /// UTF-8
2300         UTF16LE,        /// UTF-16 Little Endian
2301         UTF16BE,        /// UTF-16 Big Endian
2302         UTF32LE,        /// UTF-32 Little Endian
2303         UTF32BE,        /// UTF-32 Big Endian
2304 }
2305 
2306 private enum int NBOMS = 5;
2307 immutable Endian[NBOMS] BOMEndian =
2308 [ std.system.endian,
2309   Endian.littleEndian, Endian.bigEndian,
2310   Endian.littleEndian, Endian.bigEndian
2311   ];
2312 
2313 immutable ubyte[][NBOMS] ByteOrderMarks =
2314 [ [0xEF, 0xBB, 0xBF],
2315   [0xFF, 0xFE],
2316   [0xFE, 0xFF],
2317   [0xFF, 0xFE, 0x00, 0x00],
2318   [0x00, 0x00, 0xFE, 0xFF]
2319   ];
2320 
2321 
2322 /***
2323  * This subclass wraps a stream with big-endian or little-endian byte order
2324  * swapping.
2325  *
2326  * UTF Byte-Order-Mark (BOM) signatures can be read and deduced or
2327  * written.
2328  * Note that an EndianStream should not be used as the source of another
2329  * FilterStream since a FilterStream call the source with byte-oriented
2330  * read/write requests and the EndianStream will not perform any byte swapping.
2331  * The EndianStream reads and writes binary data (non-getc functions) in a
2332  * one-to-one
2333  * manner with the source stream so the source stream's position and state will be
2334  * kept in sync with the EndianStream if only non-getc functions are called.
2335  */
2336 class EndianStream : FilterStream {
2337 
2338   Endian endian;        /// Endianness property of the source stream.
2339 
2340   /***
2341    * Create the endian stream for the source stream source with endianness end.
2342    * The default endianness is the native byte order.
2343    * The Endian type is defined
2344    * in the std.system module.
2345    */
2346   this(Stream source, Endian end = std.system.endian) {
2347     super(source);
2348     endian = end;
2349   }
2350 
2351   /***
2352    * Return -1 if no BOM and otherwise read the BOM and return it.
2353    *
2354    * If there is no BOM or if bytes beyond the BOM are read then the bytes read
2355    * are pushed back onto the ungetc buffer or ungetcw buffer.
2356    * Pass ungetCharSize == 2 to use
2357    * ungetcw instead of ungetc when no BOM is present.
2358    */
2359   int readBOM(int ungetCharSize = 1) {
2360     ubyte[4] BOM_buffer;
2361     int n = 0;       // the number of read bytes
2362     int result = -1; // the last match or -1
2363     for (int i=0; i < NBOMS; ++i) {
2364       int j;
2365       immutable ubyte[] bom = ByteOrderMarks[i];
2366       for (j=0; j < bom.length; ++j) {
2367         if (n <= j) { // have to read more
2368           if (eof)
2369             break;
2370           readExact(&BOM_buffer[n++],1);
2371         }
2372         if (BOM_buffer[j] != bom[j])
2373           break;
2374       }
2375       if (j == bom.length) // found a match
2376         result = i;
2377     }
2378     ptrdiff_t m = 0;
2379     if (result != -1) {
2380       endian = BOMEndian[result]; // set stream endianness
2381       m = ByteOrderMarks[result].length;
2382     }
2383     if ((ungetCharSize == 1 && result == -1) || (result == BOM.UTF8)) {
2384       while (n-- > m)
2385         ungetc(BOM_buffer[n]);
2386     } else { // should eventually support unget for dchar as well
2387       if (n & 1) // make sure we have an even number of bytes
2388         readExact(&BOM_buffer[n++],1);
2389       while (n > m) {
2390         n -= 2;
2391         wchar cw = *(cast(wchar*)&BOM_buffer[n]);
2392         fixBO(&cw,2);
2393         ungetcw(cw);
2394       }
2395     }
2396     return result;
2397   }
2398 
2399   /***
2400    * Correct the byte order of buffer to match native endianness.
2401    * size must be even.
2402    */
2403   final void fixBO(const(void)* buffer, size_t size) {
2404     if (endian != std.system.endian) {
2405       ubyte* startb = cast(ubyte*)buffer;
2406       uint* start = cast(uint*)buffer;
2407       switch (size) {
2408       case 0: break;
2409       case 2: {
2410         ubyte x = *startb;
2411         *startb = *(startb+1);
2412         *(startb+1) = x;
2413         break;
2414       }
2415       case 4: {
2416         *start = bswap(*start);
2417         break;
2418       }
2419       default: {
2420         uint* end = cast(uint*)(buffer + size - uint.sizeof);
2421         while (start < end) {
2422           uint x = bswap(*start);
2423           *start = bswap(*end);
2424           *end = x;
2425           ++start;
2426           --end;
2427         }
2428         startb = cast(ubyte*)start;
2429         ubyte* endb = cast(ubyte*)end;
2430         auto len = uint.sizeof - (startb - endb);
2431         if (len > 0)
2432           fixBO(startb,len);
2433       }
2434       }
2435     }
2436   }
2437 
2438   /***
2439    * Correct the byte order of the given buffer in blocks of the given size and
2440    * repeated the given number of times.
2441    * size must be even.
2442    */
2443   final void fixBlockBO(void* buffer, uint size, size_t repeat) {
2444     while (repeat--) {
2445       fixBO(buffer,size);
2446       buffer += size;
2447     }
2448   }
2449 
2450   override void read(out byte x) { readExact(&x, x.sizeof); }
2451   override void read(out ubyte x) { readExact(&x, x.sizeof); }
2452   override void read(out short x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); }
2453   override void read(out ushort x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); }
2454   override void read(out int x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); }
2455   override void read(out uint x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); }
2456   override void read(out long x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); }
2457   override void read(out ulong x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); }
2458   override void read(out float x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); }
2459   override void read(out double x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); }
2460   override void read(out real x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); }
2461   override void read(out char x) { readExact(&x, x.sizeof); }
2462   override void read(out wchar x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); }
2463   override void read(out dchar x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); }
2464 
2465   override wchar getcw() {
2466     wchar c;
2467     if (prevCr) {
2468       prevCr = false;
2469       c = getcw();
2470       if (c != '\n')
2471         return c;
2472     }
2473     if (unget.length > 1) {
2474       c = unget[unget.length - 1];
2475       unget.length = unget.length - 1;
2476     } else {
2477       void* buf = &c;
2478       size_t n = readBlock(buf,2);
2479       if (n == 1 && readBlock(buf+1,1) == 0)
2480           throw new ReadException("not enough data in stream");
2481       fixBO(&c,c.sizeof);
2482     }
2483     return c;
2484   }
2485 
2486   override wchar[] readStringW(size_t length) {
2487     wchar[] result = new wchar[length];
2488     readExact(result.ptr, length * wchar.sizeof);
2489     fixBlockBO(result.ptr, wchar.sizeof, length);
2490     return result;
2491   }
2492 
2493   /// Write the specified BOM b to the source stream.
2494   void writeBOM(BOM b) {
2495     immutable ubyte[] bom = ByteOrderMarks[b];
2496     writeBlock(bom.ptr, bom.length);
2497   }
2498 
2499   override void write(byte x) { writeExact(&x, x.sizeof); }
2500   override void write(ubyte x) { writeExact(&x, x.sizeof); }
2501   override void write(short x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); }
2502   override void write(ushort x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); }
2503   override void write(int x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); }
2504   override void write(uint x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); }
2505   override void write(long x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); }
2506   override void write(ulong x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); }
2507   override void write(float x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); }
2508   override void write(double x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); }
2509   override void write(real x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); }
2510   override void write(char x) { writeExact(&x, x.sizeof); }
2511   override void write(wchar x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); }
2512   override void write(dchar x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); }
2513 
2514   override void writeStringW(const(wchar)[] str) {
2515     foreach(wchar cw;str) {
2516       fixBO(&cw,2);
2517       s.writeExact(&cw, 2);
2518     }
2519   }
2520 
2521   override @property bool eof() { return s.eof && !ungetAvailable();  }
2522   override @property ulong size() { return s.size;  }
2523 
2524   unittest {
2525     MemoryStream m;
2526     m = new MemoryStream ();
2527     EndianStream em = new EndianStream(m,Endian.bigEndian);
2528     uint x = 0x11223344;
2529     em.write(x);
2530     assert( m.data[0] == 0x11 );
2531     assert( m.data[1] == 0x22 );
2532     assert( m.data[2] == 0x33 );
2533     assert( m.data[3] == 0x44 );
2534     em.position = 0;
2535     ushort x2 = 0x5566;
2536     em.write(x2);
2537     assert( m.data[0] == 0x55 );
2538     assert( m.data[1] == 0x66 );
2539     em.position = 0;
2540     static ubyte[12] x3 = [1,2,3,4,5,6,7,8,9,10,11,12];
2541     em.fixBO(x3.ptr,12);
2542     if (std.system.endian == Endian.littleEndian) {
2543       assert( x3[0] == 12 );
2544       assert( x3[1] == 11 );
2545       assert( x3[2] == 10 );
2546       assert( x3[4] == 8 );
2547       assert( x3[5] == 7 );
2548       assert( x3[6] == 6 );
2549       assert( x3[8] == 4 );
2550       assert( x3[9] == 3 );
2551       assert( x3[10] == 2 );
2552       assert( x3[11] == 1 );
2553     }
2554     em.endian = Endian.littleEndian;
2555     em.write(x);
2556     assert( m.data[0] == 0x44 );
2557     assert( m.data[1] == 0x33 );
2558     assert( m.data[2] == 0x22 );
2559     assert( m.data[3] == 0x11 );
2560     em.position = 0;
2561     em.write(x2);
2562     assert( m.data[0] == 0x66 );
2563     assert( m.data[1] == 0x55 );
2564     em.position = 0;
2565     em.fixBO(x3.ptr,12);
2566     if (std.system.endian == Endian.bigEndian) {
2567       assert( x3[0] == 12 );
2568       assert( x3[1] == 11 );
2569       assert( x3[2] == 10 );
2570       assert( x3[4] == 8 );
2571       assert( x3[5] == 7 );
2572       assert( x3[6] == 6 );
2573       assert( x3[8] == 4 );
2574       assert( x3[9] == 3 );
2575       assert( x3[10] == 2 );
2576       assert( x3[11] == 1 );
2577     }
2578     em.writeBOM(BOM.UTF8);
2579     assert( m.position == 3 );
2580     assert( m.data[0] == 0xEF );
2581     assert( m.data[1] == 0xBB );
2582     assert( m.data[2] == 0xBF );
2583     em.writeString ("Hello, world");
2584     em.position = 0;
2585     assert( m.position == 0 );
2586     assert( em.readBOM() == BOM.UTF8 );
2587     assert( m.position == 3 );
2588     assert( em.getc() == 'H' );
2589     em.position = 0;
2590     em.writeBOM(BOM.UTF16BE);
2591     assert( m.data[0] == 0xFE );
2592     assert( m.data[1] == 0xFF );
2593     em.position = 0;
2594     em.writeBOM(BOM.UTF16LE);
2595     assert( m.data[0] == 0xFF );
2596     assert( m.data[1] == 0xFE );
2597     em.position = 0;
2598     em.writeString ("Hello, world");
2599     em.position = 0;
2600     assert( em.readBOM() == -1 );
2601     assert( em.getc() == 'H' );
2602     assert( em.getc() == 'e' );
2603     assert( em.getc() == 'l' );
2604     assert( em.getc() == 'l' );
2605     em.position = 0;
2606   }
2607 }
2608 
2609 /***
2610  * Parameterized subclass that wraps an array-like buffer with a stream
2611  * interface.
2612  *
2613  * The type Buffer must support the length property, opIndex and opSlice.
2614  * Compile in release mode when directly instantiating a TArrayStream to avoid
2615  * link errors.
2616  */
2617 class TArrayStream(Buffer): Stream {
2618   Buffer buf; // current data
2619   ulong len;  // current data length
2620   ulong cur;  // current file position
2621 
2622   /// Create the stream for the the buffer buf. Non-copying.
2623   this(Buffer buf) {
2624     super ();
2625     this.buf = buf;
2626     this.len = buf.length;
2627     readable = writeable = seekable = true;
2628   }
2629 
2630   // ensure subclasses don't violate this
2631   invariant() {
2632     assert(len <= buf.length);
2633     assert(cur <= len);
2634   }
2635 
2636   override size_t readBlock(void* buffer, size_t size) {
2637     assertReadable();
2638     ubyte* cbuf = cast(ubyte*) buffer;
2639     if (len - cur < size)
2640       size = cast(size_t)(len - cur);
2641     ubyte[] ubuf = cast(ubyte[])buf[cast(size_t)cur .. cast(size_t)(cur + size)];
2642     cbuf[0 .. size] = ubuf[];
2643     cur += size;
2644     return size;
2645   }
2646 
2647   override size_t writeBlock(const void* buffer, size_t size) {
2648     assertWriteable();
2649     ubyte* cbuf = cast(ubyte*) buffer;
2650     ulong blen = buf.length;
2651     if (cur + size > blen)
2652       size = cast(size_t)(blen - cur);
2653     ubyte[] ubuf = cast(ubyte[])buf[cast(size_t)cur .. cast(size_t)(cur + size)];
2654     ubuf[] = cbuf[0 .. size];
2655     cur += size;
2656     if (cur > len)
2657       len = cur;
2658     return size;
2659   }
2660 
2661   override ulong seek(long offset, SeekPos rel) {
2662     assertSeekable();
2663     long scur; // signed to saturate to 0 properly
2664 
2665     switch (rel) {
2666     case SeekPos.Set: scur = offset; break;
2667     case SeekPos.Current: scur = cast(long)(cur + offset); break;
2668     case SeekPos.End: scur = cast(long)(len + offset); break;
2669     default:
2670         assert(0);
2671     }
2672 
2673     if (scur < 0)
2674       cur = 0;
2675     else if (scur > len)
2676       cur = len;
2677     else
2678       cur = cast(ulong)scur;
2679 
2680     return cur;
2681   }
2682 
2683   override @property size_t available () { return cast(size_t)(len - cur); }
2684 
2685   /// Get the current memory data in total.
2686   @property ubyte[] data() {
2687     if (len > size_t.max)
2688       throw new StreamException("Stream too big");
2689     const(void)[] res = buf[0 .. cast(size_t)len];
2690     return cast(ubyte[])res;
2691   }
2692 
2693   override string toString() {
2694       // assume data is UTF8
2695       return to!(string)(cast(char[])data);
2696   }
2697 }
2698 
2699 /* Test the TArrayStream */
2700 unittest {
2701   char[100] buf;
2702   TArrayStream!(char[]) m;
2703 
2704   m = new TArrayStream!(char[]) (buf);
2705   assert (m.isOpen);
2706   m.writeString ("Hello, world");
2707   assert (m.position == 12);
2708   assert (m.available == 88);
2709   assert (m.seekSet (0) == 0);
2710   assert (m.available == 100);
2711   assert (m.seekCur (4) == 4);
2712   assert (m.available == 96);
2713   assert (m.seekEnd (-8) == 92);
2714   assert (m.available == 8);
2715   assert (m.size == 100);
2716   assert (m.seekSet (4) == 4);
2717   assert (m.readString (4) == "o, w");
2718   m.writeString ("ie");
2719   assert (buf[0..12] == "Hello, wield");
2720   assert (m.position == 10);
2721   assert (m.available == 90);
2722   assert (m.size == 100);
2723   m.seekSet (0);
2724   assert (m.printf ("Answer is %d", 42) == 12);
2725   assert (buf[0..12] == "Answer is 42");
2726 }
2727 
2728 /// This subclass reads and constructs an array of bytes in memory.
2729 class MemoryStream: TArrayStream!(ubyte[]) {
2730 
2731   /// Create the output buffer and setup for reading, writing, and seeking.
2732   // clear to an empty buffer.
2733   this() { this(cast(ubyte[]) null); }
2734 
2735   /***
2736    * Create the output buffer and setup for reading, writing, and seeking.
2737    * Load it with specific input data.
2738    */
2739   this(ubyte[] buf) { super (buf); }
2740   this(byte[] buf) { this(cast(ubyte[]) buf); } /// ditto
2741   this(char[] buf) { this(cast(ubyte[]) buf); } /// ditto
2742 
2743   /// Ensure the stream can write count extra bytes from cursor position without an allocation.
2744   void reserve(size_t count) {
2745     if (cur + count > buf.length)
2746       buf.length = cast(uint)((cur + count) * 2);
2747   }
2748 
2749   override size_t writeBlock(const void* buffer, size_t size) {
2750     reserve(size);
2751     return super.writeBlock(buffer,size);
2752   }
2753 
2754   unittest {
2755     MemoryStream m;
2756 
2757     m = new MemoryStream ();
2758     assert (m.isOpen);
2759     m.writeString ("Hello, world");
2760     assert (m.position == 12);
2761     assert (m.seekSet (0) == 0);
2762     assert (m.available == 12);
2763     assert (m.seekCur (4) == 4);
2764     assert (m.available == 8);
2765     assert (m.seekEnd (-8) == 4);
2766     assert (m.available == 8);
2767     assert (m.size == 12);
2768     assert (m.readString (4) == "o, w");
2769     m.writeString ("ie");
2770     assert (cast(char[]) m.data == "Hello, wield");
2771     m.seekEnd (0);
2772     m.writeString ("Foo");
2773     assert (m.position == 15);
2774     assert (m.available == 0);
2775     m.writeString ("Foo foo foo foo foo foo foo");
2776     assert (m.position == 42);
2777     m.position = 0;
2778     assert (m.available == 42);
2779     version (GNU) {} else // writef not allowed in GNU
2780     {
2781       m.writef("%d %d %s",100,345,"hello");
2782       auto str = m.toString();
2783       assert (str[0..13] == "100 345 hello", str[0 .. 13]);
2784       assert (m.available == 29);
2785       assert (m.position == 13);
2786 
2787       MemoryStream m2;
2788       m.position = 3;
2789       m2 = new MemoryStream ();
2790       m2.writeString("before");
2791       m2.copyFrom(m,10);
2792       str = m2.toString();
2793       assert (str[0..16] == "before 345 hello");
2794       m2.position = 3;
2795       m2.copyFrom(m);
2796       auto str2 = m.toString();
2797       str = m2.toString();
2798       assert (str == ("bef" ~ str2));
2799     }
2800   }
2801 }
2802 
2803 import std.mmfile;
2804 
2805 /***
2806  * This subclass wraps a memory-mapped file with the stream API.
2807  * See std.mmfile module.
2808  */
2809 class MmFileStream : TArrayStream!(MmFile) {
2810 
2811   /// Create stream wrapper for file.
2812   this(MmFile file) {
2813     super (file);
2814     MmFile.Mode mode = file.mode();
2815     writeable = mode > MmFile.Mode.read;
2816   }
2817 
2818   override void flush() {
2819     if (isopen) {
2820       super.flush();
2821       buf.flush();
2822     }
2823   }
2824 
2825   override void close() {
2826     if (isopen) {
2827       super.close();
2828       buf.destroy();
2829       buf = null;
2830     }
2831   }
2832 }
2833 
2834 unittest {
2835   auto test_file = undead.internal.file.deleteme ~ "-testing.txt";
2836   MmFile mf = new MmFile(test_file,MmFile.Mode.readWriteNew,100,null);
2837   MmFileStream m;
2838   m = new MmFileStream (mf);
2839   m.writeString ("Hello, world");
2840   assert (m.position == 12);
2841   assert (m.seekSet (0) == 0);
2842   assert (m.seekCur (4) == 4);
2843   assert (m.seekEnd (-8) == 92);
2844   assert (m.size == 100);
2845   assert (m.seekSet (4));
2846   assert (m.readString (4) == "o, w");
2847   m.writeString ("ie");
2848   ubyte[] dd = m.data;
2849   assert ((cast(char[]) dd)[0 .. 12] == "Hello, wield");
2850   m.position = 12;
2851   m.writeString ("Foo");
2852   assert (m.position == 15);
2853   m.writeString ("Foo foo foo foo foo foo foo");
2854   assert (m.position == 42);
2855   m.close();
2856   mf = new MmFile(test_file);
2857   m = new MmFileStream (mf);
2858   assert (!m.writeable);
2859   char[] str = m.readString(12);
2860   assert (str == "Hello, wield");
2861   m.close();
2862   std.file.remove(test_file);
2863 }
2864 
2865 
2866 /***
2867  * This subclass slices off a portion of another stream, making seeking relative
2868  * to the boundaries of the slice.
2869  *
2870  * It could be used to section a large file into a
2871  * set of smaller files, such as with tar archives. Reading and writing a
2872  * SliceStream does not modify the position of the source stream if it is
2873  * seekable.
2874  */
2875 class SliceStream : FilterStream {
2876   private {
2877     ulong pos;  // our position relative to low
2878     ulong low; // low stream offset.
2879     ulong high; // high stream offset.
2880     bool bounded; // upper-bounded by high.
2881   }
2882 
2883   /***
2884    * Indicate both the source stream to use for reading from and the low part of
2885    * the slice.
2886    *
2887    * The high part of the slice is dependent upon the end of the source
2888    * stream, so that if you write beyond the end it resizes the stream normally.
2889    */
2890   this (Stream s, ulong low)
2891   in {
2892     assert (low <= s.size);
2893   }
2894   do {
2895     super(s);
2896     this.low = low;
2897     this.high = 0;
2898     this.bounded = false;
2899   }
2900 
2901   /***
2902    * Indicate the high index as well.
2903    *
2904    * Attempting to read or write past the high
2905    * index results in the end being clipped off.
2906    */
2907   this (Stream s, ulong low, ulong high)
2908   in {
2909     assert (low <= high);
2910     assert (high <= s.size);
2911   }
2912   do {
2913     super(s);
2914     this.low = low;
2915     this.high = high;
2916     this.bounded = true;
2917   }
2918 
2919   invariant() {
2920     if (bounded)
2921       assert (pos <= high - low);
2922     else
2923       // size() does not appear to be const, though it should be
2924       assert (pos <= (cast()s).size - low);
2925   }
2926 
2927   override size_t readBlock (void *buffer, size_t size) {
2928     assertReadable();
2929     if (bounded && size > high - low - pos)
2930         size = cast(size_t)(high - low - pos);
2931     ulong bp = s.position;
2932     if (seekable)
2933       s.position = low + pos;
2934     size_t ret = super.readBlock(buffer, size);
2935     if (seekable) {
2936       pos = s.position - low;
2937       s.position = bp;
2938     }
2939     return ret;
2940   }
2941 
2942   override size_t writeBlock (const void *buffer, size_t size) {
2943     assertWriteable();
2944     if (bounded && size > high - low - pos)
2945         size = cast(size_t)(high - low - pos);
2946     ulong bp = s.position;
2947     if (seekable)
2948       s.position = low + pos;
2949     size_t ret = s.writeBlock(buffer, size);
2950     if (seekable) {
2951       pos = s.position - low;
2952       s.position = bp;
2953     }
2954     return ret;
2955   }
2956 
2957   override ulong seek(long offset, SeekPos rel) {
2958     assertSeekable();
2959     long spos;
2960 
2961     switch (rel) {
2962       case SeekPos.Set:
2963         spos = offset;
2964         break;
2965       case SeekPos.Current:
2966         spos = cast(long)(pos + offset);
2967         break;
2968       case SeekPos.End:
2969         if (bounded)
2970           spos = cast(long)(high - low + offset);
2971         else
2972           spos = cast(long)(s.size - low + offset);
2973         break;
2974       default:
2975         assert(0);
2976     }
2977 
2978     if (spos < 0)
2979       pos = 0;
2980     else if (bounded && spos > high - low)
2981       pos = high - low;
2982     else if (!bounded && spos > s.size - low)
2983       pos = s.size - low;
2984     else
2985       pos = cast(ulong)spos;
2986 
2987     readEOF = false;
2988     return pos;
2989   }
2990 
2991   override @property size_t available() {
2992     size_t res = s.available;
2993     ulong bp = s.position;
2994     if (bp <= pos+low && pos+low <= bp+res) {
2995       if (!bounded || bp+res <= high)
2996         return cast(size_t)(bp + res - pos - low);
2997       else if (high <= bp+res)
2998         return cast(size_t)(high - pos - low);
2999     }
3000     return 0;
3001   }
3002 
3003   unittest {
3004     MemoryStream m;
3005     SliceStream s;
3006 
3007     m = new MemoryStream ((cast(char[])"Hello, world").dup);
3008     s = new SliceStream (m, 4, 8);
3009     assert (s.size == 4);
3010     assert (m.position == 0);
3011     assert (s.position == 0);
3012     assert (m.available == 12);
3013     assert (s.available == 4);
3014 
3015     assert (s.writeBlock (cast(char *) "Vroom", 5) == 4);
3016     assert (m.position == 0);
3017     assert (s.position == 4);
3018     assert (m.available == 12);
3019     assert (s.available == 0);
3020     assert (s.seekEnd (-2) == 2);
3021     assert (s.available == 2);
3022     assert (s.seekEnd (2) == 4);
3023     assert (s.available == 0);
3024     assert (m.position == 0);
3025     assert (m.available == 12);
3026 
3027     m.seekEnd(0);
3028     m.writeString("\nBlaho");
3029     assert (m.position == 18);
3030     assert (m.available == 0);
3031     assert (s.position == 4);
3032     assert (s.available == 0);
3033 
3034     s = new SliceStream (m, 4);
3035     assert (s.size == 14);
3036     assert (s.toString () == "Vrooorld\nBlaho");
3037     s.seekEnd (0);
3038     assert (s.available == 0);
3039 
3040     s.writeString (", etcetera.");
3041     assert (s.position == 25);
3042     assert (s.seekSet (0) == 0);
3043     assert (s.size == 25);
3044     assert (m.position == 18);
3045     assert (m.size == 29);
3046     assert (m.toString() == "HellVrooorld\nBlaho, etcetera.");
3047   }
3048 }