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