comparison lphobos/std/stream.d @ 473:373489eeaf90

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