binary._pike
Go to the documentation of this file.
1 /* Copyright (C) 2000-2006 Thomas Bopp, Thorsten Hampel, Ludger Merkens
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  * $Id: binary.pike,v 1.2 2009/05/06 19:23:10 astra Exp $
18  */
19 #include <coal.h>
20 #include <macros.h>
21 #include <assert.h>
22 #include <database.h>
23 //! Implementation of the COAL binary format. Consult coal documentation for
24 //! serialization and deserialization of sTeam objects and basic pike
25 //! types (as well as java).
26 class binary {
27 public:
28 
29 
30 
31 
32 
33  string wstr;
34  int iLastTID;
35 
36 object find_obj(int id)
37 {
38 #if constant(_Server)
39  return find_object(id);
40 #endif
41 }
42 
43 /**
44  * convert an array to binary string
45  *
46  * @param arr - the array to send
47  * @return the binary representation of the array
48  * @see send_binary
49  */
50 string send_array(array arr)
51 {
52  int i, sz;
53  string str;
54 
55  sz = sizeof(arr);
56  str = " ";
57  str[0] = CMD_TYPE_ARRAY;
58  str[1] = (sz & (255<<8)) >> 8;
59  str[2] = (sz & 255);
60 
61  for ( i = 0; i < sz; i++ )
62  str += send_binary(arr[i]);
63  return str;
64 }
65 
66 /**
67  * convert a mapping to a binary string
68  *
69  * @param map - the mapping to send
70  * @return the binary representation of the mapping
71  * @see send_binary
72  */
73 string send_mapping(mapping map)
74 {
75  int i, sz;
76  string str;
77  array ind;
78 
79  ind = indices(map);
80  sz = sizeof(ind);
81  str = " ";
82  str[0] = CMD_TYPE_MAPPING;
83  str[1] = (sz & (255<<8)) >> 8;
84  str[2] = (sz & 255);
85 
86  for ( i = 0; i < sz; i++ ) {
87  str += send_binary(ind[i]);
88  str += send_binary(map[ind[i]]);
89  }
90  return str;
91 }
92 
93 /**
94  * convert a variable to a binary string
95  *
96  * @param arg - the variable to convert
97  * @return the binary representation of the variable
98  * @see receive_binary
99  */
100 string
101 send_binary(mixed arg)
102 {
103  int len;
104  string str;
105 
106  if ( zero_type(arg) )
107  arg = 0; //send zero
108 
109  if ( floatp(arg) ) {
110  string floatstr;
111 
112  str = " ";
113  floatstr = sprintf("%F", arg);
114  str[0] = CMD_TYPE_FLOAT;
115  str[1] = floatstr[0];
116  str[2] = floatstr[1];
117  str[3] = floatstr[2];
118  str[4] = floatstr[3];
119  }
120  else if ( intp(arg) ) {
121  str = " ";
122  str[0] = CMD_TYPE_INT;
123  if ( arg < 0 ) {
124  arg = -arg;
125  arg = (arg ^ 0x7fffffff) + 1; // 32 bit
126  str[1] = ((arg & ( 0xff000000)) >> 24);
127  str[1] |= (0x80);
128  }
129  else {
130  str[1] = ((arg & ( 0xff000000)) >> 24);
131  }
132  str[2] = (arg & ( 255 << 16)) >> 16;
133  str[3] = (arg & ( 255 << 8)) >> 8;
134  str[4] = (arg & ( 255 ));
135  }
136  else if ( functionp(arg) ) {
137  str = " ";
138  string fname;
139  object o = function_object(arg);
140  if ( !objectp(o) || !functionp(o->get_object_id) )
141  fname = "(function)";
142  else
143  fname = "("+function_name(arg) + "():" + o->get_object_id() + ")";
144 
145  len = strlen(fname);
146  str[0] = CMD_TYPE_FUNCTION;
147  str[1] = (len & ( 255 << 24)) >> 24;
148  str[2] = (len & ( 255 << 16)) >> 16;
149  str[3] = (len & ( 255 << 8)) >> 8;
150  str[4] = (len & 255);
151 
152  str += fname;
153  }
154  else if ( programp(arg) ) {
155  string prg = master()->describe_program(arg);
156 
157  str = " ";
158  len = strlen(prg);
159  str[0] = CMD_TYPE_PROGRAM;
160  str[1] = (len & 0xff000000) >> 24;
161  str[2] = (len & 0x0000ff00) >> 16;
162  str[3] = (len & 0x00ff0000) >> 8;
163  str[4] = (len & 0x000000ff);
164  str += prg;
165 
166  }
167  else if ( stringp(arg) ) {
168  str = " ";
169  len = strlen(arg);
170  str[0] = CMD_TYPE_STRING;
171  str[1] = (len & 0xff000000) >> 24;
172  str[2] = (len & 0x00ff0000) >> 16;
173  str[3] = (len & 0x0000ff00) >> 8;
174  str[4] = (len & 0x000000ff);
175  str += arg;
176  }
177  else if ( objectp(arg) ) {
178  int id;
179  if ( functionp(arg->serialize_coal) &&
180  arg->serialize_coal!=arg->__null )
181  {
182  mixed map = arg->serialize_coal();
183  if ( mappingp(map) )
184  return send_mapping(map);
185  id = 0;
186  }
187  else if ( !functionp(arg->get_object_id) ) {
188  id = 0;
189  }
190  else if ( functionp(arg->status)
191  && (arg->status() < 0 || arg->status() == PSTAT_DELETED))
192  id = 0;
193  else
194  id = arg->get_object_id();
195 
196 
197  if ( id >= 0x80000000 ) {
198  str = " ";
199  str[0] = CMD_TYPE_OBJECT;
200  str += compose_object(id);
201  }
202  else {
203  str = " ";
204  str[0] = CMD_TYPE_OBJECT;
205  str[1] = (id & ( 255 << 24)) >> 24;
206  str[2] = (id & ( 255 << 16)) >> 16;
207  str[3] = (id & ( 255 << 8)) >> 8;
208  str[4] = (id & ( 255 ));
209  }
210 
211  if ( id == 0 || !functionp(arg->get_object_class) )
212  arg = 0;
213  else
214  arg = arg->get_object_class();
215 
216  string classStr = " ";
217  classStr[0] = (arg & ( 255 << 24)) >> 24;
218  classStr[1] = (arg & ( 255 << 16)) >> 16;
219  classStr[2] = (arg & ( 255 << 8)) >> 8;
220  classStr[3] = (arg & ( 255 ));
221  str += classStr;
222  }
223  else if ( arrayp(arg) )
224  return send_array(arg);
225  else if ( mappingp(arg) )
226  return send_mapping(arg);
227  else
228  error("Failed to serialize - unknown type of arg="+sprintf("%O",arg));
229  return str;
230 }
231 
232 /**
233  * a mapping was found at offset position pos
234  *
235  * @param pos - the position where the mapping starts in the received string
236  * @return the mapping and the end position of the mapping data
237  * @see receive_args
238  */
239 array(int|mapping)
240 receive_mapping(int pos)
241 {
242  mapping map;
243  int i, len;
244  mixed val;
245  mixed ind, v;
246 
247  map = ([ ]);
248  len = (wstr[pos] << 8) + wstr[pos+1];
249  pos += 2;
250 
251  for ( i = 0; i < len; i++ )
252  {
253  val = receive_args(pos);
254  pos = val[1];
255  ind = val[0];
256  val = receive_args(pos);
257  pos = val[1];
258  v = val[0];
259  map[ind] = v;
260  }
261  return ({ map, pos });
262 }
263 
264 /**
265  * an array was found in the received string
266  *
267  * @param pos - the startposition of the array data
268  * @return the array and the end position
269  * @see receive_args
270  */
271 array
272 receive_array(int pos)
273 {
274  int i, len;
275  array arr;
276  mixed val;
277 
278  len = (wstr[pos] << 8) + wstr[pos+1];
279  pos += 2;
280  arr = allocate(len);
281  for ( i = 0; i < len; i++ )
282  {
283  val = receive_args(pos);
284  pos = val[1];
285  arr[i] = val[0];
286  }
287  return ({ arr, pos });
288 }
289 
290 
291 /**
292  * receive a variable at position i, the type is not yet known
293  *
294  * @param i - the position where the variable starts,
295  * including type information
296  * @return the variable and end position in the binary string
297  * @see send_binary
298  */
299 mixed
300 receive_args(int i)
301 {
302  int type, tmp;
303  object obj;
304  int len;
305  mixed res;
306 
307  type = wstr[i];
308  switch(type) {
309  case CMD_TYPE_INT:
310  res = (int)((wstr[i+1]<<24) + (wstr[i+2] << 16) +
311  (wstr[i+3] << 8) + wstr[i+4]);
312  if ( res > 0 && res & (1<<31) ) {
313  // conversion from 32 to 64 bit if negative
314  res = (res ^ (0xffffffff)) + 1;
315  res *= -1; // negative
316  }
317  return ({ res, i+5 });
318  case CMD_TYPE_FLOAT:
319  string floatstr;
320  floatstr = " ";
321  floatstr[0] = wstr[i+1];
322  floatstr[1] = wstr[i+2];
323  floatstr[2] = wstr[i+3];
324  floatstr[3] = wstr[i+4];
325  sscanf(floatstr, "%4F", res);
326  return ({ res, i+5 });
327  case CMD_TYPE_OBJECT:
328  tmp = (int)((wstr[i+1]<<24) + (wstr[i+2] << 16) +
329  (wstr[i+3] << 8) + wstr[i+4]);
330  if ( tmp & 0x80000000 ) {
331  int oid_len, namespace_id;
332 
333  namespace_id = (tmp & 0x00ffff00) >> 8;
334  oid_len = (tmp & 0x000000ff);
335  int oid = 0;
336  for ( int x = 0; x < oid_len; x++ ) {
337  oid |= ( wstr[i + x + 5] << ( 8 * (oid_len-x-1) ));
338  }
339  tmp = oid | ( tmp << (oid_len*8) );
340  return ({ find_obj(tmp), i+oid_len+9 });
341  }
342  else {
343  obj = find_obj(tmp);
344  return ({ obj, i+9 });
345  }
346  case CMD_TYPE_PROGRAM:
347  case CMD_TYPE_STRING:
348  len = (int)((wstr[i+1]<<24)+(wstr[i+2]<<16) +
349  (wstr[i+3] << 8) + wstr[i+4]);
350  return ({ wstr[i+5..i+len-1+5], i+len+5 });
351  case CMD_TYPE_FUNCTION:
352  len = (int)((wstr[i+1]<<24)+(wstr[i+2]<<16) +
353  (wstr[i+3] << 8) + wstr[i+4]);
354  function f;
355  object o;
356  int id;
357  string fname;
358  sscanf(wstr[i+5..i+len-1+5], "(%s():%d)", fname, id);
359  o = find_obj(id);
360  if ( objectp(o) )
361  f = o->find_function(fname);
362  return ({ f, i+len+5 });
363  case CMD_TYPE_ARRAY:
364  return receive_array(i+1);
365  case CMD_TYPE_MAPPING:
366  return receive_mapping(i+1);
367  }
368  error("coal::Unknown type "+ type);
369 }
370 
371 protected:
372  string compose_object(int id)
373 {
374  string bitstr = " ";
375  while ( id > 0 ) {
376  int sid = ( id & 0xff );
377  bitstr[0] = sid;
378  bitstr = " " + bitstr;
379  id = (id >> 8);
380  }
381  return bitstr;
382 }
383 
384 public:
385 
386 protected:
387  int receive_object(string str)
388 {
389 }
390 
391 public:
392 
393 
394 protected:
395  string coal_compose_header(int t_id, int cmd, int o_id, int class_id)
396 {
397  string scmd = " ";
398  scmd[0] = COMMAND_BEGIN_MASK; /* command begin flag */
399  scmd[5] = (t_id & (255 << 24)) >> 24;
400  scmd[6] = (t_id & (255 << 16)) >> 16;
401  scmd[7] = (t_id & (255 << 8)) >> 8;
402  scmd[8] = t_id & 255;
403  scmd[9] = cmd%256;
404 
405  string bitstr = "";
406  if ( o_id >= 0x80000000 ) {
407  // new! long oid supported
408  bitstr = compose_object(o_id);
409  }
410  else {
411  bitstr = " ";
412  bitstr[0] = (o_id & (255 << 24)) >> 24;
413  bitstr[1] = (o_id & (255 << 16)) >> 16;
414  bitstr[2] = (o_id & (255 << 8)) >> 8;
415  bitstr[3] = o_id & 255;
416  }
417  scmd += bitstr;
418  string strClass = " ";
419  strClass[0] = (class_id & (255 << 24)) >> 24;
420  strClass[1] = (class_id & (255 << 16)) >> 16;
421  strClass[2] = (class_id & (255 << 8)) >> 8;
422  strClass[3] = class_id & 255;
423  scmd += strClass;
424  return scmd;
425 }
426 
427 public:
428 
429 /**
430  * converts a coal command to a binary string
431  *
432  * @param t_id - the transaction id
433  * @param cmd - the command
434  * @param o_id - the relevant object id
435  * @param args - the additional args to convert
436  * @return the binary string representation
437  * @see send_binary
438  */
439 string
440 coal_compose(int t_id, int cmd, object|int o_id, int class_id, mixed args)
441 {
442  string scmd;
443  if (objectp(o_id) )
444  o_id = o_id->get_object_id();
445 #if !constant(coal.coal_compose)
446  scmd = coal_compose_header(t_id, cmd, o_id, class_id);
447  scmd += send_binary(args);
448 #else
449  scmd = coal.coal_compose(t_id, cmd, o_id, class_id);
450  string params = coal.coal_serialize(args);
451 #ifdef VERIFY_CMOD
452  // lots of overhead !
453  if ( scmd != coal_compose_header(t_id, cmd, o_id, class_id) )
454  FATAL("FATAL ERROR in communication header\n%O", scmd);
455  string binary_params = send_binary(args);
456  if ( params != binary_params ) {
457  FATAL("FATAL ERROR in coal arguments when serializing: %O",
458  args);
459  string prefix = String.common_prefix(({params, binary_params}));
460  FATAL("Common Prefix:\n%O", prefix);
461  FATAL("Rest coal.coal_serialize:\n%O", params[strlen(prefix)-1..]);
462  FATAL("Rest serialize:\n%O", binary_params[strlen(prefix)-1..]);
463  }
464 #endif
465  scmd += params;
466 
467 #endif
468 
469  int slen = strlen(scmd);
470  scmd[1] = (slen & 0xff000000) >> 24;
471  scmd[2] = (slen & 0x00ff0000) >> 16;
472  scmd[3] = (slen & 0x0000ff00) >> 8;
473  scmd[4] = (slen & 0x000000ff);
474 
475  return scmd;
476 }
477 
478 protected:
479  array|int coal_uncompose_header(string str)
480 {
481  int cmd, t_id, len, i, slen, id, n;
482  int offset = 18;
483 
484  if ( !stringp(str) )
485  return -1;
486  slen = strlen(str);
487  if ( slen == 0 )
488  return -1;
489  for ( n = 0; n < slen-10; n++ )
490  if ( str[n] == COMMAND_BEGIN_MASK )
491  break;
492  if ( n >= slen-18 )
493  return -1;
494 
495  len = (int)((str[n+1]<<24) + (str[n+2]<<16) + (str[n+3]<<8) +str[n+4]);
496  if ( len+n > slen || len < 12 ) // need whole string in buffer
497  return 0;
498 
499  t_id = (int)((str[n+5] << 24) + (str[n+6]<<16) +
500  (str[n+7]<<8) + str[n+8]);
501  cmd = (int)str[n+9];
502  id = (int)((str[n+10] << 24) + (str[n+11]<<16) +
503  (str[n+12]<<8) + str[n+13]);
504 
505  int nid, sid;
506  nid = sid = 0;
507  // highest bit set - read additional information about server and namespace
508  if ( id & 0x80000000 ) {
509  int oid_len, namespace_id;
510 
511  namespace_id = (id & 0x00ffff00) >> 8;
512  oid_len = (id & 0x00000fff);
513 
514  if ( oid_len + offset > slen )
515  throw( ({ "Protocol Error - OID length larger than package",
516  ({ }), t_id, cmd, id }) );
517  int oid = 0;
518  for ( i = 0; i < oid_len; i++ ) {
519  oid |= ( str[n + i + offset] << ( 8 * (oid_len-i-1) ));
520  }
521  offset += oid_len;
522  id = oid | ( id << (oid_len*8) );
523  }
524  return ({ t_id, cmd, id, n, len });
525 }
526 
527 
528 /**
529  * receive_binary
530  *
531  * @param str - what is received
532  * @return array containing { tid, cmd, obj_id }, args, unparsed rest
533  * @see send_binary
534  */
535 protected:
536  mixed
537 receive_binary(string str)
538 {
539  int cmd, t_id, len, id, n;
540  mixed result, args;
541  int offset = 18;
542 
543 #if !constant(coal.coal_uncompose)
544  result = coal_uncompose_header(str);
545  if ( !arrayp(result) )
546  return result;
547  [ t_id, cmd, id, n, len] = result;
548 
549  wstr = str;
550  args = receive_args(n+offset);
551  args = args[0];
552 #else
553  if (strlen(str) < 18)
554  return -1;
555  result = coal.coal_uncompose(str);
556  if ( !arrayp(result) )
557  return result;
558  [ t_id, cmd, id, n, len] = result;
559  args = coal.coal_unserialize(str, n+offset, find_obj);
560 #if constant(check_equal)
561 #ifdef VERIFY_CMOD
562  wstr = str;
563  array verify = coal_uncompose_header(str);
564  if ( t_id != verify[0] || cmd != verify[1] || id != verify[2] || n != verify[3] ||
565  len != verify[4] )
566  {
567  FATAL("Error in COAL communication header (receive): \nParsed: %O, Expected: %O",
568  result, verify);
569  }
570  array params = receive_args(n+offset);
571  if (!check_equal(args, params[0])) {
572  FATAL("Error in COAL Communication params (receive):\n Parsed: %O, Expected: %O",
573  args, params[0]);
574  }
575 #endif
576 #endif
577 #endif
578  iLastTID = t_id;
579 
580  wstr = "";
581  return ({ ({ t_id, cmd, id }), args, str[n+len..] });
582 }
583 
584 public:
585 
586 
587 
588 
589 
590 
591 
592 
593 };