Mercurial Hosting > luan
diff core/src/luan/modules/RpcLuan.java @ 610:b4f3dbe1c6e3
add Rpc and change Hosting to use Rpc
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Fri, 11 Dec 2015 00:13:13 -0700 |
parents | |
children | cdc70de628b5 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/src/luan/modules/RpcLuan.java Fri Dec 11 00:13:13 2015 -0700 @@ -0,0 +1,228 @@ +package luan.modules; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.EOFException; +import java.nio.charset.StandardCharsets; +import java.util.Set; +import java.util.IdentityHashMap; +import java.util.Collections; +import java.util.Map; +import luan.Luan; +import luan.LuanState; +import luan.LuanTable; +import luan.LuanFunction; +import luan.LuanException; +import luan.LuanMethod; + + +public final class RpcLuan { + private static final int NIL = 0; + private static final int STRING = 1; + private static final int BOOLEAN = 2; + private static final int NUMBER = 3; + private static final int BINARY = 4; + private static final int TABLE = 5; + + @LuanMethod public static Object[] call(LuanState luan,LuanTable socketTbl,String fnName,Object... args) + throws LuanException, IOException + { + IoLuan.LuanSocket socket = (IoLuan.LuanSocket)socketTbl.rawGet("java"); + InputStream in = new BufferedInputStream(socket.inputStream()); + OutputStream out = new BufferedOutputStream(socket.outputStream()); + try { + writeString(out,fnName); + writeInt(out,args.length); + for( Object arg : args ) { + writeObj(out,luan,arg); + } + out.flush(); + boolean ok = readBoolean(in); + if( ok ) { + int n = readInt(in); + Object[] rtn = new Object[n]; + for( int i=0; i<n; i++ ) { + rtn[i] = readObj(in,luan); + } + return rtn; + } else { + String msg = readString(in); + throw new LuanException(luan,msg); + } + } finally { + out.close(); + in.close(); + } + } + + public static void respond(LuanState luan,LuanTable socketTbl,LuanTable fns) + throws IOException, LuanException + { + IoLuan.LuanSocket socket = (IoLuan.LuanSocket)socketTbl.rawGet("java"); + InputStream in = new BufferedInputStream(socket.inputStream()); + OutputStream out = new BufferedOutputStream(socket.outputStream()); + try { + Object[] rtn; + try { + String fnName = readString(in); + int nArgs = readInt(in); + Object[] args = new Object[nArgs]; + for( int i=0; i<nArgs; i++ ) { + args[i] = readObj(in,luan); + } + LuanFunction fn = (LuanFunction)fns.rawGet(fnName); + if( fn == null ) + throw new LuanException(luan, "function not found: " + fnName ); + rtn = Luan.array(fn.call(luan,args)); + } catch(LuanException e) { + writeBoolean(out,false); + writeString(out,e.getFullMessage()); + return; + } + writeBoolean(out,true); + writeInt(out,rtn.length); + for( Object obj : rtn ) { + writeObj(out,luan,obj); + } + } finally { + out.close(); + in.close(); + } + } + + static void writeObj(OutputStream out,LuanState luan,Object obj) throws IOException, LuanException { + if( obj == null ) { + out.write(NIL); + } + else if( obj instanceof String ) { + out.write(STRING); + writeString(out,(String)obj); + } + else if( obj instanceof Boolean ) { + out.write(BOOLEAN); + writeBoolean(out,(Boolean)obj); + } + else if( obj instanceof Number ) { + out.write(NUMBER); + writeString(out,obj.toString()); + } + else if( obj instanceof byte[] ) { + byte[] a = (byte[])obj; + out.write(BINARY); + writeInt(out,a.length); + out.write(a); + } + else if( obj instanceof LuanTable ) { + out.write(TABLE); + String s = pickle( luan, obj, Collections.newSetFromMap(new IdentityHashMap<LuanTable,Boolean>()) ); + writeString(out,s); + } + else + throw new LuanException(luan, "invalid type: " + obj.getClass() ); + } + + static Object readObj(InputStream in,LuanState luan) throws IOException, LuanException { + int type = in.read(); + switch(type) { + case NIL: + return null; + case STRING: + return readString(in); + case BOOLEAN: + return readBoolean(in); + case NUMBER: + return Double.valueOf(readString(in)); + case BINARY: + return readBinary(in,readInt(in)); + case TABLE: + String s = readString(in); + LuanFunction fn = BasicLuan.load(luan,s,"rpc-reader",null,true); + return fn.call(luan); + default: + throw new LuanException(luan, "invalid type: " + type ); + } + } + + static Boolean readBoolean(InputStream in) throws IOException { + return Boolean.valueOf(readString(in)); + } + + static String readString(InputStream in) throws IOException { + int len = readInt(in); + byte[] a = readBinary(in,len); + return new String(a,StandardCharsets.UTF_8); + } + + static int readInt(InputStream in) throws IOException { + int ch1 = in.read(); + int ch2 = in.read(); + int ch3 = in.read(); + int ch4 = in.read(); + if ((ch1 | ch2 | ch3 | ch4) < 0) + throw new EOFException(); + return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0)); + } + + static byte[] readBinary(InputStream in,int size) throws IOException { + byte[] a = new byte[size]; + int i = 0; + while( i < size ) { + int n = in.read(a,i,size-i); + if( n == -1 ) + throw new EOFException(); + i += n; + } + return a; + } + + static void writeBoolean(OutputStream out,Boolean b) throws IOException { + writeString(out,b.toString()); + } + + static void writeString(OutputStream out,String s) throws IOException { + byte[] a = s.getBytes(StandardCharsets.UTF_8); + writeInt(out,a.length); + out.write(a); + } + + static void writeInt(OutputStream out,int v) throws IOException { + out.write((v >>> 24) & 0xFF); + out.write((v >>> 16) & 0xFF); + out.write((v >>> 8) & 0xFF); + out.write((v >>> 0) & 0xFF); + } + + + private static String pickle(LuanState luan,Object obj,Set<LuanTable> set) throws LuanException { + if( obj == null ) + return "nil"; + if( obj instanceof Boolean ) + return obj.toString(); + if( obj instanceof Number ) + return Luan.toString((Number)obj); + if( obj instanceof String ) + return "\"" + Luan.stringEncode((String)obj) + "\""; + if( obj instanceof LuanTable ) { + LuanTable tbl = (LuanTable)obj; + if( !set.add(tbl) ) { + throw new LuanException(luan, "circular reference in table" ); + } + StringBuilder sb = new StringBuilder(); + sb.append( "{" ); + for( Map.Entry<Object,Object> entry : tbl.iterable(luan) ) { + sb.append( "[" ); + sb.append( pickle(luan,entry.getKey(),set) ); + sb.append( "]=" ); + sb.append( pickle(luan,entry.getValue(),set) ); + sb.append( ", " ); + } + sb.append( "}" ); + return sb.toString(); + } + throw new LuanException(luan, "invalid type: " + obj.getClass() ); + } + +}