1 /* Copyright (C) 2000-2006 Thomas Bopp, Thorsten Hampel, Ludger Merkens
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.
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.
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
17 * $Id: binary.pike,v 1.2 2009/05/06 19:23:10 astra Exp $
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).
36 object find_obj(int id)
39 return find_object(id);
44 * convert an array to binary string
46 * @param arr - the array to send
47 * @return the binary representation of the array
50 string send_array(array arr)
57 str[0] = CMD_TYPE_ARRAY;
58 str[1] = (sz & (255<<8)) >> 8;
61 for ( i = 0; i < sz; i++ )
62 str += send_binary(arr[i]);
67 * convert a mapping to a binary string
69 * @param map - the mapping to send
70 * @return the binary representation of the mapping
73 string send_mapping(mapping map)
82 str[0] = CMD_TYPE_MAPPING;
83 str[1] = (sz & (255<<8)) >> 8;
86 for ( i = 0; i < sz; i++ ) {
87 str += send_binary(ind[i]);
88 str += send_binary(map[ind[i]]);
94 * convert a variable to a binary string
96 * @param arg - the variable to convert
97 * @return the binary representation of the variable
101 send_binary(mixed arg)
106 if ( zero_type(arg) )
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];
120 else if ( intp(arg) ) {
122 str[0] = CMD_TYPE_INT;
125 arg = (arg ^ 0x7fffffff) + 1; // 32 bit
126 str[1] = ((arg & ( 0xff000000)) >> 24);
130 str[1] = ((arg & ( 0xff000000)) >> 24);
132 str[2] = (arg & ( 255 << 16)) >> 16;
133 str[3] = (arg & ( 255 << 8)) >> 8;
134 str[4] = (arg & ( 255 ));
136 else if ( functionp(arg) ) {
139 object o = function_object(arg);
140 if ( !objectp(o) || !functionp(o->get_object_id) )
141 fname = "(function)";
143 fname = "("+function_name(arg) + "():" + o->get_object_id() + ")";
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);
154 else if ( programp(arg) ) {
155 string prg = master()->describe_program(arg);
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);
167 else if ( stringp(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);
177 else if ( objectp(arg) ) {
179 if ( functionp(arg->serialize_coal) &&
180 arg->serialize_coal!=arg->__null )
182 mixed map = arg->serialize_coal();
184 return send_mapping(map);
187 else if ( !functionp(arg->get_object_id) ) {
190 else if ( functionp(arg->status)
191 && (arg->status() < 0 || arg->status() == PSTAT_DELETED))
194 id = arg->get_object_id();
197 if ( id >= 0x80000000 ) {
199 str[0] = CMD_TYPE_OBJECT;
200 str += compose_object(id);
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 ));
211 if ( id == 0 || !functionp(arg->get_object_class) )
214 arg = arg->get_object_class();
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 ));
223 else if ( arrayp(arg) )
224 return send_array(arg);
225 else if ( mappingp(arg) )
226 return send_mapping(arg);
228 error("Failed to serialize - unknown type of arg="+sprintf("%O",arg));
233 * a mapping was found at offset position pos
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
240 receive_mapping(int pos)
248 len = (wstr[pos] << 8) + wstr[pos+1];
251 for ( i = 0; i < len; i++ )
253 val = receive_args(pos);
256 val = receive_args(pos);
261 return ({ map, pos });
265 * an array was found in the received string
267 * @param pos - the startposition of the array data
268 * @return the array and the end position
272 receive_array(int pos)
278 len = (wstr[pos] << 8) + wstr[pos+1];
281 for ( i = 0; i < len; i++ )
283 val = receive_args(pos);
287 return ({ arr, pos });
292 * receive a variable at position i, the type is not yet known
294 * @param i - the position where the variable starts,
295 * including type information
296 * @return the variable and end position in the binary string
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
317 return ({ res, i+5 });
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;
333 namespace_id = (tmp & 0x00ffff00) >> 8;
334 oid_len = (tmp & 0x000000ff);
336 for ( int x = 0; x < oid_len; x++ ) {
337 oid |= ( wstr[i + x + 5] << ( 8 * (oid_len-x-1) ));
339 tmp = oid | ( tmp << (oid_len*8) );
340 return ({ find_obj(tmp), i+oid_len+9 });
344 return ({ obj, i+9 });
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]);
358 sscanf(wstr[i+5..i+len-1+5], "(%s():%d)", fname, id);
361 f = o->find_function(fname);
362 return ({ f, i+len+5 });
364 return receive_array(i+1);
365 case CMD_TYPE_MAPPING:
366 return receive_mapping(i+1);
368 error("coal::Unknown type "+ type);
372 string compose_object(int id)
376 int sid = ( id & 0xff );
378 bitstr = " " + bitstr;
387 int receive_object(string str)
395 string coal_compose_header(int t_id, int cmd, int o_id, int class_id)
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;
406 if ( o_id >= 0x80000000 ) {
407 // new! long oid supported
408 bitstr = compose_object(o_id);
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;
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;
430 * converts a coal command to a binary string
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
440 coal_compose(int t_id, int cmd, object|int o_id, int class_id, mixed args)
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);
449 scmd = coal.coal_compose(t_id, cmd, o_id, class_id);
450 string params = coal.coal_serialize(args);
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",
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..]);
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);
479 array|int coal_uncompose_header(string str)
481 int cmd, t_id, len, i, slen, id, n;
489 for ( n = 0; n < slen-10; n++ )
490 if ( str[n] == COMMAND_BEGIN_MASK )
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
499 t_id = (int)((str[n+5] << 24) + (str[n+6]<<16) +
500 (str[n+7]<<8) + str[n+8]);
502 id = (int)((str[n+10] << 24) + (str[n+11]<<16) +
503 (str[n+12]<<8) + str[n+13]);
507 // highest bit set - read additional information about server and namespace
508 if ( id & 0x80000000 ) {
509 int oid_len, namespace_id;
511 namespace_id = (id & 0x00ffff00) >> 8;
512 oid_len = (id & 0x00000fff);
514 if ( oid_len + offset > slen )
515 throw( ({ "Protocol Error - OID length larger than package",
516 ({ }), t_id, cmd, id }) );
518 for ( i = 0; i < oid_len; i++ ) {
519 oid |= ( str[n + i + offset] << ( 8 * (oid_len-i-1) ));
522 id = oid | ( id << (oid_len*8) );
524 return ({ t_id, cmd, id, n, len });
531 * @param str - what is received
532 * @return array containing { tid, cmd, obj_id }, args, unparsed rest
537 receive_binary(string str)
539 int cmd, t_id, len, id, n;
543 #if !constant(coal.coal_uncompose)
544 result = coal_uncompose_header(str);
545 if ( !arrayp(result) )
547 [ t_id, cmd, id, n, len] = result;
550 args = receive_args(n+offset);
553 if (strlen(str) < 18)
555 result = coal.coal_uncompose(str);
556 if ( !arrayp(result) )
558 [ t_id, cmd, id, n, len] = result;
559 args = coal.coal_unserialize(str, n+offset, find_obj);
560 #if constant(check_equal)
563 array verify = coal_uncompose_header(str);
564 if ( t_id != verify[0] || cmd != verify[1] || id != verify[2] || n != verify[3] ||
567 FATAL("Error in COAL communication header (receive): \nParsed: %O, Expected: %O",
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",
581 return ({ ({ t_id, cmd, id }), args, str[n+len..] });