# Copyright 2005 Kevin Reid, under the terms of the MIT X license # found at http://www.opensource.org/licenses/mit-license.html ................ pragma.enable("easy-return") pragma.disable("explicit-result-guard") pragma.enable("dot-props") pragma.enable("verb-curry") pragma.enable("accumulator") def map implements DeepFrozen { # XXX eutil to v(f, coll) { return accum [] for v in coll {_.with(f(v))}} to kv(f, coll) { return accum [] for k => v in coll {_.with(f(k, v))}} } def makeOutStreamShell := def Range := EIO::Range def ALL := EIO::ALL def IPAuthor implements DeepFrozen { # This is somewhat an experiment in writing close-to-the-implementation code in E # XXX can we give a smaller authority than all-of-lisp practically? to run(lisp) { def makeFDInStream := (lisp) # XXX this is horribly verbose. the lisp interface needs some shortcuts def intern := lisp["CL", "INTERN"]::"function" def read := lisp["CL", "READ-FROM-STRING"]::"function" def l__quasiParser { to valueMaker(t) { return def vm { to substitute(values) { return read(simple__quasiParser.valueMaker(t).substitute(values)) } } } } { def asdfOperate := lisp["ASDF", "OPERATE"]::"function" def load_op := intern("LOAD-OP", "ASDF") # XXX :verbose nil for asdf loads asdfOperate(load_op, intern("E-ON-CL.SOCKETS", "KEYWORD")) } def clCoerce := lisp["CL", "COERCE"]::"function" def fooWrite := lisp["E.SOCKETS", "FOO-WRITE"]::"function" def fooConnectTcp := lisp["E.SOCKETS", "FOO-CONNECT-TCP"]::"function" def _getAddrInfo := lisp["E.SOCKETS", "GET-ADDR-INFO"]::"function" def Element := int # XXX int8 def IP { to __printOn(out :TextWriter) { out.write("") } to getAddrInfo(host, service, hints) { # _getAddrInfo handles coercion return _getAddrInfo(host, service, hints) } /** XXX copy documentation from keio implementation */ to getRemoteTCPEndpoint(hostSpec :String, serviceSpec :String) { # XXX should we separate out the TCP part? def remoteTCPEndpoint { to __printOn(out :TextWriter) { out.write("") } to connect(==([].asMap())) { def nameObj { to __printOn(out :TextWriter) { out.print(hostSpec) out.write(":") out.print(serviceSpec) } } def inStream def outStream when (IP.getAddrInfo(hostSpec, serviceSpec, null)) -> done(address) { def socket := fooConnectTcp(address, null) # this value is semantically arbitrary, but matching the socket's default send buffer is, I hope, good for efficiency def maxOutAvailable := socket::sockoptSendBuffer # The buffer for the output side; there is no portable way to ask a socket how much local buffer is available, so we must maintain our own. def outBuffer := [].diverge(Element) bind outStream := makeOutStreamShell(Element, def outBackend, def outImpl { to __printOn(out :TextWriter) { out.printSame(nameObj) } to write(elements :List) { require(elements.size() <= maxOutAvailable - outBuffer.size()) outBuffer.append(elements) } to flush() { def v := clCoerce(outBuffer.snapshot(), l`(cl:vector (cl:unsigned-byte 8))`) escape writeError { def elementsWritten := fooWrite(socket, v, writeError) outBuffer.setRun(0, elementsWritten, []) outBackend.setAvailable(maxOutAvailable - outBuffer.size()) } catch error { # XXX typed exception; javaism def p := `file descriptor write error for $outStream: $error` : traceln(p) outStream.fail(p) } } to terminate(t) { traceln(`XXX should be closing the socket $socket but that isn't implemented yet (IPAuthor)`) } }) bind inStream := makeFDInStream(nameObj, socket, socket::sockoptReceiveBuffer) outBackend.setAvailable(maxOutAvailable) # no arbitrary side effects as available reactors can't have been registered yet } catch p { inStream__Resolver.smash(p) outStream__Resolver.smash(p) } return [inStream, outStream] } } return remoteTCPEndpoint } } return IP } }