1 package org.rosuda.REngine.Rserve.protocol;
3 // JRclient library - client interface to Rserve, see http://www.rosuda.org/Rserve/
4 // Copyright (C) 2004 Simon Urbanek
5 // --- for licensing information see LICENSE file in the original JRclient distribution ---
11 import org.rosuda.REngine.Rserve.RConnection;
13 /** This class encapsulates the QAP1 protocol used by Rserv.
14 it is independent of the underying protocol(s), therefore RTalk
15 can be used over any transport layer
17 The current implementation supports long (0.3+/0102) data format only
18 up to 32-bit and only for incoming packets.
23 public static final int DT_INT=1;
24 public static final int DT_CHAR=2;
25 public static final int DT_DOUBLE=3;
26 public static final int DT_STRING=4;
27 public static final int DT_BYTESTREAM=5;
28 public static final int DT_SEXP=10;
29 public static final int DT_ARRAY=11;
31 /** this is a flag saying that the contents is large (>0xfffff0) and hence uses 56-bit length field */
32 public static final int DT_LARGE=64;
34 public static final int CMD_login=0x001;
35 public static final int CMD_voidEval=0x002;
36 public static final int CMD_eval=0x003;
37 public static final int CMD_shutdown=0x004;
38 public static final int CMD_openFile=0x010;
39 public static final int CMD_createFile=0x011;
40 public static final int CMD_closeFile=0x012;
41 public static final int CMD_readFile=0x013;
42 public static final int CMD_writeFile=0x014;
43 public static final int CMD_removeFile=0x015;
44 public static final int CMD_setSEXP=0x020;
45 public static final int CMD_assignSEXP=0x021;
47 public static final int CMD_setBufferSize=0x081;
48 public static final int CMD_setEncoding=0x082;
50 public static final int CMD_detachSession=0x030;
51 public static final int CMD_detachedVoidEval=0x031;
52 public static final int CMD_attachSession=0x032;
54 // control commands since 0.6-0
55 public static final int CMD_ctrlEval=0x42;
56 public static final int CMD_ctrlSource=0x45;
57 public static final int CMD_ctrlShutdown=0x44;
59 // errors as returned by Rserve
60 public static final int ERR_auth_failed=0x41;
61 public static final int ERR_conn_broken=0x42;
62 public static final int ERR_inv_cmd=0x43;
63 public static final int ERR_inv_par=0x44;
64 public static final int ERR_Rerror=0x45;
65 public static final int ERR_IOerror=0x46;
66 public static final int ERR_not_open=0x47;
67 public static final int ERR_access_denied=0x48;
68 public static final int ERR_unsupported_cmd=0x49;
69 public static final int ERR_unknown_cmd=0x4a;
70 public static final int ERR_data_overflow=0x4b;
71 public static final int ERR_object_too_big=0x4c;
72 public static final int ERR_out_of_mem=0x4d;
73 public static final int ERR_ctrl_closed=0x4e;
74 public static final int ERR_session_busy=0x50;
75 public static final int ERR_detach_failed=0x51;
80 /** constructor; parameters specify the streams
81 @param sis socket input stream
82 @param sos socket output stream */
84 public RTalk(InputStream sis, OutputStream sos) {
88 /** writes bit-wise int to a byte buffer at specified position in Intel-endian form
89 @param v value to be written
91 @param o offset in the buffer to start at. An int takes always 4 bytes */
92 public static void setInt(int v, byte[] buf, int o) {
93 buf[o]=(byte)(v&255); o++;
94 buf[o]=(byte)((v&0xff00)>>8); o++;
95 buf[o]=(byte)((v&0xff0000)>>16); o++;
96 buf[o]=(byte)((v&0xff000000)>>24);
99 /** writes cmd/resp/type byte + 3/7 bytes len into a byte buffer at specified offset.
100 @param ty type/cmd/resp byte
104 @return offset in buf just after the header. Please note that since Rserve 0.3 the header can be either 4 or 8 bytes long, depending on the len parameter.
106 public static int setHdr(int ty, int len, byte[] buf, int o) {
107 buf[o]=(byte)((ty&255)|((len>0xfffff0)?DT_LARGE:0)); o++;
108 buf[o]=(byte)(len&255); o++;
109 buf[o]=(byte)((len&0xff00)>>8); o++;
110 buf[o]=(byte)((len&0xff0000)>>16); o++;
111 if (len>0xfffff0) { // for large data we need to set the next 4 bytes as well
112 buf[o]=(byte)((len&0xff000000)>>24); o++;
113 buf[o]=0; o++; // since len is int, we get 32-bits only
120 /** creates a new header according to the type and length of the parameter
121 @param ty type/cmd/resp byte
123 public static byte[] newHdr(int ty, int len) {
124 byte[] hdr=new byte[(len>0xfffff0)?8:4];
125 setHdr(ty,len,hdr,0);
129 /** converts bit-wise stored int in Intel-endian form into Java int
130 @param buf buffer containg the representation
131 @param o offset where to start (4 bytes will be used)
132 @return the int value. no bounds checking is done so you need to
133 make sure that the buffer is big enough */
134 public static int getInt(byte[] buf, int o) {
135 return ((buf[o]&255)|((buf[o+1]&255)<<8)|((buf[o+2]&255)<<16)|((buf[o+3]&255)<<24));
138 /** converts bit-wise stored length from a header. "long" format is supported up to 32-bit
140 @param o offset of the header (length is at o+1)
142 public static int getLen(byte[] buf, int o) {
145 ((buf[o]&64)>0)? // "long" format; still - we support 32-bit only
146 ((buf[o+1]&255)|((buf[o+2]&255)<<8)|((buf[o+3]&255)<<16)|((buf[o+4]&255)<<24))
148 ((buf[o+1]&255)|((buf[o+2]&255)<<8)|((buf[o+3]&255)<<16));
151 /** converts bit-wise Intel-endian format into long
153 @param o offset (8 bytes will be used)
154 @return long value */
155 public static long getLong(byte[] buf, int o) {
156 long low=((long)getInt(buf,o))&0xffffffffL;
157 long hi=((long)getInt(buf,o+4))&0xffffffffL;
162 public static void setLong(long l, byte[] buf, int o) {
163 setInt((int)(l&0xffffffffL),buf,o);
164 setInt((int)(l>>32),buf,o+4);
167 /** sends a request with no attached parameters
169 @return returned packet or <code>null</code> if something went wrong */
170 public RPacket request(int cmd) {
171 byte[] d = new byte[0];
172 return request(cmd,d);
175 /** sends a request with attached parameters
177 @param cont contents - parameters
178 @return returned packet or <code>null</code> if something went wrong */
179 public RPacket request(int cmd, byte[] cont) {
180 return request(cmd,null,cont,0,(cont==null)?0:cont.length);
183 /** sends a request with attached prefix and parameters. Both prefix and cont can be <code>null</code>. Effectively <code>request(a,b,null)</code> and <code>request(a,null,b)</code> are equivalent.
184 @param cmd command - a special command of -1 prevents request from sending anything
185 @param prefix - this content is sent *before* cont. It is provided to save memory copy operations where a small header precedes a large data chunk (usually prefix conatins the parameter header and cont contains the actual data).
187 @param offset offset in cont where to start sending (if <0 then 0 is assumed, if >cont.length then no cont is sent)
188 @param len number of bytes in cont to send (it is clipped to the length of cont if necessary)
189 @return returned packet or <code>null</code> if something went wrong */
190 public RPacket request(int cmd, byte[] prefix, byte[] cont, int offset, int len) {
192 if (offset>=cont.length) { cont=null; len=0; }
193 else if (len>cont.length-offset) len=cont.length-offset;
195 if (offset<0) offset=0;
197 int contlen=(cont==null)?0:len;
198 if (prefix!=null && prefix.length>0) contlen+=prefix.length;
199 byte[] hdr=new byte[16];
201 setInt(contlen,hdr,4);
202 for(int i=8;i<16;i++) hdr[i]=0;
206 if (prefix!=null && prefix.length>0)
208 if (cont!=null && cont.length>0)
209 os.write(cont,offset,len);
212 byte[] ih=new byte[16];
215 int rep=getInt(ih,0);
216 int rl =getInt(ih,4);
218 byte[] ct=new byte[rl];
221 int rd=is.read(ct,n,rl-n);
224 return new RPacket(rep,ct);
226 return new RPacket(rep,null);
227 } catch(Exception e) {
233 /** sends a request with one string parameter attached
235 @param par parameter - length and DT_STRING will be prepended
236 @return returned packet or <code>null</code> if something went wrong */
237 public RPacket request(int cmd, String par) {
239 byte[] b=par.getBytes(RConnection.transferCharset);
241 if ((sl&3)>0) sl=(sl&0xfffffc)+4; // make sure the length is divisible by 4
242 byte[] rq=new byte[sl+5];
244 for(i=0;i<b.length;i++)
246 while (i<sl) { // pad with 0
249 setHdr(DT_STRING,sl,rq,0);
250 return request(cmd,rq);
251 } catch (Exception e) {
257 /** sends a request with one string parameter attached
259 @param par parameter of the type DT_INT
260 @return returned packet or <code>null</code> if something went wrong */
261 public RPacket request(int cmd, int par) {
263 byte[] rq=new byte[8];
265 setHdr(DT_INT,4,rq,0);
266 return request(cmd,rq);
267 } catch (Exception e) {