/*
** $Id: trans.c,v 1.2 2008/12/18 04:17:02 dredd Exp $
**
** $Source: /cvsroot/swlpc/swlpc/runtime/trans.c,v $
** $Revision: 1.2 $
** $Date: 2008/12/18 04:17:02 $
** $State: Exp $
**
** Author: Mike McGaughey, 1993-1998
**
** See the file "Copying" distributed with this file.
*/
/*
* Transmission - build, buffering, unbuffering.
* Used by the dg receive stuff (comm1) and send stuff
* (interpret) to put together a buffer denoting a value.
* Can send anything except objects. No passing is added to
* any value (this saves enormous amounts of space).
*
* Warning: assumes 32 bit ints. Used to use network byte order,
* but we found that the mips compiler is extremely good with unsigned,
* etc, conversions, so now we send it raw.
*/
#include "proto.h"
/*
* Dgram packaging routines - these write a value into a (sendable) buffer,
* or read a buffer into a value. If the buffer is too short to write to,
* the send routine returns (-2). When receiving, if the incoming data appears
* to be truncated, it returns (-2) (but the passed value is set to as much
* as was read), and if there is an invalid type (or whatever), it returns (-1).
* Other negative values are returned for truncated or too-large or negative arrays.
*
* Note: they expect values (not values).
*
* Integers, etc, are sent in network byte order. It's hard to know what to
* do with floats (besides pray).
*/
#define Ty_0 0 /* A simple zero value - common */
#define Ty_sInt1 1 /* 1 byte signed int */
#define Ty_sInt2 2 /* 2 byte signed int */
#define Ty_sInt4 3 /* 4 byte signed int */
#define Ty_String 4 /* string, with terminator */
#define Ty_ArrayB 5 /* array; followed by (byte) nelem, then elements */
#define Ty_ArrayS 6 /* array, followed by (short) nelem, then elements */
#define Ty_Real 7 /* 4 byte real */
#define Ty_uInt1 8 /* 1 byte unsigned int (appears to be common) */
#define Ty_Image 9 /* Image; (short) width, (short) height, elements */
union {
unsigned char bytes[32];
signed char sc;
unsigned char uc;
signed short ss;
unsigned short us;
signed int si;
unsigned int ui;
float f;
signed long sl;
unsigned long ul;
} convbuf;
#define SPACELEFT(buff, end, need) (((end) - (buff) - 1) < (need)? 0 : 1)
/*
* Build a transmission buffer. If it's going to run out of space
* when fitting in the last value, always include the tag anyway (if it
* fits) - this fits in better with the possibility of the network
* truncating it.
*/
int buff_write_val(unsigned char * buff, int len, Val *from)
{
unsigned char * obuff = buff;/* length calcs in arrays */
signed long i; /* integer manipulations */
int j; /* counter */
int t; /* num array elts */
int wrlen; /* num bytes to dump */
unsigned char tag; /* tag to dump */
unsigned char *outbytes = &convbuf.bytes[0]; /* where to dump from */
if (!from)
return(0); /* a zero-length transmission */
if (len < 1)
return(-2); /* truncated before tag */
switch(from->type) {
default: /* can't send objects */
return(-1);
#ifdef GRAPHICS
case T_IMAGE: /* (short)width,(short)height,(width*height)elements */
{
short w, h;
Img *img;
img = from->u.img;
w = img->w;
h = img->h;
wrlen = w * h;
outbytes = img->pic;
tag = Ty_Image;
*(buff++) = tag; /* if we got here, the tag fits */
convbuf.us = w;
*(buff++) = convbuf.bytes[0];
*(buff++) = convbuf.bytes[1];
convbuf.us = h;
*(buff++) = convbuf.bytes[0];
*(buff++) = convbuf.bytes[1];
for (j=0; j < wrlen; j++)
*(buff++) = outbytes[j];
return wrlen + 5;
}
#endif
case T_NUMBER: /* 32 bit signed; choose the smallest representation */
i = from->u.number;
if (!i) {
tag = Ty_0;
wrlen = 0;
}
else if (i >= 0 && i < 256) {
tag = Ty_uInt1;
convbuf.uc = i;
wrlen = 1;
}
else if (i >= (-128) && i < 127) {
tag = Ty_sInt1;
convbuf.sc = i;
wrlen = 1;
}
else if (-(1<<15) <= i && i < 1<<15) {
tag = Ty_sInt2;
convbuf.ss = i;
wrlen = 2;
}
else {
tag = Ty_sInt4;
convbuf.si = i;
wrlen = 4;
}
break;
case T_REAL:
tag = Ty_Real;
convbuf.f = from->u.real;
wrlen = sizeof(float);
break;
case T_STRING:
tag = Ty_String;
outbytes = (unsigned char *)(from->u.string->str);
wrlen = 1 + from->u.string->length; /* null byte */
break;
case T_POINTER:
/* read the size */
t = from->u.vec->size;
if (t < 256) {
tag = Ty_ArrayB;
convbuf.uc = t;
wrlen = 1;
}
else {
tag = Ty_ArrayS;
convbuf.us = t;
wrlen = 2;
}
*(buff++) = tag;
if (wrlen + 1 > len)
return(-2);
for (j=0; j<wrlen; j++)
*(buff++) = convbuf.bytes[j];
/* dump the contents. while in here, len is relative to obuff. */
for (j = 0; j < t; j++) {
int did = buff_write_val(buff, len - (buff - obuff), &(from->u.vec->item[j]));
if (did < 0)
return(did);
buff += did;
}
return(buff - obuff);
}
*(buff++) = tag; /* if we got here, the tag fits */
if (len < 1 + wrlen)
return(-2);
for (j=0; j < wrlen; j++)
*(buff++) = outbytes[j];
return wrlen + 1;
}
/*
* The reverse of the above (we hope :).
* Build an value from an incoming buffer. The value
* is assumed initialised (to something that assign_value
* won't complain about).
* Strings are treated specially - if they are truncated,
* we can assume the network did it (or some asshole is sending
* spurious packets). So, if no null byte is found before the
* end of the buffer when processing a string, we place a null at the
* end (thus truncating one extra character). Ugly.
*/
int buff_read_val(unsigned char *buff, int len, Val *to)
{
unsigned char tag;
char * x; /* string copying */
int nread = 1;
int j, t;
Shared * y;
if (len < 1)
return(-2);
tag = *(buff++);
switch(tag) {
default: /* bad type */
return(-1);
case Ty_0:
to->type = T_NUMBER;
to->u.number = 0;
nread = 1;
break;
case Ty_uInt1:
if (len < 2)
return(-2);
to->type = T_NUMBER;
to->u.number = (*buff); /* buff is unsigned char */
nread = 2;
break;
case Ty_sInt1:
if (len < 2)
return(-2);
to->type = T_NUMBER;
convbuf.uc = *buff; /* store, without change */
to->u.number = convbuf.sc; /* but bring back with sign extension */
nread = 2;
break;
case Ty_sInt2:
if (len < 3)
return(-2);
to->type = T_NUMBER;
nread = 3;
convbuf.bytes[0] = *(buff++); /* these numbers need to change if we change orders */
convbuf.bytes[1] = *(buff++);
to->u.number = convbuf.ss; /* back as signed short */
break;
case Ty_sInt4:
if (len < 5)
return(-2);
to->type = T_NUMBER;
nread = 5;
for (j=0; j<4; j++)
convbuf.bytes[j] = *(buff++);
to->u.number = convbuf.si;
break;
case Ty_Real:
nread = 1 + sizeof(float);
if (len < nread)
return(-2);
to->type = T_REAL;
for (j=0; j<sizeof(float); j++)
convbuf.bytes[j] = *(buff++);
to->u.real = convbuf.f;
break;
case Ty_String: /* if we're being sent garbage (or if truncated), there may not be nulls, etc */
if (len < 2) /* need space for a null byte */
return(-2);
x = memchr(buff, 0, len-1); /* 1 byte was already read - tag. Find trailing null */
if (!x)
buff[len - 1] = 0; /* ruthlessly truncate it */
y = string_copy(buff);
to->type = T_STRING;
to->u.string = y;
if (!x) return(-2); /* ran out of space, but assigned it ok */
nread = 2 + y->length; /* tag + null + len */
break;
case Ty_ArrayB:
if (len < 2)
return(-2);
t = (*(buff++)); /* buff is unsigned chars */
nread = 2;
goto arr_read;
case Ty_ArrayS:
if (len < 3)
return(-2);
convbuf.bytes[0] = *(buff++);
convbuf.bytes[1] = *(buff++);
t = convbuf.us;
nread = 3;
arr_read:
if (t < 0)
return(-4);
if (t > MAX_ARRAY_SIZE)
return(-5);
assign_value(to, allocate_array(t));
// wipe(1);
/* we work directly in the assigned value, in case of truncation */
for (j = 0; j < t; j++) {
int did = buff_read_val(buff, len - nread, &(to->u.vec->item[j]));
if (did < 0)
return(did);
if (did < 1) /* end of array matches end of buffer */
break;
nread += did;
buff += did;
}
break;
#ifdef GRAPHICS
case Ty_Image:
{
int w, h, t;
Img * i;
if (len < 4)
return (-2);
convbuf.bytes[0] = *(buff++);
convbuf.bytes[1] = *(buff++);
w = convbuf.us;
convbuf.bytes[0] = *(buff++);
convbuf.bytes[1] = *(buff++);
h = convbuf.us;
i = create_image(w, h);
memcpy(i->pic, buff, len - 4);
to->u.img = i;
}
#endif
}
return nread;
}