]> git.tdb.fi Git - libs/net.git/blobdiff - source/protocol.cpp
Add protocol framework
[libs/net.git] / source / protocol.cpp
diff --git a/source/protocol.cpp b/source/protocol.cpp
new file mode 100644 (file)
index 0000000..1a21c9e
--- /dev/null
@@ -0,0 +1,242 @@
+/* $Id$
+
+This file is part of libmspnet
+Copyright © 2009  Mikkosoft Productions, Mikko Rasa
+Distributed under the LGPL
+*/
+
+#include <cstring>
+#include <string>
+#include <msp/strings/lexicalcast.h>
+#include "protocol.h"
+
+using namespace std;
+
+namespace {
+
+template<typename T>
+class Assembler
+{
+public:
+       static char *assemble(const T &v, char *, char *);
+       static const char *disassemble(T &, const char *, const char *);
+};
+
+template<typename T>
+class Assembler<vector<T> >
+{
+public:
+       static char *assemble(const vector<T> &v, char *, char *);
+       static const char *disassemble(vector<T> &, const char *, const char *);
+};
+
+template<typename T>
+char *Assembler<T>::assemble(const T &v, char *data, char *end)
+{
+       // XXX Assumes little-endian
+       const char *ptr=reinterpret_cast<const char *>(&v)+sizeof(T);
+       for(unsigned i=0; i<sizeof(T); ++i)
+       {
+               if(data==end)
+                       throw Msp::Exception("Out of buffer space");
+               *data++=*--ptr;
+       }
+       return data;
+}
+
+template<>
+char *Assembler<string>::assemble(const string &v, char *data, char *end)
+{
+       data=Assembler<unsigned short>::assemble(v.size(), data, end);
+       if(end-data<static_cast<int>(v.size()))
+               throw Msp::Exception("Out of buffer space");
+       memcpy(data, v.data(), v.size());
+       return data+v.size();
+}
+
+template<typename T>
+char *Assembler<vector<T> >::assemble(const vector<T> &v, char *data, char *end)
+{
+       data=Assembler<unsigned short>::assemble(v.size(), data, end);
+       for(typename vector<T>::const_iterator i=v.begin(); i!=v.end(); ++i)
+               data=Assembler<T>::assemble(*i, data, end);
+       return data;
+}
+
+template<typename T>
+const char *Assembler<T>::disassemble(T &v, const char *data, const char *end)
+{
+       char *ptr=reinterpret_cast<char *>(&v)+sizeof(T);
+       for(unsigned i=0; i<sizeof(T); ++i)
+       {
+               if(data==end)
+                       throw Msp::Exception("Premature end of data");
+               *--ptr=*data++;
+       }
+       return data;
+}
+
+template<>
+const char *Assembler<string>::disassemble(string &v, const char *data, const char *end)
+{
+       unsigned short size;
+       data=Assembler<unsigned short>::disassemble(size, data, end);
+       if(end-data<size)
+               throw Msp::Exception("Premature end of data");
+       v.assign(data, data+size);
+       return data+size;
+}
+
+template<typename T>
+const char *Assembler<vector<T> >::disassemble(vector<T> &v, const char *data, const char *end)
+{
+       /* We assume that the vector is in pristine state - this holds because the
+       only code path leading here is from PacketDef<P>::disassemble, which creates
+       a new packet. */
+       unsigned short size;
+       data=Assembler<unsigned short>::disassemble(size, data, end);
+       for(unsigned i=0; i<size; ++i)
+       {
+               T u;
+               data=Assembler<T>::disassemble(u, data, end);
+               v.push_back(u);
+       }
+       return data;
+}
+
+}
+
+namespace Msp {
+namespace Net {
+
+Protocol::Protocol(unsigned npi):
+       next_packet_id(npi)
+{ }
+
+Protocol::~Protocol()
+{
+       for(map<unsigned, PacketDefBase *>::iterator i=packet_class_defs.begin(); i!=packet_class_defs.end(); ++i)
+               delete i->second;
+}
+
+void Protocol::add_packet(PacketDefBase &pdef)
+{
+       PacketDefBase *&ptr=packet_class_defs[pdef.get_class_id()];
+       if(ptr)
+               delete ptr;
+       ptr=&pdef;
+       packet_id_defs[pdef.get_id()]=&pdef;
+}
+
+const Protocol::PacketDefBase &Protocol::get_packet_by_class(unsigned id) const
+{
+       PacketMap::const_iterator i=packet_class_defs.find(id);
+       if(i==packet_class_defs.end())
+               throw KeyError("Unknown packet class", lexical_cast(id));
+       return *i->second;
+}
+
+const Protocol::PacketDefBase &Protocol::get_packet_by_id(unsigned id) const
+{
+       PacketMap::const_iterator i=packet_id_defs.find(id);
+       if(i==packet_id_defs.end())
+               throw KeyError("Unknown packet ID", lexical_cast(id));
+       return *i->second;
+}
+
+unsigned Protocol::disassemble(ReceiverBase &rcv, const char *data, unsigned size) const
+{
+       const unsigned char *udata=reinterpret_cast<const unsigned char *>(data);
+       unsigned id=(udata[0]<<8)+udata[1];
+       unsigned psz=(udata[2]<<8)+udata[3];
+       if(psz>size)
+               throw InvalidParameterValue("Not enough data for packet");
+       const PacketDefBase &pdef=get_packet_by_id(id);
+       const char *ptr=pdef.disassemble(rcv, data+4, data+psz);
+       return ptr-data;
+}
+
+unsigned Protocol::get_packet_size(const char *data, unsigned size) const
+{
+       if(size<4)
+               return 0;
+       const unsigned char *udata=reinterpret_cast<const unsigned char *>(data);
+       return (udata[2]<<8)+udata[3];
+}
+
+unsigned Protocol::get_hash() const
+{
+       // TODO
+       return 123;
+}
+
+void Protocol::assemble_header(char *buf, unsigned id, unsigned size)
+{
+       buf[0]=(id>>8)&0xFF;
+       buf[1]=id&0xFF;
+       buf[2]=(size>>8)&0xFF;
+       buf[3]=size&0xFF;
+}
+
+template<typename T>
+char *Protocol::assemble_field(const T &v, char *d, char *e)
+{ return Assembler<T>::assemble(v, d, e); }
+
+template char *Protocol::assemble_field<>(const char &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const signed char &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const unsigned char &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const short &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const unsigned short &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const int &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const unsigned &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const long &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const unsigned long &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const float &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const double &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const string &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const vector<char> &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const vector<signed char> &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const vector<unsigned char> &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const vector<short> &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const vector<unsigned short> &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const vector<int> &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const vector<unsigned> &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const vector<long> &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const vector<unsigned long> &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const vector<float> &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const vector<double> &v, char *d, char *e);
+template char *Protocol::assemble_field<>(const vector<string> &v, char *d, char *e);
+
+template<typename T>
+const char *Protocol::disassemble_field(T &v, const char *d, const char *e)
+{ return Assembler<T>::disassemble(v, d, e); }
+
+template const char *Protocol::disassemble_field<>(char &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(signed char &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(unsigned char &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(short &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(unsigned short &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(int &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(unsigned &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(long &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(unsigned long &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(float &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(double &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(string &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(vector<char> &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(vector<signed char> &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(vector<unsigned char> &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(vector<short> &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(vector<unsigned short> &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(vector<int> &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(vector<unsigned> &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(vector<long> &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(vector<unsigned long> &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(vector<float> &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(vector<double> &v, const char *d, const char *e);
+template const char *Protocol::disassemble_field<>(vector<string> &v, const char *d, const char *e);
+
+unsigned Protocol::PacketDefBase::next_class_id=1;
+
+} // namespace Net
+} // namespace Msp