1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 // severely outdated i/o stream interface
19 module iv
.stream
/*is aliced*/;
22 import std
.conv
: ConvOverflowException
;
23 import std
.traits
: isMutable
;
24 public import core
.stdc
.stdio
: SEEK_SET
, SEEK_CUR
, SEEK_END
;
27 // ////////////////////////////////////////////////////////////////////////// //
28 import iv
.exex
: MyException
, ExceptionCtor
;
30 mixin(MyException
!"StreamException");
33 // ////////////////////////////////////////////////////////////////////////// //
34 enum isReadableStream(T
) = is(typeof((inout int=0) {
37 auto v
= cast(void[])b
;
41 enum isWriteableStream(T
) = is(typeof((inout int=0) {
44 t
.rawWrite(cast(void[])b
);
47 enum isCloseableStream(T
) = is(typeof((inout int=0) {
52 enum isRWStream(T
) = isReadableStream
!T
&& isWriteableStream
!T
;
54 template isSeekableStream(T
) {
55 enum isSeekableStream
= is(typeof((inout int=0) {
56 import core
.stdc
.stdio
: SEEK_END
;
64 enum isClosableStream(T
) = is(typeof((inout int=0) {
70 enum streamHasEOF(T
) = is(typeof((inout int=0) {
75 enum streamHasSeek(T
) = is(typeof((inout int=0) {
76 import core
.stdc
.stdio
: SEEK_END
;
81 enum streamHasTell(T
) = is(typeof((inout int=0) {
86 enum streamHasName(T
) = is(typeof((inout int=0) {
91 enum streamHasSize(T
) = is(typeof((inout int=0) {
96 enum streamHasIsOpen(T
) = is(typeof((inout int=0) {
101 enum streamHasDetach(T
) = is(typeof((inout int=0) {
107 version(unittest_stream
)
110 static assert(isReadableStream
!File
);
111 static assert(isWriteableStream
!File
);
112 static assert(isRWStream
!File
);
113 static assert(isSeekableStream
!File
);
114 static assert(streamHasEOF
!File
);
115 static assert(streamHasSeek
!File
);
116 static assert(streamHasTell
!File
);
117 static assert(streamHasName
!File
);
118 static assert(streamHasSize
!File
);
120 static assert(!isReadableStream
!S
);
121 static assert(!isWriteableStream
!S
);
122 static assert(!isRWStream
!S
);
123 static assert(!isSeekableStream
!S
);
124 static assert(!streamHasEOF
!S
);
125 static assert(!streamHasSeek
!S
);
126 static assert(!streamHasTell
!S
);
127 static assert(!streamHasName
!S
);
128 static assert(!streamHasSize
!S
);
132 // ////////////////////////////////////////////////////////////////////////// //
133 T
[] rawReadExact(TF
, T
)(auto ref TF fl
, T
[] buf
)
134 if (isReadableStream
!TF
&& isMutable
!T
)
136 import std
.exception
: enforce
;
137 auto res
= fl
.rawRead(cast(void[])buf
);
138 enforce(res
.length
== T
.sizeof
*buf
.length
, "reading error");
143 // just for convience
144 void rawWriteExact(TF
, T
) (auto ref TF fl
, in T
[] buf
)
145 if (isWriteableStream
!TF
)
147 fl
.rawWrite(cast(void[])buf
);
151 // ////////////////////////////////////////////////////////////////////////// //
152 private enum goodEndianness(string s
) = (s
== "LE" || s
== "le" || s
== "BE" || s
== "be");
155 private template isLittleEndianness(string s
) if (goodEndianness
!s
) {
156 enum isLittleEndianness
= (s
== "LE" || s
== "le");
159 private template isSystemEndianness(string s
) if (goodEndianness
!s
) {
160 version(LittleEndian
) {
161 enum isSystemEndianness
= (s
== "LE" || s
== "le");
163 enum isSystemEndianness
= (s
== "BE" || s
== "be");
168 // ////////////////////////////////////////////////////////////////////////// //
169 // usage: write!ubyte(fl, 10)
170 void writeInt(TD
, string es
="LE", T
, TF
) (auto ref TF fl
, T n
) @trusted
171 if (goodEndianness
!es
&& isWriteableStream
!TF
&& __traits(isIntegral
, TD
) && __traits(isIntegral
, T
))
173 static assert(T
.sizeof
<= 8);
174 static assert(TD
.sizeof
<= 8);
175 static if (__traits(isUnsigned
, TD
)) {
177 static if (!__traits(isUnsigned
, T
)) {
178 if (n
< 0) throw new ConvOverflowException("writeInt overflow");
182 static if (!__traits(isUnsigned
, T
)) {
183 if (n
< TD
.min
) throw new ConvOverflowException("writeInt overflow");
186 if (n
> TD
.max
) throw new ConvOverflowException("writeInt overflow");
188 static if (isSystemEndianness
!es
) {
189 fl
.rawWriteExact((&v
)[0..1]);
191 ubyte[TD
.sizeof
] b
= void;
192 version(LittleEndian
) {
193 // convert to big-endian
194 foreach_reverse (immutable idx
; 0..TD
.sizeof
) {
199 // convert to little-endian
200 foreach (immutable idx
; 0..TD
.sizeof
) {
210 // usage: read!ubyte(fl)
211 T
readInt(T
, string es
="LE", TF
) (auto ref TF fl
) @trusted
212 if (goodEndianness
!es
&& isReadableStream
!TF
&& __traits(isIntegral
, T
))
214 static assert(T
.sizeof
<= 8);
215 static if (isSystemEndianness
!es
) {
217 fl
.rawReadExact((&v
)[0..1]);
220 ubyte[T
.sizeof
] b
= void;
223 version(LittleEndian
) {
224 // convert from big-endian
225 foreach (immutable idx
; 0..T
.sizeof
) {
230 // conver from little-endian
231 foreach_reverse (immutable idx
; 0..T
.sizeof
) {
241 private enum reverseBytesMixin
= "
242 foreach (idx; 0..b.length/2) {
244 b[idx] = b[b.length-idx-1];
245 b[b.length-idx-1] = t;
250 void writeFloat(TD
, string es
="LE", T
, TF
) (auto ref TF fl
, T n
) @trusted
251 if (goodEndianness
!es
&& isWriteableStream
!TF
&& __traits(isFloating
, TD
) && (__traits(isFloating
, T
) ||
__traits(isIntegral
, T
)))
253 static assert(TD
.sizeof
<= 8);
254 static if (__traits(isIntegral
, T
)) {
255 static assert(T
.sizeof
<= 8);
256 writeFloat
!(TD
, TD
)(fl
, cast(TD
)n
);
257 } else static if (__traits(isFloating
, T
)) {
258 static assert(T
.sizeof
<= 8);
260 static if (isSystemEndianness
!es
) {
261 fl
.rawWriteExact((&v
)[0..1]);
263 import core
.stdc
.string
: memcpy
;
264 ubyte[TD
.sizeof
] b
= void;
265 memcpy(b
.ptr
, &v
, TD
.sizeof
);
266 mixin(reverseBytesMixin
);
275 TD
readFloat(TD
, string es
="LE", TF
) (auto ref TF fl
) @trusted
276 if (goodEndianness
!es
&& isReadableStream
!TF
&& __traits(isFloating
, TD
))
278 static assert(TD
.sizeof
<= 8);
280 static if (isSystemEndianness
!es
) {
281 fl
.rawReadExact((&v
)[0..1]);
283 import core
.stdc
.string
: memcpy
;
284 ubyte[TD
.sizeof
] b
= void;
286 mixin(reverseBytesMixin
);
287 memcpy(&v
, b
.ptr
, TD
.sizeof
);
293 void writeNum(TD
, string es
="LE", T
, TF
) (auto ref TF fl
, T n
) @trusted if (__traits(isIntegral
, TD
)) { writeInt
!(TD
, es
, T
, TF
)(fl
, n
); }
294 void writeNum(TD
, string es
="LE", T
, TF
) (auto ref TF fl
, T n
) @trusted if (__traits(isFloating
, TD
)) { writeFloat
!(TD
, es
, T
, TF
)(fl
, n
); }
296 TD
readNum(TD
, string es
="LE", TF
) (auto ref TF fl
) @trusted if (__traits(isIntegral
, TD
)) { return readInt
!(TD
, es
, TF
)(fl
); }
297 TD
readNum(TD
, string es
="LE", TF
) (auto ref TF fl
) @trusted if (__traits(isFloating
, TD
)) { return readFloat
!(TD
, es
, TF
)(fl
); }
300 // ////////////////////////////////////////////////////////////////////////// //
301 void writeVULong(TF
) (auto ref TF fl
, ulong v
) if (isWriteableStream
!TF
) {
302 ubyte[16] buf
= void; // actually, 10 is enough ;-)
303 usize pos
= 1; // anyway
304 // now write as signed
305 if (v
== 0x8000_0000_0000_0000uL) {
306 // special (negative zero)
309 if (v
&0x8000_0000_0000_0000uL) {
310 v
= (v^
~0uL)+1; // negate v
317 if (v
> 0) buf
[0] |
= 0x40;
321 if (v
> 0) buf
[pos
] |
= 0x80;
325 fl
.rawWriteExact(buf
[0..pos
]);
329 ulong readVULong(TF
) (auto ref TF fl
) @trusted if (isWriteableStream
!TF
) {
332 // first byte contains sign flag
334 if (c
[0] == 0x80) return 0x8000_0000_0000_0000uL; // special (negative zero)
335 bool neg = (c
[0]&0x80) != 0;
338 // 63/7 == 9, so we can shift at most 56==(7*8) bits
341 if (shift
> 62) throw new ConvOverflowException("readVULong overflow");
344 if (shift
== 62 && n
> 1) throw new ConvOverflowException("readVULong overflow");
349 if (neg) v
= (v^
~0uL)+1; // negate v
354 // write variable-length signed integer
355 void writeVInt(T
, TF
) (auto ref TF fl
, T n
) @trusted if (isWriteableStream
!TF
&& __traits(isIntegral
, T
)) {
356 static assert(T
.sizeof
<= 8);
357 static if (__traits(isUnsigned
, T
)) {
358 // output type is unsigned
361 // output type is signed
362 writeVULong(fl
, cast(ulong)(cast(long)n
));
367 // read variable-length integer
368 T
readVInt(T
, TF
) (auto ref TF fl
) @trusted if (isReadableStream
!TF
&& __traits(isIntegral
, T
)) {
369 static assert(T
.sizeof
<= 8);
370 ulong v
= readVULong(fl
);
371 static if (__traits(isUnsigned
, T
)) {
372 // output type is unsigned
373 static if (!is(T
== ulong)) {
374 if (v
> T
.max
) throw new ConvOverflowException("readVInt overflow");
377 // output type is signed
378 static if (!is(T
== long)) {
379 if (cast(long)v
< T
.min ||
cast(long)v
> T
.max
) throw new ConvOverflowException("readVInt overflow");
386 // ////////////////////////////////////////////////////////////////////////// //
388 string
readZString(TF
) (auto ref TF fl
, bool* eolhit
=null, usize maxSize
=1024*1024) @trusted if (isReadableStream
!TF
) {
389 import std
.array
: appender
;
391 if (eolhit
is null) eolhit
= &eh
;
393 if (maxSize
== 0) return null;
394 auto res
= appender
!string();
396 ubyte ch
= fl
.readNum
!ubyte();
397 if (ch
== 0) { *eolhit
= true; break; }
398 if (maxSize
== 0) break;
399 res
.put(cast(char)ch
);
406 // ////////////////////////////////////////////////////////////////////////// //
408 // eolhit will be set on EOF too
409 string
readLine(TF
) (auto ref TF fl
, bool* eolhit
=null, usize maxSize
=1024*1024) @trusted if (isReadableStream
!TF
) {
410 import std
.array
: appender
;
412 if (eolhit
is null) eolhit
= &eh
;
414 if (maxSize
== 0) return null;
415 auto res
= appender
!string();
417 static if (streamHasEOF
!TF
) if (fl
.eof
) { *eolhit
= true; break; }
418 ubyte ch
= fl
.readNum
!ubyte();
420 static if (streamHasEOF
!TF
) if (fl
.eof
) { *eolhit
= true; break; }
421 ch
= fl
.readNum
!ubyte();
422 if (ch
== '\n') { *eolhit
= true; break; }
423 if (maxSize
== 0) break;
425 } else if (ch
== '\n') {
429 if (maxSize
== 0) break;
430 res
.put(cast(char)ch
);
437 // ////////////////////////////////////////////////////////////////////////// //
438 public struct MemoryStream
{
440 import core
.stdc
.stdio
: SEEK_SET
, SEEK_CUR
, SEEK_END
;
446 @property ubyte[] bytes () @safe pure nothrow @nogc { return data
; }
448 @property uint size () const @safe pure nothrow @nogc { return cast(uint)data
.length
; } //FIXME: x86_64
449 @property uint tell () const @safe pure nothrow @nogc { return curpos
; }
451 //TODO: check for overflow
452 void seek (long offset
, int origin
=SEEK_SET
) @trusted {
453 if (origin
== SEEK_CUR
) {
455 } else if (origin
== SEEK_END
) {
456 offset
= cast(long)data
.length
+offset
;
458 if (offset
< 0 || offset
> data
.length
) throw new StreamException("invalid offset");
459 curpos
= cast(uint)offset
;
462 T
[] rawRead(T
)(T
[] buf
) @trusted nothrow @nogc if (isMutable
!T
) {
463 if (buf
.length
> 0) {
464 //TODO: check for overflow
465 usize rlen
= (data
.length
-curpos
)/T
.sizeof
;
466 if (rlen
> buf
.length
) rlen
= buf
.length
;
468 import core
.stdc
.string
: memcpy
;
469 auto src
= cast(const(ubyte)*)data
.ptr
;
470 auto dest
= cast(ubyte*)buf
.ptr
;
471 memcpy(cast(ubyte*)buf
.ptr
, data
.ptr
+curpos
, rlen
*T
.sizeof
);
472 curpos
+= rlen
*T
.sizeof
;
480 void rawWrite(T
) (in T
[] buf
) @trusted nothrow {
481 if (buf
.length
!= 0) {
482 //TODO: check for overflow
483 import core
.stdc
.string
: memcpy
;
485 usize bsz
= T
.sizeof
*buf
.length
;
486 usize nsz
= curpos
+bsz
;
487 if (nsz
> data
.length
) data
.length
= nsz
;
489 memcpy(data
.ptr
+curpos
, cast(const(void*))buf
.ptr
, bsz
);
494 @property bool eof () const @trusted pure nothrow @nogc { return (curpos
>= data
.length
); }
496 void close () @safe pure nothrow @nogc {
503 static assert(isReadableStream
!MemoryStream
);
504 static assert(isWriteableStream
!MemoryStream
);
505 static assert(isRWStream
!MemoryStream
);
506 static assert(isSeekableStream
!MemoryStream
);
507 static assert(isClosableStream
!MemoryStream
);
508 static assert(streamHasEOF
!MemoryStream
);
509 static assert(streamHasSeek
!MemoryStream
);
510 static assert(streamHasTell
!MemoryStream
);
513 // ////////////////////////////////////////////////////////////////////////// //
514 public struct MemoryStreamRO
{
516 import core
.stdc
.stdio
: SEEK_SET
, SEEK_CUR
, SEEK_END
;
522 this (const(void)[] adata
) @trusted nothrow @nogc {
523 data
= cast(typeof(data
))adata
;
526 @property const(ubyte)[] bytes () @safe pure nothrow @nogc { return data
; }
528 @property uint size () const @safe pure nothrow @nogc { return cast(uint)data
.length
; } //FIXME: x86_64
529 @property uint tell () const @safe pure nothrow @nogc { return curpos
; }
531 //TODO: check for overflow
532 void seek (long offset
, int origin
=SEEK_SET
) @trusted {
533 if (origin
== SEEK_CUR
) {
535 } else if (origin
== SEEK_END
) {
536 offset
= cast(long)data
.length
+offset
;
538 if (offset
< 0 || offset
> data
.length
) throw new StreamException("invalid offset");
539 curpos
= cast(uint)offset
;
542 T
[] rawRead(T
)(T
[] buf
) @trusted nothrow @nogc if (isMutable
!T
) {
543 if (buf
.length
> 0) {
544 //TODO: check for overflow
545 usize rlen
= (data
.length
-curpos
)/T
.sizeof
;
546 if (rlen
> buf
.length
) rlen
= buf
.length
;
548 import core
.stdc
.string
: memcpy
;
549 auto src
= cast(const(ubyte)*)data
.ptr
;
550 auto dest
= cast(ubyte*)buf
.ptr
;
551 memcpy(cast(ubyte*)buf
.ptr
, data
.ptr
+curpos
, rlen
*T
.sizeof
);
552 curpos
+= rlen
*T
.sizeof
;
560 @property bool eof () const @trusted pure nothrow @nogc { return (curpos
>= data
.length
); }
562 void close () @safe pure nothrow @nogc {
569 static assert(isReadableStream
!MemoryStreamRO
);
570 static assert(!isWriteableStream
!MemoryStreamRO
);
571 static assert(!isRWStream
!MemoryStreamRO
);
572 static assert(isSeekableStream
!MemoryStreamRO
);
573 static assert(isClosableStream
!MemoryStreamRO
);
574 static assert(streamHasEOF
!MemoryStreamRO
);
575 static assert(streamHasSeek
!MemoryStreamRO
);
576 static assert(streamHasTell
!MemoryStreamRO
);
577 static assert(streamHasSize
!MemoryStreamRO
);
580 // ////////////////////////////////////////////////////////////////////////// //
581 version(unittest_stream
) {
584 private void dump (const(ubyte)[] data
, File fl
=stdout
) @trusted {
585 for (usize ofs
= 0; ofs
< data
.length
; ofs
+= 16) {
586 writef("%04X:", ofs
);
587 foreach (immutable i
; 0..16) {
588 if (i
== 8) write(' ');
589 if (ofs
+i
< data
.length
) writef(" %02X", data
[ofs
+i
]); else write(" ");
592 foreach (immutable i
; 0..16) {
593 if (ofs
+i
>= data
.length
) break;
594 if (i
== 8) write(' ');
595 ubyte b
= data
[ofs
+i
];
596 if (b
<= 32 || b
>= 127) write('.'); else writef("%c", cast(char)b
);
604 version(unittest_stream
)
607 auto ms
= new MemoryStream();
608 ms
.rawWrite(cast(ubyte[])"hello");
609 assert(ms
.data
== cast(ubyte[])"hello");
613 assert(ms
.rawRead(d
).length
== 2);
614 assert(d
== [0x6568, 0x6c6c, 0]);
615 //dump(cast(ubyte[])d);
618 auto ms
= new MemoryStream();
619 wchar[] a
= ['\u0401', '\u0280', '\u089e'];
624 auto ms
= /*new*/ MemoryStreamRO(cast(const(void)[])"hello");
625 assert(ms
.data
== cast(ubyte[])"hello");
630 version(unittest_stream
)
632 import std
.exception
: enforce
;
635 auto fl
= new MemoryStream();
640 fl
.writeVInt(128000);
641 fl
.writeVInt(-32000);
644 assert(fl
.data
== [0x01,0x81,0x40,0xD0,0x0F,0xC0,0xF4,0x03]);
648 assert(fl
.readVInt
!byte() == 1);
649 assert(fl
.readVInt
!byte() == -1);
650 assert(fl
.readVInt
!int() == 128000);
651 assert(fl
.readVInt
!int() == -32000);
652 assert(fl
.tell() == fl
.size
);
653 } catch (StreamException
) {
654 enforce(false, "FUCK! RANGE!");
659 assert(fl
.readVInt
!byte() == 1);
660 assert(fl
.readVInt
!byte() == -1);
661 assert(fl
.readVInt
!int() == 128000);
662 assert(fl
.readVInt
!short() == -32000);
663 assert(fl
.tell() == fl
.size
);
664 } catch (StreamException
) {
665 enforce(false, "FUCK! RANGE!");
671 assert(fl
.readVInt
!byte() == 1);
673 assert(fl
.readVInt
!byte() == -1);
675 assert(fl
.readVInt
!byte() == 128000);
677 assert(fl
.readVInt
!byte() == -32000);
679 assert(fl
.tell() == fl
.size
);
681 enforce(false, "SHIT!");
682 } catch (Exception
) {
683 enforce(cnt
== 2, "FUCK RANGE");
689 assert(fl
.readVInt
!byte() == 1);
691 assert(fl
.readVInt
!ubyte() == -1);
693 assert(fl
.readVInt
!byte() == 128000);
695 assert(fl
.readVInt
!byte() == -32000);
697 assert(fl
.tell() == fl
.size
);
699 enforce(false, "SHIT!");
700 } catch (Exception
) {
701 enforce(cnt
== 1, "FUCK RANGE");
707 assert(fl
.readVInt
!byte() == 1);
709 assert(fl
.readVInt
!byte() == -1);
711 assert(fl
.readVInt
!int() == 128000);
713 assert(fl
.readVInt
!ushort() == -32000);
715 assert(fl
.tell() == fl
.size
);
717 enforce(false, "SHIT!");
718 } catch (Exception
) {
719 enforce(cnt
== 3, "FUCK RANGE");
723 version(LittleEndian
) {
725 auto fl
= new MemoryStream();
727 fl
.writeInt
!(long, "BE")(1);
729 fl
.writeNum
!long(-2);
730 fl
.writeNum
!(long, "BE")(1);
731 fl
.writeNum
!(long, "BE")(-2);
732 fl
.writeNum
!float(1.0f);
733 fl
.writeNum
!(float, "BE")(1.0f);
736 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
737 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
738 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
739 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
740 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
741 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
742 0x00,0x00,0x80,0x3F, 0x3F,0x80,0x00,0x00,
746 assert(fl
.readInt
!long() == 1);
747 assert(fl
.readInt
!(long, "BE")() == 1);
748 assert(fl
.readNum
!long() == 1);
749 assert(fl
.readNum
!long() == -2);
750 assert(fl
.readNum
!(long, "BE")() == 1);
751 assert(fl
.readNum
!(long, "BE")() == -2);
752 assert(fl
.readNum
!float() == 1.0f);
753 assert(fl
.readNum
!(float, "BE")() == 1.0f);
754 assert(fl
.tell() == fl
.size
);
755 //writeln("IO: done");
760 auto fl
= new MemoryStream();
761 fl
.rawWrite("loves\nalice\n");
764 assert(fl
.readLine(&eol
) == "loves" && eol
== true && !fl
.eof
);
765 assert(fl
.readLine(&eol
) == "alice" && eol
== true && fl
.eof
);
769 auto fl
= new MemoryStream();
770 fl
.rawWrite("loves\0alice\0");
773 assert(fl
.readZString(&eol
) == "loves" && eol
== true && !fl
.eof
);
774 assert(fl
.readZString(&eol
) == "alice" && eol
== true && fl
.eof
);
779 // ////////////////////////////////////////////////////////////////////////// //
780 private class PartialStreamROData
{
783 immutable bool gcrange
; // do `GC.removeRange()`?
784 immutable long start
;
786 long curpos
; // current position
787 usize gcroot
; // allocated memory that must be free()d
789 this (long astart
, long asize
, usize agcroot
, bool arange
) @safe nothrow @nogc
799 //{ import std.stdio; stderr.writefln("0x%08x: ctor, rc=%s", gcroot, rc); }
802 // this should never be called
803 ~this () @safe nothrow @nogc {
804 if (rc
!= 0) assert(0); // the thing that should not be
805 assert(0); // why we are here?!
808 void incRef () @safe nothrow @nogc {
809 if (++rc
== 0) assert(0); // hey, this is definitely a bug!
810 //{ import std.stdio; stderr.writefln("0x%08x: incRef, rc=%s", gcroot, rc); }
813 // return true if this class is dead
815 if (rc
-- == 0) assert(0); // hey, this is definitely a bug!
816 //{ import std.stdio; stderr.writefln("0x%08x: decRef, rc=%s", gcroot, rc); }
818 import core
.memory
: GC
;
819 import core
.stdc
.stdlib
: free
;
820 clear(); // finalize stream
821 if (gcroot
== 0) assert(0); // the thing that should not be
824 GC
.removeRange(cast(void*)gcroot
);
825 GC
.removeRoot(cast(void*)gcroot
);
827 // free allocated memory
828 free(cast(void*)gcroot
);
830 //{ import std.stdio; stderr.writefln("0x%08x: dead, rc=%s", gcroot, rc); }
839 abstract void clear ();
840 abstract void[] read (void[] buf
);
844 private final class PartialStreamDataImpl(ST
) : PartialStreamROData
{
847 this(ST
) (auto ref ST astrm
, long astart
, long asize
, usize agcroot
, bool arange
) {
848 super(astart
, asize
, agcroot
, arange
);
853 override void clear () { stream
= stream
.init
; }
855 override void[] read (void[] buf
) {
856 assert(curpos
>= 0 && curpos
<= size
);
857 usize len
= buf
.length
;
858 if (len
> size
-curpos
) len
= cast(usize
)(size
-curpos
);
860 stream
.seek(start
+curpos
);
861 auto res
= stream
.rawRead(buf
[0..len
]);
862 curpos
+= res
.length
;
871 public struct PartialStreamRO
{
873 PartialStreamROData mStData
;
875 void initialize(ST
) (auto ref ST astrm
, long astart
, long asize
)
876 if (isReadableStream
!ST
&& isSeekableStream
!ST
)
878 if (astart
< 0) astart
= astrm
.tell
;
879 if (astart
< 0) throw new StreamException("invalid partial stream parameters");
881 astrm
.seek(0, SEEK_END
);
883 if (asize
< 0) throw new StreamException("invalid partial stream parameters");
884 if (astart
> asize
) throw new StreamException("invalid partial stream parameters");
888 // and now... rock-a-rolla!
890 // actually, we shouldn't use malloc() here, 'cause we can have alot of
891 // free memory in GC and no memory for malloc(), but... let's be realistic:
892 // we aren't aiming at constrained systems
893 import core
.exception
: onOutOfMemoryError
;
894 import core
.memory
: GC
;
895 import core
.stdc
.stdlib
: malloc
;
896 import std
.conv
: emplace
;
897 import std
.traits
: hasIndirections
;
898 alias CT
= PartialStreamDataImpl
!ST
; // i'm lazy
899 enum instSize
= __traits(classInstanceSize
, CT
);
900 // let's hope that malloc() aligns returned memory right
901 auto mem
= malloc(instSize
);
902 if (mem
is null) onOutOfMemoryError(); // oops
903 usize root
= cast(usize
)mem
;
904 static if (hasIndirections
!ST
) {
905 // ouch, ST has some pointers; register it as gc root and range
906 // note that this approach is very simplictic; we might want to
907 // scan the type for pointers using typeinfo pointer bitmap and
908 // register only pointer containing areas.
909 GC
.addRoot(cast(void*)root
);
910 GC
.addRange(cast(void*)root
, instSize
);
915 mStData
= emplace
!CT(mem
[0..instSize
], astrm
, astart
, asize
, root
, isrng
);
920 import core
.stdc
.stdio
: SEEK_SET
, SEEK_CUR
, SEEK_END
;
922 immutable string name
;
924 // ST must support copying!
925 this(ST
) (string aname
, auto ref ST astrm
, long astart
=-1, long asize
=-1)
926 if (isReadableStream
!ST
&& isSeekableStream
!ST
)
928 initialize(astrm
, astart
, asize
);
932 this(ST
) (auto ref ST astrm
, long astart
=-1, long asize
=-1)
933 if (isReadableStream
!ST
&& isSeekableStream
!ST
)
935 initialize(astrm
, astart
, asize
);
939 this (this) @safe nothrow @nogc { if (isOpen
) mStData
.incRef(); }
940 ~this () { close(); }
942 void opAssign() (auto ref PartialStreamRO src
) {
944 // assigning to opened stream
946 // both streams are opened
947 // we don't care if internal streams are different, our rc scheme will took care of this
948 auto old
= mStData
; // decRef() can throw, so be on the safe side
949 mStData
= src
.mStData
;
950 mStData
.incRef(); // this can't throw
951 old
.decRef(); // release old stream
953 // just close this one
956 } else if (src
.isOpen
) {
957 // this stream is closed, but other is open; easy deal
958 mStData
= src
.mStData
;
963 @property bool isOpen () const pure @safe nothrow @nogc { return (mStData
!is null); }
972 @property long stofs () const @safe pure nothrow @nogc { return (isOpen ? mStData
.start
: 0); }
973 @property long tell () const @safe pure nothrow @nogc { return (isOpen ? mStData
.curpos
: 0); }
974 @property long size () const @safe pure nothrow @nogc { return (isOpen ? mStData
.size
: 0); }
975 @property bool eof () const @trusted pure nothrow @nogc { return (isOpen ? mStData
.curpos
>= mStData
.size
: true); }
977 //TODO: check for overflow
978 void seek (long offset
, int origin
=SEEK_SET
) @trusted {
979 if (!isOpen
) throw new StreamException("can't seek in closed partial stream");
980 if (origin
== SEEK_CUR
) {
981 offset
+= mStData
.curpos
;
982 } else if (origin
== SEEK_END
) {
983 offset
= mStData
.size
+offset
;
985 if (offset
< 0 || offset
> mStData
.size
) throw new StreamException("invalid offset");
986 mStData
.curpos
= offset
;
989 T
[] rawRead(T
)(T
[] buf
) @trusted if (isMutable
!T
) {
990 if (!isOpen
) throw new StreamException("can't read from closed partial stream");
991 if (buf
.length
> 0) {
992 auto res
= mStData
.read(cast(void[])buf
);
993 return buf
[0..res
.length
/T
.sizeof
];
1001 static assert(isReadableStream
!PartialStreamRO
);
1002 static assert(!isWriteableStream
!PartialStreamRO
);
1003 static assert(!isRWStream
!PartialStreamRO
);
1004 static assert(isSeekableStream
!PartialStreamRO
);
1005 static assert(isClosableStream
!PartialStreamRO
);
1006 static assert(streamHasEOF
!PartialStreamRO
);
1007 static assert(streamHasSeek
!PartialStreamRO
);
1008 static assert(streamHasTell
!PartialStreamRO
);
1011 version(unittest_stream
)
1013 void rwc(T
) (T stream
, long pos
, char ch
) {
1016 auto r
= t
.rawRead(b
);
1017 assert(r
.length
== b
.length
);
1021 auto ms
= new MemoryStream();
1022 ms
.rawWrite(cast(void[])"test");
1023 auto t
= PartialStreamRO(ms
, 1);
1027 auto r
= t
.rawRead(b
);
1028 assert(r
.length
== b
.length
);
1029 assert(b
[0] == 's');
1037 // ////////////////////////////////////////////////////////////////////////// //
1038 // turn streams to ranges
1039 // rngtype can be: "any", "read", "write"
1040 // you can add ",indexable" to rngtype to include `opIndex()`
1041 auto streamAsRange(string rngtype
="any", STP
) (auto ref STP st
) if (isReadableStream
!STP || isWriteableStream|STP
) {
1048 template ParseType (string s
) {
1049 private static string
get (string
str) {
1051 while (spos
< str.length
&& str[spos
] <= ' ') ++spos
;
1053 while (epos
< str.length
&& str[epos
] != ',') ++epos
;
1054 while (epos
> 0 && str[epos
-1] <= ' ') --epos
;
1055 return str[spos
..epos
];
1057 private static string
skip (string
str) {
1059 while (spos
< str.length
&& str[spos
] != ',') ++spos
;
1060 if (spos
< str.length
) ++spos
;
1061 while (spos
< str.length
&& str[spos
] <= ' ') ++spos
;
1062 return str[spos
..$];
1064 private ubyte parse (string
str) {
1066 while (str.length
> 0) {
1069 case "read": has |
= HasR
; break;
1070 case "write": has |
= HasW
; break;
1071 case "any": has |
= HasR|HasW
; break;
1072 case "indexable": has |
= HasI
; break;
1074 foreach (immutable char ch
; w
) {
1076 case 'r': has |
= HasR
; break;
1077 case 'w': has |
= HasW
; break;
1078 case 'i': has |
= HasI
; break;
1079 default: assert(0, "invalid mode word: '"~w
~"'");
1086 if (has
== 0) has
= HasR|HasW
; // any
1089 enum ParseType
= parse(s
);
1092 enum typeflags
= ParseType
!(rngtype
);
1093 // setup stream type
1094 static if ((typeflags
&HasRW
) == HasRW
) {
1095 enum rdStream
= isReadableStream
!STP
;
1096 enum wrStream
= isWriteableStream
!STP
;
1097 } else static if (typeflags
&HasR
) {
1098 static assert(isReadableStream
!STP
, "stream must be readable");
1099 enum rdStream
= isReadableStream
!STP
;
1100 enum wrStream
= false;
1101 } else static if (typeflags
&HasW
) {
1102 static assert(isWriteableStream
!STP
, "stream must be writeable");
1103 enum rdStream
= false;
1104 enum wrStream
= isWriteableStream
!STP
;
1106 static assert(0, "invalid range type: "~rngtype
);
1109 import core
.stdc
.stdio
: SEEK_SET
, SEEK_CUR
, SEEK_END
;
1111 static struct StreamRange(ST
) {
1114 static if (rdStream
) {
1119 this(STX
) (auto ref STX ast
) {
1121 static if (rdStream
) {
1123 // catch errors here, as `std.stdio.File` throws exception on reading from "w" files
1125 auto rd
= strm
.rawRead(curByte
);
1126 if (rd
.length
!= 0) atEof
= false;
1127 } catch (Exception
) {}
1132 // output range part
1133 static if (wrStream
) {
1135 void put (in ubyte data
) { strm
.rawWriteExact((&data
)[0..1]); }
1136 void put (in ubyte[] data
...) { strm
.rawWriteExact(data
); }
1140 static if (rdStream
) {
1142 @property bool empty () const pure @safe nothrow @nogc { return atEof
; }
1145 static if (streamHasTell
!ST
&& (streamHasSeek
!ST || streamHasSize
!ST
)) {
1146 private enum hasRealLength
= true;
1147 @property usize
length() () {
1148 immutable cpos
= strm
.tell
;
1149 static if (streamHasSize
!ST
) {
1150 immutable sz
= strm
.size
;
1152 strm
.seek(0, SEEK_END
);
1153 immutable sz
= strm
.tell
;
1154 strm
.seek(cpos
, SEEK_SET
);
1156 if (cpos
>= sz
) return 0;
1157 immutable len
= sz
-cpos
;
1158 if (len
> usize
.max
) {
1159 import core
.exception
: onRangeError
;
1162 return cast(usize
)len
;
1165 private enum hasRealLength
= false;
1169 @property ubyte front () const pure @safe nothrow @nogc { return curByte
[0]; }
1172 void popFront() () {
1174 if (!atEof
&& strm
.rawRead(curByte
[]).length
== 0) atEof
= true;
1178 // it's slow and unreliable
1179 static if ((typeflags
&HasI
) && streamHasTell
!ST
&& streamHasSeek
!ST
&& hasRealLength
) {
1180 ubyte opIndex() (usize pos
) {
1181 import core
.exception
: onRangeError
;
1182 if (pos
>= this.length
) onRangeError();
1183 immutable cpos
= strm
.tell
;
1184 strm
.seek(pos
, SEEK_CUR
);
1186 strm
.rawReadExact(res
[]);
1187 strm
.seek(cpos
, SEEK_SET
);
1194 return StreamRange
!STP(st
);