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