+/* $Id$
+
+This file is part of gldbg
+Copyright © 2010 Mikko Rasa, Mikkosoft Productions
+Distributed under the GPL
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include "packet.h"
+#include "tmpalloc.h"
+
+struct GlOutPacket
+{
+ char *ptr;
+ unsigned length;
+ struct iovec *vec;
+};
+
+struct GlInPacket
+{
+ const char *ptr;
+ unsigned length;
+ unsigned chunk;
+};
+
+typedef struct GlOutPacket GlOutPacket;
+typedef struct GlInPacket GlInPacket;
+
+struct GlPacket
+{
+ union
+ {
+ GlOutPacket out;
+ GlInPacket in;
+ };
+};
+
+// XXX Should make this stuff truly re-entrant
+static char *out_buffer = 0;
+static struct iovec *iovecs = 0;
+static GlPacket packet;
+/*static char *in_buffer = 0;
+static unsigned in_length;*/
+
+static void next_vec(GlPacket *pkt)
+{
+ GlOutPacket *out = &pkt->out;
+
+ if(out->ptr!=out->vec->iov_base)
+ {
+ out->vec->iov_len = out->ptr-(char *)out->vec->iov_base;
+ out->length += out->vec->iov_len;
+ ++out->vec;
+ out->vec->iov_base = out->ptr;
+ }
+}
+
+static void reset(GlPacket *pkt)
+{
+ GlOutPacket *out = &pkt->out;
+
+ out->ptr = out_buffer;
+ out->vec = iovecs;
+ out->vec->iov_base = out->ptr;
+ out->length = 0;
+}
+
+GlPacket *packet_begin(unsigned short func)
+{
+ GlPacket *pkt;
+
+ if(!out_buffer)
+ out_buffer = (char *)malloc(1024);
+ if(!iovecs)
+ iovecs = (struct iovec *)malloc(16*sizeof(struct iovec));
+
+ pkt = &packet;
+ reset(pkt);
+
+ packet_write_int(pkt, 0);
+ packet_write_short(pkt, func);
+
+ return pkt;
+}
+
+void packet_send_partial(GlPacket *pkt, int fd)
+{
+ GlOutPacket *out = &pkt->out;
+
+ out->length |= 0x80000000;
+ packet_send(pkt, fd);
+
+ reset(pkt);
+
+ packet_write_int(pkt, 0);
+}
+
+void packet_send(GlPacket *pkt, int fd)
+{
+ GlOutPacket *out = &pkt->out;
+ struct iovec *v;
+
+ next_vec(pkt);
+ out->ptr = out_buffer;
+ packet_write_int(pkt, out->length);
+
+ for(v=iovecs; v!=out->vec; )
+ {
+ int ret;
+
+ ret = writev(fd, v, out->vec-v);
+ if(ret<0)
+ return;
+
+ while(v!=out->vec && (unsigned)ret>=v->iov_len)
+ {
+ ret -= v->iov_len;
+ ++v;
+ }
+
+ if(v!=out->vec)
+ {
+ v->iov_base += ret;
+ v->iov_len -= ret;
+ }
+ }
+}
+
+static void write_raw(GlPacket *pkt, const char *ptr, unsigned size, int byteswap)
+{
+ GlOutPacket *out = &pkt->out;
+ unsigned i;
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ if(byteswap)
+ {
+ ptr += size;
+ for(i=0; i<size; ++i)
+ *out->ptr++ = *--ptr;
+ }
+ else
+#endif
+ {
+ for(i=0; i<size; ++i)
+ *out->ptr++ = *ptr++;
+ }
+}
+
+void packet_write_char(GlPacket *pkt, char v)
+{
+ GlOutPacket *out = &pkt->out;
+ *out->ptr++ = v;
+}
+
+void packet_write_short(GlPacket *pkt, short v)
+{
+ write_raw(pkt, (char *)&v, sizeof(short), 1);
+}
+
+void packet_write_int(GlPacket *pkt, int v)
+{
+ write_raw(pkt, (char *)&v, sizeof(int), 1);
+}
+
+void packet_write_long(GlPacket *pkt, long v)
+{
+ write_raw(pkt, (char *)&v, sizeof(long), 1);
+}
+
+void packet_write_long_long(GlPacket *pkt, long long v)
+{
+ write_raw(pkt, (char *)&v, sizeof(long long), 1);
+}
+
+void packet_write_float(GlPacket *pkt, float v)
+{
+ write_raw(pkt, (char *)&v, sizeof(float), 1);
+}
+
+void packet_write_double(GlPacket *pkt, double v)
+{
+ write_raw(pkt, (char *)&v, sizeof(double), 1);
+}
+
+void packet_write_pointer(GlPacket *pkt, const void *p)
+{
+ write_raw(pkt, (char *)&p, sizeof(void *), 1);
+}
+
+void packet_write_data(GlPacket *pkt, const void *data, unsigned size)
+{
+ if(data)
+ {
+ GlOutPacket *out = &pkt->out;
+
+ packet_write_int(pkt, size);
+ next_vec(pkt);
+ out->vec->iov_base = (void *)data;
+ out->vec->iov_len = size;
+ out->length += size;
+ ++out->vec;
+ out->vec->iov_base = out->ptr;
+ }
+ else
+ packet_write_int(pkt, 0);
+}
+
+void packet_write_string(GlPacket *pkt, const char *s)
+{
+ packet_write_data(pkt, s, strlen(s)+1);
+}
+
+void packet_write_string_array(GlPacket *pkt, const char **sa, unsigned size)
+{
+ unsigned i;
+ size /= sizeof(const char *);
+ packet_write_int(pkt, size);
+ for(i=0; i<size; ++i)
+ packet_write_string(pkt, sa[i]);
+}
+
+GlPacket *packet_receive_str(const char *data, unsigned *len)
+{
+ const char *end = data+*len;
+ GlPacket *pkt;
+ GlInPacket *in;
+
+ if(*len<sizeof(int)+sizeof(short))
+ return 0;
+
+ pkt = &packet;
+ in = &pkt->in;
+ in->ptr = data;
+ in->chunk = *len;
+
+ packet_read_int(pkt, (int *)&in->chunk);
+
+ if(in->chunk&0x80000000)
+ {
+ in->chunk &= 0x7FFFFFFF;
+ in->length = in->chunk;
+
+ while(1)
+ {
+ if(end<in->ptr+in->length+sizeof(int))
+ return NULL;
+
+ GlPacket p;
+ p.in.ptr = in->ptr+in->length;
+ p.in.chunk = p.in.length = end-p.in.ptr;
+
+ unsigned chunk;
+ packet_read_int(&p, (int *)&chunk);
+
+ in->length += chunk&0x7FFFFFFF;
+ if(!(chunk&0x80000000))
+ break;
+ }
+ }
+ else
+ in->length = in->chunk;
+
+ if(end<in->ptr+in->length)
+ return NULL;
+
+ *len = in->ptr+in->length-data;
+
+ return pkt;
+}
+
+static void next_chunk(GlPacket *pkt)
+{
+ GlInPacket *in = &pkt->in;
+
+ if(in->length==0)
+ return;
+
+ in->chunk = in->length;
+ packet_read_int(pkt, (int *)&in->chunk);
+ in->chunk &= 0x7FFFFFFF;
+}
+
+static void read_raw(GlPacket *pkt, char *ptr, unsigned size, int byteswap)
+{
+ GlInPacket *in = &pkt->in;
+
+ if(in->chunk==0)
+ next_chunk(pkt);
+
+ if(in->chunk>=size)
+ {
+ unsigned i;
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ if(byteswap)
+ {
+ ptr += size;
+ for(i=0; i<size; ++i)
+ *--ptr = *in->ptr++;
+ }
+ else
+#endif
+ {
+ for(i=0; i<size; ++i)
+ *ptr++ = *in->ptr++;
+ }
+
+ in->length -= size;
+ in->chunk -= size;
+ }
+ else
+ {
+ memset(ptr, 0, size);
+ in->length -= in->chunk;
+ in->chunk = 0;
+ }
+}
+
+void packet_read_char(GlPacket *pkt, char *v)
+{
+ read_raw(pkt, v, 1, 0);
+}
+
+void packet_read_short(GlPacket *pkt, short *v)
+{
+ read_raw(pkt, (char *)v, sizeof(short), 1);
+}
+
+void packet_read_int(GlPacket *pkt, int *v)
+{
+ read_raw(pkt, (char *)v, sizeof(int), 1);
+}
+
+void packet_read_long(GlPacket *pkt, long *v)
+{
+ read_raw(pkt, (char *)v, sizeof(long), 1);
+}
+
+void packet_read_long_long(GlPacket *pkt, long long *v)
+{
+ read_raw(pkt, (char *)v, sizeof(long long), 1);
+}
+
+void packet_read_float(GlPacket *pkt, float *v)
+{
+ read_raw(pkt, (char *)v, sizeof(float), 1);
+}
+
+void packet_read_double(GlPacket *pkt, double *v)
+{
+ read_raw(pkt, (char *)v, sizeof(double), 1);
+}
+
+void packet_read_pointer(GlPacket *pkt, pointer *v)
+{
+ read_raw(pkt, (char *)v, sizeof(pointer), 1);
+}
+
+void packet_read_data(GlPacket *pkt, pointer *v)
+{
+ GlInPacket *in = &pkt->in;
+ int vlen;
+
+ packet_read_int(pkt, &vlen);
+ if(vlen)
+ *v = in->ptr;
+ else
+ *v = NULL;
+ in->ptr += vlen;
+ in->chunk -= vlen;
+ in->length -= vlen;
+}
+
+void packet_read_string(GlPacket *pkt, string *v)
+{
+ packet_read_data(pkt, (pointer *)v);
+}
+
+void packet_read_string_array(GlPacket *pkt, string **v)
+{
+ int count;
+ int i;
+ packet_read_int(pkt, &count);
+ *v = (string *)tmpalloc(count*sizeof(string));
+ for(i=0; i<count; ++i)
+ packet_read_string(pkt, *v+i);
+}