Allow schema files that are missing checksums on the !!SCHEMAMATIC line.
[versaplex.git] / wvdotnet / wvstream.cs
blobf7ee852c79feb0e495b0e51dfc78e13e5fc1c7c5
1 /*
2 * Versaplex:
3 * Copyright (C)2007-2008 Versabanq Innovations Inc. and contributors.
4 * See the included file named LICENSE for license information.
5 */
6 using System;
7 using System.Net;
8 using Wv.Extensions;
10 namespace Wv
12 public interface IWvStream: IDisposable
14 bool ok { get; }
15 Exception err { get; }
16 EndPoint localaddr { get; }
17 EndPoint remoteaddr { get; }
19 int read(WvBytes b);
20 int write(WvBytes b);
21 bool flush(int msec_timeout);
23 event Action onreadable;
24 event Action onwritable;
25 event Action onclose;
27 void close();
28 void noread();
29 void nowrite();
32 public class WvStream : IWvStream
34 public static IWvEventer ev = new WvEventer();
36 public static void runonce()
38 ev.runonce();
41 public static void runonce(int msec_timeout)
43 ev.runonce(msec_timeout);
46 public WvStream()
50 public void Dispose()
52 close();
55 bool is_readable = false, is_writable = false;
56 event Action _onreadable, _onwritable, _onclose;
58 protected bool can_onreadable { get { return _onreadable != null; } }
59 protected bool can_onwritable { get { return _onwritable != null; } }
61 protected void do_readable()
63 if (can_onreadable)
65 is_readable = false;
66 _onreadable();
70 protected void do_writable()
72 if (can_onwritable)
74 is_writable = false;
75 _onwritable();
79 protected void do_close()
81 if (_onclose != null)
82 _onclose();
85 object pr_obj = new object();
86 protected void post_readable()
88 is_readable = true;
89 ev.addpending(pr_obj, do_readable);
92 object pw_obj = new object();
93 protected void post_writable()
95 is_writable = true;
96 ev.addpending(pw_obj, do_writable);
99 public virtual event Action onreadable {
100 add { _onreadable += value; if (is_readable) post_readable(); }
101 remove { _onreadable -= value; }
103 public virtual event Action onwritable {
104 add { _onwritable += value; if (is_writable) post_writable(); }
105 remove { _onwritable -= value; }
107 public virtual event Action onclose {
108 add { _onclose += value; }
109 remove { _onclose -= value; }
112 bool isopen = true;
113 public virtual bool ok { get { return isopen && err == null; } }
115 Exception _err;
116 public virtual Exception err {
117 get {
118 return _err;
120 set {
121 if (_err == null) // remember the *first* error
123 _err = value;
124 close();
129 public virtual void close()
131 ev.delpending(pr_obj);
132 ev.delpending(pw_obj);
133 canread = false;
134 flush(-1);
135 canwrite = false;
136 if (isopen)
138 isopen = false;
139 if (_onclose != null) _onclose();
141 GC.SuppressFinalize(this);
144 public virtual EndPoint localaddr {
145 get { return null; }
148 public virtual EndPoint remoteaddr {
149 get { return null; }
152 public virtual int read(WvBytes b)
154 return 0;
157 // for convenience. Note: always returns non-null, but the returned
158 // array size might be zero.
159 public WvBytes read(int len)
161 WvBytes bytes = new byte[len];
162 int got = read(bytes);
163 return bytes.sub(0, got);
166 public void read(WvBuf b, int max)
168 int got = read(b.alloc(max));
169 b.unalloc(max-got);
172 public virtual int write(WvBytes b)
174 return b.len; // lie: we "wrote" all the bytes to nowhere
178 * Wait up to msec_timeout milliseconds for the stream to become
179 * readable or writable, respectively.
181 * Our default implementation always returns immediately, consistent
182 * with the Unix select() behaviour of returning immediately on
183 * non-select()able file handles.
185 * Returns true if the stream is readable or writable before the
186 * timeout, false otherwise.
188 * Waiting synchronously is usually a bad idea in WvStreams
189 * programs. Use onreadable/onwritable/onclose instead whenever
190 * you can.
192 public virtual bool wait(int msec_timeout,
193 bool readable, bool writable)
195 if (readable && is_readable)
196 return true;
197 if (writable && is_writable)
198 return true;
199 if (!ok || (readable && !canread) || (writable && !canwrite))
200 return false;
201 return true;
204 // Don't make these anything but private! They're tempting, but
205 // they're not *really* what you want to know. Use these instead:
206 // ok -> whether or not your stream is useful at all
207 // wait(0, true, false) -> whether your stream has bytes *right now*.
208 // If canread goes false, wait(-1, true, false) will return false.
209 bool canread = true, canwrite = true;
211 public virtual void noread()
213 canread = false;
214 maybe_autoclose();
217 public virtual void nowrite()
219 canwrite = false;
220 maybe_autoclose();
223 public void maybe_autoclose()
225 if (!canread && !canwrite)
226 close();
229 public virtual bool flush(int msec_timeout)
231 return true; // no buffer
234 // WARNING: assumes the write() will succeed! Use only on WvStreams
235 // with a write buffer.
236 public void print(string fmt, params object[] args)
238 print((object)wv.fmt(fmt, args));
241 // WARNING: assumes the write() will succeed! Use only on WvStreams
242 // with a write buffer.
243 public virtual void print(object o)
245 byte[] b = o.ToUTF8();
246 int n = write(b);
247 wv.assert(n == b.Length,
248 "Don't use print() on an unbuffered WvStream!");
252 // Wraps a WvStream in another WvStream, allowing us to override some
253 // behaviour. By default, a WvStreamClone just passes everything through
254 // to the inner stream.
255 public class WvStreamClone : WvStream
257 protected WvStream inner = null;
258 bool hasinner { get { return inner != null; } }
260 public WvStreamClone(WvStream inner)
262 setinner(inner);
265 public void setinner(WvStream inner)
267 if (inner != this.inner)
269 if (hasinner)
271 this.inner.onreadable -= do_readable;
272 this.inner.onwritable -= do_writable;
273 this.inner.onclose -= do_close;
275 this.inner = inner;
276 if (hasinner)
278 if (can_onreadable) this.inner.onreadable += do_readable;
279 if (can_onwritable) this.inner.onwritable += do_writable;
280 this.inner.onclose += do_close;
285 public override bool ok {
286 get { return base.ok && hasinner && inner.ok; }
289 public override Exception err {
290 get {
291 return hasinner ? inner.err : base.err;
293 set {
294 if (hasinner)
295 inner.err = value;
296 else
297 base.err = value;
301 public override EndPoint localaddr {
302 get { return hasinner ? inner.localaddr : base.localaddr; }
304 public override EndPoint remoteaddr {
305 get { return hasinner ? inner.localaddr : base.localaddr; }
308 public override int read(WvBytes b)
310 if (hasinner)
311 return inner.read(b);
312 else
313 return 0; // 0 bytes read
316 public override int write(WvBytes b)
318 if (hasinner)
319 return inner.write(b);
320 else
321 return 0; // 0 bytes written
324 public override bool wait(int msec_timeout,
325 bool readable, bool writable)
327 if (hasinner)
328 return inner.wait(msec_timeout, readable, writable);
329 else
330 return base.wait(msec_timeout, readable, writable);
333 public override void noread()
335 base.noread();
336 if (hasinner)
337 inner.noread();
340 public override void nowrite()
342 base.nowrite();
343 if (hasinner)
344 inner.nowrite();
347 public override bool flush(int msec_timeout)
349 if (hasinner)
350 return inner.flush(msec_timeout);
351 else
352 return true;
355 // We only want to register our callback with the inner stream if
356 // we *have* a callback, and then only once. Otherwise the stream
357 // might start listening for read when we don't have any readable
358 // handlers, resulting in it spinning forever.
359 public override event Action onreadable {
360 add { if (!can_onreadable) inner.onreadable += do_readable;
361 base.onreadable += value; }
362 remove { base.onreadable -= value;
363 if (!can_onreadable) inner.onreadable -= do_readable; }
365 public override event Action onwritable {
366 add { if (!can_onwritable) inner.onwritable += do_writable;
367 base.onwritable += value; }
368 remove { base.onwritable -= value;
369 if (!can_onwritable) inner.onwritable -= do_writable; }
374 /// Adds an input buffer to a WvStream.
375 public class WvInBufStream : WvStreamClone
377 WvBuf inbuf = new WvBuf();
379 public WvInBufStream(WvStream inner) : base(inner)
383 public override int read(WvBytes b)
385 if (inbuf.used > 0)
387 int max = inbuf.used > b.len ? b.len : inbuf.used;
388 b.put(0, inbuf.get(max));
389 post_readable();
390 return max;
392 else
393 return base.read(b);
396 public string _getline(char splitchar)
398 if (inbuf.strchr(splitchar) > 0)
400 post_readable(); // not stalled yet!
401 return inbuf.get(inbuf.strchr(splitchar)).FromUTF8();
403 return null;
406 public string getline(int msec_timeout, char splitchar)
408 string l = _getline(splitchar);
409 if (l != null) return l;
411 foreach (var remain in wv.until(msec_timeout))
413 if (inner.wait(remain, true, false))
414 inner.read(inbuf, 4096);
416 l = _getline(splitchar);
417 if (l != null) return l;
419 if (inbuf.used == 0 && !ok)
420 break;
423 if (!ok && inbuf.used > 0)
424 return inbuf.getall().FromUTF8();
426 return null;
430 /// Adds an output buffer to a WvStream
431 public class WvOutBufStream : WvStreamClone
433 WvBuf outbuf = new WvBuf();
434 bool writereg = true;
436 public WvOutBufStream(WvStream inner) : base(inner)
440 public override int write(WvBytes b)
442 outbuf.put(b);
443 flush(0);
445 // always succeed
446 return b.len;
449 void _flush()
451 int wrote = base.write(outbuf.peekall());
452 outbuf.get(wrote);
454 if (outbuf.used > 0 && !writereg)
456 inner.onwritable += _flush;
457 writereg = true;
459 else if (outbuf.used == 0 && writereg)
461 inner.onwritable -= _flush;
462 writereg = false;
465 if (outbuf.used == 0)
466 post_writable();
469 public override bool flush(int msec_timeout)
471 foreach (int remain in wv.until(msec_timeout))
473 _flush();
474 if (remain != 0 && outbuf.used > 0)
475 wait(remain, false, true);
476 if (outbuf.used == 0)
477 return true;
479 return false;
484 * A combination of WvInBufStream and WvOutBufStream.
486 * We put the OutBufStream on the inside and the InBufStream on the
487 * outside, because InBufStream actually adds API functions while
488 * OutBufStream doesn't. So getline() will work as expected on this
489 * kind of stream.
491 public class WvBufStream : WvInBufStream
493 public WvBufStream(WvStream inner)
494 : base(new WvOutBufStream(inner))