flexlay2: respect maximum box size
[iv.d.git] / vfs / io.d
blobda2db5d0320449566d9da7363415374684e1c34f
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 /**
18 * wrap any low-level (or high-level) stream into refcounted struct.
19 * this struct can be used instead of `std.stdio.File` when you need
20 * a concrete type instead of working with generic stream templates.
21 * wrapped stream is thread-safe (i.e. reads, writes, etc), but
22 * wrapper itself isn't.
24 module iv.vfs.io /*is aliced*/;
25 private:
27 public import iv.vfs;
28 import iv.alice;
31 // ////////////////////////////////////////////////////////////////////////// //
32 // aaaaah, let's conflict with std.stdio!
33 public __gshared VFile stdin, stdout, stderr;
35 shared static this () {
36 debug(vfs_rc) { import core.stdc.stdio : printf; printf("******** SHARED CTOR FOR iv.vfs.io\n"); }
37 stdin = wrapStdin;
38 stdout = wrapStdout;
39 stderr = wrapStderr;
43 // ////////////////////////////////////////////////////////////////////////// //
44 public auto readTextFile(T=string) (const(char)[] fname, int maxlen=int.max/8)
45 if (is(T == string) || is(T == const(char)[]) || is(T == char[]) || is(T == const char[]))
47 return readTextFile(VFile(fname), maxlen);
51 //TODO: fallback to chunked reader if `fl.hasSize` is false
52 public auto readTextFile(T=string) (VFile fl, int maxlen=int.max/8)
53 if (is(T == string) || is(T == const(char)[]) || is(T == char[]) || is(T == const char[]) ||
54 is(T == immutable(ubyte)[]) || is(T == const(ubyte)[]) || is(T == ubyte[]) || is(T == const ubyte[]))
56 auto fpos = fl.tell;
57 auto flen = fl.size-fpos;
58 if (flen < 1) return cast(T)null;
59 if (flen > maxlen) throw new Exception("text file too big");
60 auto res = new char[](cast(int)flen);
61 scope(failure) delete res; // make GC life easier
62 fl.rawReadExact(res);
63 return cast(T)res; // it is safe to cast here
67 // ////////////////////////////////////////////////////////////////////////// //
68 /// read 0-terminated string from stream. very slow, no recoding.
69 /// eolhit will be set on EOF too.
70 public string readZString(ST) (auto ref ST fl, bool* eolhit=null, usize maxSize=1024*1024) if (isReadableStream!ST) {
71 bool eh;
72 if (eolhit is null) eolhit = &eh;
73 *eolhit = false;
74 if (maxSize == 0) return null;
75 char[] res;
76 ubyte ch;
77 for (;;) {
78 if (fl.rawRead((&ch)[0..1]).length == 0) { *eolhit = true; break; }
79 if (ch == 0) { *eolhit = true; break; }
80 if (maxSize == 0) break;
81 res ~= ch;
82 --maxSize;
84 return cast(string)res; // it is safe to cast here
88 // ////////////////////////////////////////////////////////////////////////// //
89 /// read stream line by line. very slow, no recoding.
90 // hack around "has scoped destruction, cannot build closure"
91 public auto byLineCopy(bool keepTerm=false) (VFile fl) { return byLineCopyImpl!keepTerm(fl); }
92 public auto byLineCopy(bool keepTerm=false, ST) (auto ref ST fl) if (!is(ST == VFile) && isRorWStream!ST) { return byLineCopyImpl!keepTerm(fl); }
94 public auto byLine(bool keepTerm=false) (VFile fl) { return byLineCopyImpl!(keepTerm, true)(fl); }
95 public auto byLine(bool keepTerm=false, ST) (auto ref ST fl) if (!is(ST == VFile) && isRorWStream!ST) { return byLineCopyImpl!(keepTerm, true)(fl); }
97 private auto byLineCopyImpl(bool keepTerm=false, bool reuseBuffer=false, ST) (auto ref ST fl) {
98 static struct BLR(bool keepTerm, bool reuse, ST) {
99 private:
100 ST st;
101 bool eof, futureeof;
102 char[256] rdbuf;
103 int rdpos, rdsize;
104 char[] buf;
105 usize bufused;
106 static if (!reuse) string s;
107 private:
108 this() (auto ref ST ast) {
109 st = ast;
110 if (st.eof) eof = true; else popFront();
112 public:
113 @property bool empty () const pure nothrow @safe @nogc { return eof; }
114 @property auto front () inout nothrow @trusted @nogc { static if (reuse) return buf.ptr[0..bufused]; else return s; }
115 void popFront () {
116 char getch () {
117 assert(!futureeof);
118 if (rdpos >= rdsize) {
119 rdpos = rdsize = 0;
120 auto rd = st.rawRead(rdbuf[]);
121 if (rd.length == 0) { futureeof = true; return 0; }
122 rdsize = cast(int)rd.length;
124 assert(rdpos < rdsize);
125 return rdbuf.ptr[rdpos++];
127 void putch (char ch) {
128 if (bufused == buf.length) {
129 auto optr = buf.ptr;
130 buf ~= ch;
131 if (buf.ptr !is optr) {
132 import core.memory : GC;
133 if (buf.ptr is GC.addrOf(buf.ptr)) GC.setAttr(buf.ptr, GC.BlkAttr.NO_INTERIOR);
135 ++bufused;
136 } else {
137 buf.ptr[bufused++] = ch;
140 if (futureeof) { eof = true; futureeof = false; }
141 bufused = 0;
142 static if (!reuse) s = null;
143 if (eof) return;
144 bool wasChar = false;
145 for (;;) {
146 char ch = getch();
147 if (futureeof) break;
148 wasChar = true;
149 if (ch == '\r') {
150 // cr
151 ch = getch();
152 if (futureeof) {
153 static if (keepTerm) putch('\r');
154 break;
156 if (ch == '\n') {
157 static if (keepTerm) { putch('\r'); putch('\n'); }
158 break;
160 } else if (ch == '\n') {
161 // lf
162 static if (keepTerm) putch('\n');
163 break;
165 putch(ch);
167 if (!wasChar) {
168 assert(futureeof);
169 futureeof = false;
170 eof = true;
171 } else {
172 static if (!reuse) s = (bufused ? buf.ptr[0..bufused].idup : "");
176 return BLR!(keepTerm, reuseBuffer, ST)(fl);
180 // ////////////////////////////////////////////////////////////////////////// //
181 public auto readf(Char:dchar, A...) (VFile fl, const(Char)[] fmt, A args) { return readfImpl!(Char)(fl, fmt, args); }
182 public auto readf(ST, Char:dchar, A...) (auto ref ST fl, const(Char)[] fmt, A args) if (!is(ST == VFile) && isRorWStream!ST) { return readfImpl!(Char, ST)(fl, fmt, args); }
184 private auto readfImpl(Char:dchar, ST, A...) (auto ref ST fl, const(Char)[] fmt, A args) {
185 import std.format : formattedRead;
186 static struct Reader(ST) {
187 ST fl;
188 char ch;
189 bool eof;
190 this() (auto ref ST afl) { fl = afl; if (fl.eof) eof = true; else popFront(); }
191 @property bool empty () const pure nothrow @safe @nogc { return eof; }
192 @property char front () const pure nothrow @safe @nogc { return ch; }
193 void popFront() { if (!eof) { eof = (fl.rawRead((&ch)[0..1]).length == 0); } }
195 auto rd = Reader!ST(fl);
196 return formattedRead(rd, fmt, args);
200 // ////////////////////////////////////////////////////////////////////////// //
201 public string readln() (VFile fl) { return readlnImpl(fl); }
202 public string readln(ST) (auto ref ST fl) if (!is(ST == VFile) && isRorWStream!ST) { return readlnImpl!ST(fl); }
203 public string readln() () { return readlnImpl(stdin); }
205 // slow, but IDC
206 private string readlnImpl(ST) (auto ref ST fl) {
207 enum MaxLen = 65536;
208 if (fl.eof) return null;
209 char[] res;
210 char ch;
211 for (;;) {
212 if (fl.rawRead((&ch)[0..1]).length != 1) break;
213 if (ch == '\n') break;
214 if (ch == '\r') {
215 if (fl.rawRead((&ch)[0..1]).length != 1) break;
216 if (ch == '\n') break;
217 if (res.length == MaxLen) throw new Exception("line too long");
218 res ~= '\r';
220 if (res.length == MaxLen) throw new Exception("line too long");
221 res ~= ch;
223 return cast(string)res; // it is safe to cast here
227 // ////////////////////////////////////////////////////////////////////////// //
228 void writeImpl(S...) (ref VFile.UnlockedWriterImpl wr, S args) {
229 import std.traits : isBoolean, isIntegral, isAggregateType, isSomeString, isSomeChar;
230 foreach (/*auto*/ arg; args) {
231 alias A = typeof(arg);
232 static if (isAggregateType!A || is(A == enum)) {
233 import std.format : formattedWrite;
234 formattedWrite(wr, "%s", arg);
235 } else static if (isSomeString!A) {
236 wr.put(arg);
237 } else static if (isIntegral!A) {
238 import std.conv : toTextRange;
239 toTextRange(arg, wr);
240 } else static if (isBoolean!A) {
241 wr.put(arg ? "true" : "false");
242 } else static if (isSomeChar!A) {
243 wr.put(arg);
244 } else {
245 import std.format : formattedWrite;
246 formattedWrite(wr, "%s", arg);
252 // ////////////////////////////////////////////////////////////////////////// //
253 public void write(A...) (A args) {
254 import std.traits : isAggregateType;
255 static if (A.length == 0) {
256 // do nothing
257 } else static if (is(A[0] == VFile)) {
258 // fl.write(...)
259 static if (A.length == 1) {
260 // nothing to do
261 } else static if (A.length == 2 &&
262 is(typeof(args[1]) : const(char)[]) &&
263 !is(typeof(args[1]) == enum) &&
264 !is(Unqual!(typeof(args[1])) == typeof(null)) &&
265 !isAggregateType!(typeof(args[1])))
267 import std.traits : isStaticArray;
268 // specialization for strings -- a very frequent case
269 synchronized(args[0].wst) {
270 //auto wr = args[0].lockedWriter;
271 auto wr = args[0].unlockedWriter;
272 static if (isStaticArray!(typeof(args[1]))) {
273 wr.put(args[1][]);
274 } else {
275 wr.put(args[1]);
278 } else {
279 synchronized(args[0].wst) {
280 //auto wr = args[0].lockedWriter;
281 auto wr = args[0].unlockedWriter;
282 writeImpl(wr, args[1..$]);
285 } else static if (A.length == 1 &&
286 is(typeof(args[0]) : const(char)[]) &&
287 !is(typeof(args[0]) == enum) &&
288 !is(Unqual!(typeof(args[0])) == typeof(null)) &&
289 !isAggregateType!(typeof(args[0])))
291 import std.traits : isStaticArray;
292 synchronized(stdout.wst) {
293 //auto wr = stdout.lockedWriter;
294 auto wr = stdout.unlockedWriter;
295 // specialization for strings -- a very frequent case
296 static if (isStaticArray!(typeof(args[0]))) {
297 wr.put(args[0][]);
298 } else {
299 wr.put(args[0]);
302 } else {
303 synchronized(stdout.wst) {
304 //auto wr = stdout.lockedWriter;
305 auto wr = stdout.unlockedWriter;
306 writeImpl(wr, args);
311 public void writeln(A...) (A args) { .write(args, "\n"); }
314 // ////////////////////////////////////////////////////////////////////////// //
315 public void writef(Char:dchar, A...) (VFile fl, const(Char)[] fmt, A args) { if (!fl.isOpen) throw new VFSException("can't write to closed stream"); import std.format : formattedWrite; synchronized(fl.wst) { auto wr = fl.unlockedWriter; formattedWrite(wr, fmt, args); } }
316 public void writef(Char:dchar, A...) (const(Char)[] fmt, A args) { if (!stdout.isOpen) return; import std.format : formattedWrite; synchronized(stdout.wst) { auto wr = stdout.lockedWriter; formattedWrite(wr, fmt, args); } }
318 public void writefln(Char:dchar, A...) (VFile fl, const(Char)[] fmt, A args) { if (!fl.isOpen) throw new VFSException("can't write to closed stream"); import std.format : formattedWrite; synchronized(fl.wst) { auto wr = fl.lockedWriter; formattedWrite(wr, fmt, args); wr.put("\n"); } }
319 public void writefln(Char:dchar, A...) (const(Char)[] fmt, A args) { if (!stdout.isOpen) return; import std.format : formattedWrite; synchronized(stdout.wst) { auto wr = stdout.lockedWriter; formattedWrite(wr, fmt, args); wr.put("\n"); } }