]> gerrit.simantics Code Review - simantics/r.git/blob - org.simantics.r.scl/src/org/rosuda/REngine/Rserve/protocol/RTalk.java
b723c2c355791658c8b1a8f05aaae3c493720e3c
[simantics/r.git] / org.simantics.r.scl / src / org / rosuda / REngine / Rserve / protocol / RTalk.java
1 package org.rosuda.REngine.Rserve.protocol;
2
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 ---
6
7 import java.util.*;
8 import java.io.*;
9 import java.net.*;
10
11 import org.rosuda.REngine.Rserve.RConnection;
12
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
16     <p>
17     The current implementation supports long (0.3+/0102) data format only
18     up to 32-bit and only for incoming packets.
19     <p>
20     @version $Id$
21 */
22 public class RTalk {
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;
30
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;
33
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;
46     
47     public static final int CMD_setBufferSize=0x081;
48     public static final int CMD_setEncoding=0x082;
49
50     public static final int CMD_detachSession=0x030;
51     public static final int CMD_detachedVoidEval=0x031;
52     public static final int CMD_attachSession=0x032;
53
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; 
58     
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;
76    
77     InputStream is;
78     OutputStream os;
79     
80     /** constructor; parameters specify the streams
81         @param sis socket input stream
82         @param sos socket output stream */
83
84     public RTalk(InputStream sis, OutputStream sos) {
85         is=sis; os=sos;
86     }
87
88     /** writes bit-wise int to a byte buffer at specified position in Intel-endian form
89         @param v value to be written
90         @param buf buffer
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);
97     }
98
99     /** writes cmd/resp/type byte + 3/7 bytes len into a byte buffer at specified offset.
100         @param ty type/cmd/resp byte
101         @param len length
102         @param buf buffer
103         @param o offset
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.
105         */
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
114             buf[o]=0; o++;
115             buf[o]=0; o++;
116         }
117         return o;
118     }
119
120     /** creates a new header according to the type and length of the parameter
121         @param ty type/cmd/resp byte
122         @param len length */        
123     public static byte[] newHdr(int ty, int len) {
124         byte[] hdr=new byte[(len>0xfffff0)?8:4];
125         setHdr(ty,len,hdr,0);
126         return hdr;
127     }
128     
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));
136     }
137
138     /** converts bit-wise stored length from a header. "long" format is supported up to 32-bit
139         @param buf buffer
140         @param o offset of the header (length is at o+1)
141         @return length */
142     public static int getLen(byte[] buf, int o) {
143
144         return
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))
147         :
148         ((buf[o+1]&255)|((buf[o+2]&255)<<8)|((buf[o+3]&255)<<16));
149     }
150
151     /** converts bit-wise Intel-endian format into long
152         @param buf buffer
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;
158         hi<<=32; hi|=low;
159         return hi;
160     }
161
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);
165     }
166
167     /** sends a request with no attached parameters
168         @param cmd command
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);
173     }
174
175     /** sends a request with attached parameters
176         @param cmd command
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);
181     }
182
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).
186         @param cont contents
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) {
191         if (cont!=null) {
192             if (offset>=cont.length) { cont=null; len=0; }
193             else if (len>cont.length-offset) len=cont.length-offset;
194         }
195         if (offset<0) offset=0;
196         if (len<0) len=0;
197         int contlen=(cont==null)?0:len;
198         if (prefix!=null && prefix.length>0) contlen+=prefix.length;
199         byte[] hdr=new byte[16];
200         setInt(cmd,hdr,0);
201         setInt(contlen,hdr,4);
202         for(int i=8;i<16;i++) hdr[i]=0;
203         try {
204             if (cmd!=-1) {
205                 os.write(hdr);
206                 if (prefix!=null && prefix.length>0)
207                     os.write(prefix);
208                 if (cont!=null && cont.length>0)
209                     os.write(cont,offset,len);
210             }
211
212             byte[] ih=new byte[16];
213             if (is.read(ih)!=16)
214                 return null;
215             int rep=getInt(ih,0);
216             int rl =getInt(ih,4);
217             if (rl>0) {
218                 byte[] ct=new byte[rl];
219                 int n=0;
220                 while (n<rl) {
221                     int rd=is.read(ct,n,rl-n);
222                     n+=rd;
223                 }
224                 return new RPacket(rep,ct);
225             }
226             return new RPacket(rep,null);
227         } catch(Exception e) {
228             e.printStackTrace();
229             return null;
230         }
231     }
232
233     /** sends a request with one string parameter attached
234         @param cmd command
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) {
238         try {
239             byte[] b=par.getBytes(RConnection.transferCharset);
240             int sl=b.length+1;
241             if ((sl&3)>0) sl=(sl&0xfffffc)+4; // make sure the length is divisible by 4
242             byte[] rq=new byte[sl+5];
243             int i;
244             for(i=0;i<b.length;i++)
245                 rq[i+4]=b[i];
246             while (i<sl) { // pad with 0
247                 rq[i+4]=0; i++;
248             };
249             setHdr(DT_STRING,sl,rq,0);
250             return request(cmd,rq);
251         } catch (Exception e) {
252             e.printStackTrace();
253         }
254         return null;
255     }
256
257     /** sends a request with one string parameter attached
258         @param cmd command
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) {
262         try {
263             byte[] rq=new byte[8];
264             setInt(par,rq,4);
265             setHdr(DT_INT,4,rq,0);
266             return request(cmd,rq);
267         } catch (Exception e) {
268         };
269         return null;
270     }
271 }