egra: added slightly faster sorter to agg mini
[iv.d.git] / vfs / pred.d
blobaadbddd9259b8b46b6fb62391991d121e6e0f2c1
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 // stream predicates
18 module iv.vfs.pred /*is aliced*/;
20 public import iv.alice;
21 public import iv.vfs.types : Seek;
22 public import iv.vfs.error;
23 public import iv.vfs.vfile : IVVFSIgnore;
26 // ////////////////////////////////////////////////////////////////////////// //
27 /// is this "low-level" stream that can be read?
28 enum isLowLevelStreamR(T) = is(typeof((inout int=0) {
29 auto t = T.init;
30 ubyte[1] b;
31 ssize r = t.read(b.ptr, 1);
32 }));
34 /// is this "low-level" stream that can be written?
35 enum isLowLevelStreamW(T) = is(typeof((inout int=0) {
36 auto t = T.init;
37 ubyte[1] b;
38 ssize w = t.write(b.ptr, 1);
39 }));
42 /// is this "low-level" stream that can be seeked?
43 enum isLowLevelStreamS(T) = is(typeof((inout int=0) {
44 auto t = T.init;
45 long p = t.lseek(0, 0);
46 }));
49 // ////////////////////////////////////////////////////////////////////////// //
50 /// check if a given stream supports `eof`
51 enum streamHasEof(T) = is(typeof((inout int=0) {
52 auto t = T.init;
53 bool n = t.eof;
54 }));
56 /// check if a given stream supports `seek`
57 enum streamHasSeek(T) = is(typeof((inout int=0) {
58 import core.stdc.stdio : SEEK_END;
59 auto t = T.init;
60 t.seek(0);
61 t.seek(0, SEEK_END);
62 }));
64 /// check if a given stream supports `tell`
65 enum streamHasTell(T) = is(typeof((inout int=0) {
66 auto t = T.init;
67 long pos = t.tell;
68 }));
70 /// check if a given stream supports `tell`
71 enum streamHasClose(T) = is(typeof((inout int=0) {
72 auto t = T.init;
73 t.close();
74 }));
76 /// check if a given stream supports `name`
77 enum streamHasName(T) = is(typeof((inout int=0) {
78 auto t = T.init;
79 const(char)[] n = t.name;
80 }));
82 /// check if a given stream supports `size`
83 enum streamHasSize(T) = is(typeof((inout int=0) {
84 auto t = T.init;
85 long pos = t.size;
86 }));
88 /// check if a given stream supports `size`
89 enum streamHasSizeLowLevel(T) = is(typeof((inout int=0) {
90 auto t = T.init;
91 long pos = t.getsize;
92 }));
94 /// check if a given stream supports `isOpen`
95 enum streamHasIsOpen(T) = is(typeof((inout int=0) {
96 auto t = T.init;
97 bool op = t.isOpen;
98 }));
100 /// check if a given stream supports `flush()`
101 enum streamHasFlush(T) = is(typeof((inout int=0) {
102 auto t = T.init;
103 t.flush();
104 }));
106 // ////////////////////////////////////////////////////////////////////////// //
107 /// check if a given stream supports `rawRead()`.
108 /// it's enough to support `void[] rawRead (void[] buf)`
109 enum isReadableStream(T) = is(typeof((inout int=0) {
110 auto t = T.init;
111 ubyte[1] b;
112 auto v = cast(void[])b;
113 t.rawRead(v);
114 }));
116 /// check if a given stream supports `rawWrite()`.
117 /// it's enough to support `inout(void)[] rawWrite (inout(void)[] buf)`
118 enum isWriteableStream(T) = is(typeof((inout int=0) {
119 auto t = T.init;
120 ubyte[1] b;
121 t.rawWrite(cast(void[])b);
122 }));
124 /// check if a given stream supports both reading and writing
125 enum isRWStream(T) = isReadableStream!T && isWriteableStream!T;
127 /// check if a given stream supports both reading and writing
128 enum isRorWStream(T) = isReadableStream!T || isWriteableStream!T;
130 /// check if a given stream supports `.seek(ofs, [whence])`, and `.tell`
131 enum isSeekableStream(T) = (streamHasSeek!T && streamHasTell!T);
133 /// check if we can get size of a given stream.
134 /// this can be done either with `.size`, or with `.seek` and `.tell`
135 enum isSizedStream(T) = (streamHasSize!T || isSeekableStream!T);
138 // ////////////////////////////////////////////////////////////////////////// //
139 version(vfs_test_stream) {
140 import std.stdio;
141 static assert(isReadableStream!File);
142 static assert(isWriteableStream!File);
143 static assert(isRWStream!File);
144 static assert(isSeekableStream!File);
145 static assert(streamHasEof!File);
146 static assert(streamHasSeek!File);
147 static assert(streamHasTell!File);
148 static assert(streamHasName!File);
149 static assert(streamHasSize!File);
150 struct S {}
151 static assert(!isReadableStream!S);
152 static assert(!isWriteableStream!S);
153 static assert(!isRWStream!S);
154 static assert(!isSeekableStream!S);
155 static assert(!streamHasEof!S);
156 static assert(!streamHasSeek!S);
157 static assert(!streamHasTell!S);
158 static assert(!streamHasName!S);
159 static assert(!streamHasSize!S);
163 // ////////////////////////////////////////////////////////////////////////// //
164 /// augment low-level streams with `rawRead`
165 T[] rawRead(ST, T) (auto ref ST st, T[] buf) if (isLowLevelStreamR!ST && !is(T == const) && !is(T == immutable)) {
166 if (buf.length > 0) {
167 auto res = st.read(buf.ptr, buf.length*T.sizeof);
168 if (res == -1 || res%T.sizeof != 0) throw new VFSException("read error");
169 return buf[0..res/T.sizeof];
170 } else {
171 return buf[0..0];
175 /// augment low-level streams with `rawWrite`
176 void rawWrite(ST, T) (auto ref ST st, in T[] buf) if (isLowLevelStreamW!ST) {
177 if (buf.length > 0) {
178 auto res = st.write(buf.ptr, buf.length*T.sizeof);
179 if (res == -1 || res%T.sizeof != 0) throw new VFSException("write error");
183 /// read exact size or throw error
184 T[] rawReadExact(ST, T) (auto ref ST st, T[] buf) if (isReadableStream!ST && !is(T == const) && !is(T == immutable)) {
185 if (buf.length == 0) return buf;
186 auto left = buf.length*T.sizeof;
187 auto dp = cast(ubyte*)buf.ptr;
188 while (left > 0) {
189 auto res = st.rawRead(cast(void[])(dp[0..left]));
190 if (res.length == 0) throw new VFSException("read error");
191 dp += res.length;
192 left -= res.length;
194 return buf;
197 /// write exact size or throw error (just for convenience)
198 void rawWriteExact(ST, T) (auto ref ST st, in T[] buf) if (isWriteableStream!ST) { st.rawWrite(buf); }
200 /// if stream doesn't have `.size`, but can be seeked, emulate it
201 long size(ST) (auto ref ST st) if (isSeekableStream!ST && !streamHasSize!ST) {
202 auto opos = st.tell;
203 st.seek(0, Seek.End);
204 auto res = st.tell;
205 st.seek(opos);
206 return res;
210 // ////////////////////////////////////////////////////////////////////////// //
211 public enum isGoodEndianness(string s) = (s == "LE" || s == "le" || s == "BE" || s == "be");
213 public template isLittleEndianness(string s) if (isGoodEndianness!s) {
214 enum isLittleEndianness = (s == "LE" || s == "le");
217 public template isBigEndianness(string s) if (isGoodEndianness!s) {
218 enum isLittleEndianness = (s == "BE" || s == "be");
221 public template isSystemEndianness(string s) if (isGoodEndianness!s) {
222 version(LittleEndian) {
223 enum isSystemEndianness = isLittleEndianness!s;
224 } else {
225 enum isSystemEndianness = isBigEndianness!s;
230 // ////////////////////////////////////////////////////////////////////////// //
231 /// write integer value of the given type, with the given endianness (default: little-endian)
232 /// usage: st.writeNum!ubyte(10)
233 void writeNum(T, string es="LE", ST) (auto ref ST st, T n) if (isGoodEndianness!es && isWriteableStream!ST && __traits(isIntegral, T)) {
234 static assert(T.sizeof <= 8); // just in case
235 static if (isSystemEndianness!es || T.sizeof == 1) {
236 st.rawWriteExact((&n)[0..1]);
237 } else {
238 ubyte[T.sizeof] b = void;
239 version(LittleEndian) {
240 // convert to big-endian
241 foreach_reverse (ref x; b) { x = n&0xff; n >>= 8; }
242 } else {
243 // convert to little-endian
244 foreach (ref x; b) { x = n&0xff; n >>= 8; }
246 st.rawWriteExact(b[]);
251 /// read integer value of the given type, with the given endianness (default: little-endian)
252 /// usage: auto v = st.readNum!ubyte
253 T readNum(T, string es="LE", ST) (auto ref ST st) if (isGoodEndianness!es && isReadableStream!ST && __traits(isIntegral, T)) {
254 static assert(T.sizeof <= 8); // just in case
255 static if (isSystemEndianness!es || T.sizeof == 1) {
256 T v = void;
257 st.rawReadExact((&v)[0..1]);
258 return v;
259 } else {
260 ubyte[T.sizeof] b = void;
261 st.rawReadExact(b[]);
262 T v = 0;
263 version(LittleEndian) {
264 // convert from big-endian
265 foreach (ubyte x; b) { v <<= 8; v |= x; }
266 } else {
267 // conver from little-endian
268 foreach_reverse (ubyte x; b) { v <<= 8; v |= x; }
270 return v;
275 private enum reverseBytesMixin = "
276 foreach (idx; 0..b.length/2) {
277 ubyte t = b[idx];
278 b[idx] = b[$-idx-1];
279 b[$-idx-1] = t;
284 /// write floating value of the given type, with the given endianness (default: little-endian)
285 /// usage: st.writeNum!float(10)
286 void writeNum(T, string es="LE", ST) (auto ref ST st, T n) if (isGoodEndianness!es && isWriteableStream!ST && __traits(isFloating, T)) {
287 static assert(T.sizeof <= 8);
288 static if (isSystemEndianness!es) {
289 st.rawWriteExact((&n)[0..1]);
290 } else {
291 import core.stdc.string : memcpy;
292 ubyte[T.sizeof] b = void;
293 memcpy(b.ptr, &v, T.sizeof);
294 mixin(reverseBytesMixin);
295 st.rawWriteExact(b[]);
300 /// read floating value of the given type, with the given endianness (default: little-endian)
301 /// usage: auto v = st.readNum!float
302 T readNum(T, string es="LE", ST) (auto ref ST st) if (isGoodEndianness!es && isReadableStream!ST && __traits(isFloating, T)) {
303 static assert(T.sizeof <= 8);
304 T v = void;
305 static if (isSystemEndianness!es) {
306 st.rawReadExact((&v)[0..1]);
307 } else {
308 import core.stdc.string : memcpy;
309 ubyte[T.sizeof] b = void;
310 st.rawReadExact(b[]);
311 mixin(reverseBytesMixin);
312 memcpy(&v, b.ptr, T.sizeof);
314 return v;
318 // ////////////////////////////////////////////////////////////////////////// //
319 // first byte: bit 7 is sign; bit 6 is "has more bytes" mark; bits 0..5: first number bits
320 // next bytes: bit 7 is "has more bytes" mark; bits 0..6: next number bits
321 void writeXInt(T : ulong, ST) (auto ref ST fl, T vv) if (isWriteableStream!ST) {
322 ubyte[16] buf = void; // actually, 10 is enough ;-)
323 static if (T.sizeof == ulong.sizeof) ulong v = cast(ulong)vv;
324 else static if (!__traits(isUnsigned, T)) ulong v = cast(ulong)cast(long)vv; // extend sign bits
325 else ulong v = cast(ulong)vv;
326 uint len = 1; // at least
327 // now write as signed
328 if (v == 0x8000_0000_0000_0000UL) {
329 // special (negative zero)
330 buf.ptr[0] = 0x80;
331 } else {
332 if (v&0x8000_0000_0000_0000UL) {
333 v = (v^~0uL)+1; // negate v
334 buf.ptr[0] = 0x80; // sign bit
335 } else {
336 buf.ptr[0] = 0;
338 buf.ptr[0] |= v&0x3f;
339 v >>= 6;
340 if (v != 0) buf.ptr[0] |= 0x40; // has more
341 while (v != 0) {
342 buf.ptr[len] = v&0x7f;
343 v >>= 7;
344 if (v > 0) buf.ptr[len] |= 0x80; // has more
345 ++len;
348 fl.rawWriteExact(buf.ptr[0..len]);
352 T readXInt(T : ulong, ST) (auto ref ST fl) if (isReadableStream!ST) {
353 import std.conv : ConvOverflowException;
354 ulong v = 0;
355 ubyte c = void;
356 // first byte contains sign flag
357 fl.rawReadExact((&c)[0..1]);
358 if (c == 0x80) {
359 // special (negative zero)
360 v = 0x8000_0000_0000_0000UL;
361 } else {
362 bool neg = ((c&0x80) != 0);
363 v = c&0x3f;
364 c <<= 1;
365 // 63/7 == 9, so we can shift at most 56==(7*8) bits
366 ubyte shift = 6;
367 while (c&0x80) {
368 if (shift > 62) throw new ConvOverflowException("readXInt overflow");
369 fl.rawReadExact((&c)[0..1]);
370 ulong n = c&0x7f;
371 if (shift == 62 && n > 1) throw new ConvOverflowException("readXInt overflow");
372 n <<= shift;
373 v |= n;
374 shift += 7;
376 if (neg) v = (v^~0uL)+1; // negate v
378 // now convert to output
379 static if (T.sizeof == v.sizeof) {
380 return v;
381 } else static if (!__traits(isUnsigned, T)) {
382 auto l = cast(long)v;
383 if (v < T.min) throw new ConvOverflowException("readXInt underflow");
384 if (v > T.max) throw new ConvOverflowException("readXInt overflow");
385 return cast(T)l;
386 } else {
387 if (v > T.max) throw new ConvOverflowException("readXInt overflow");
388 return cast(T)v;
393 // ////////////////////////////////////////////////////////////////////////// //
394 void readStruct(string es="LE", SS, ST) (auto ref ST fl, ref SS st)
395 if (is(SS == struct) && isGoodEndianness!es && isReadableStream!ST)
397 import iv.vfs.vfile : VFile;
398 static assert(!is(SS == VFile), "invalid argument order in `readStruct`");
399 void unserData(T) (ref T v) {
400 import std.traits : Unqual;
401 alias UT = Unqual!T;
402 static if (is(T : V[], V)) {
403 // array
404 static if (__traits(isStaticArray, T)) {
405 foreach (ref it; v) unserData(it);
406 } else static if (is(UT == char)) {
407 // special case: dynamic `char[]` array will be loaded as asciiz string
408 char c;
409 for (;;) {
410 if (fl.rawRead((&c)[0..1]).length == 0) break; // don't require trailing zero on eof
411 if (c == 0) break;
412 v ~= c;
414 } else {
415 assert(0, "cannot load dynamic arrays yet");
417 } else static if (is(T : V[K], K, V)) {
418 assert(0, "cannot load associative arrays yet");
419 } else static if (__traits(isIntegral, UT) || __traits(isFloating, UT)) {
420 // this takes care of `*char` and `bool` too
421 v = cast(UT)fl.readNum!(UT, es);
422 } else static if (is(T == struct)) {
423 // struct
424 import std.traits : FieldNameTuple, hasUDA;
425 foreach (string fldname; FieldNameTuple!T) {
426 static if (!hasUDA!(__traits(getMember, T, fldname), IVVFSIgnore)) {
427 unserData(__traits(getMember, v, fldname));
433 unserData(st);