]> git.tdb.fi Git - libs/net.git/blobdiff - source/protocol.h
Add protocol framework
[libs/net.git] / source / protocol.h
diff --git a/source/protocol.h b/source/protocol.h
new file mode 100644 (file)
index 0000000..082687b
--- /dev/null
@@ -0,0 +1,161 @@
+/* $Id$
+
+This file is part of libmspnet
+Copyright © 2009  Mikkosoft Productions, Mikko Rasa
+Distributed under the LGPL
+*/
+
+#ifndef MSP_NET_PROTOCOL_H_
+#define MSP_NET_PROTOCOL_H_
+
+#include <map>
+#include <vector>
+#include <msp/core/except.h>
+#include "receiver.h"
+
+namespace Msp {
+namespace Net {
+
+class Protocol
+{
+private:
+       class PacketDefBase
+       {
+       protected:
+               unsigned id;
+
+               PacketDefBase(unsigned i): id(i) { }
+       public:
+               virtual ~PacketDefBase() { }
+               virtual unsigned get_class_id() const =0;
+               unsigned get_id() const { return id; }
+               virtual const char *disassemble(ReceiverBase &, const char *, const char *) const =0;
+
+               static unsigned next_class_id;
+       };
+
+       template<typename P>
+       class FieldBase
+       {
+       protected:
+               FieldBase() { }
+       public:
+               virtual ~FieldBase() { }
+               virtual char *assemble(const P &, char *, char *) const =0;
+               virtual const char *disassemble(P &, const char *, const char *) const =0;
+       };
+
+       template<typename P, typename T>
+       class Field: public FieldBase<P>
+       {
+       private:
+               T P::*ptr;
+
+       public:
+               Field(T P::*p): ptr(p) { }
+
+               virtual char *assemble(const P &p, char *d, char *e) const
+               { return assemble_field(p.*ptr, d, e); }
+
+               virtual const char *disassemble(P &p, const char *d, const char *e) const
+               { return disassemble_field(p.*ptr, d, e); }
+       };
+
+protected:
+       template<typename P>
+       class PacketDef: public PacketDefBase
+       {
+       private:
+               std::vector<FieldBase<P> *> fields;
+
+       public:
+               PacketDef(unsigned i): PacketDefBase(i)
+               { if(!class_id) class_id=next_class_id++; }
+
+               virtual unsigned get_class_id() const { return class_id; }
+
+               template<typename T>
+               PacketDef &operator()(T P::*p)
+               { fields.push_back(new Field<P, T>(p)); return *this; }
+
+               char *assemble(const P &p, char *d, char *e) const
+               {
+                       for(typename std::vector<FieldBase<P> *>::const_iterator i=fields.begin(); i!=fields.end(); ++i)
+                               d=(*i)->assemble(p, d, e);
+                       return d;
+               }
+
+               const char *disassemble(ReceiverBase &r, const char *d, const char *e) const
+               {
+                       PacketReceiver<P> *prcv=dynamic_cast<PacketReceiver<P> *>(&r);
+                       if(!prcv)
+                               throw Exception("Packet type not supported by receiver");
+                       P pkt;
+                       for(typename std::vector<FieldBase<P> *>::const_iterator i=fields.begin(); i!=fields.end(); ++i)
+                               d=(*i)->disassemble(pkt, d, e);
+                       prcv->receive(pkt);
+                       return d;
+               }
+
+               static unsigned class_id;
+       };
+
+       typedef std::map<unsigned, PacketDefBase *> PacketMap;
+
+       unsigned next_packet_id;
+       PacketMap packet_class_defs;
+       PacketMap packet_id_defs;
+
+       Protocol(unsigned =1);
+public:
+       ~Protocol();
+
+private:
+       void add_packet(PacketDefBase &);
+
+protected:
+       template<typename P>
+       PacketDef<P> &add()
+       {
+               PacketDef<P> *pdef=new PacketDef<P>(next_packet_id++);
+               add_packet(*pdef);
+               return *pdef;
+       }
+
+       const PacketDefBase &get_packet_by_class(unsigned) const;
+       const PacketDefBase &get_packet_by_id(unsigned) const;
+
+public:
+       template<typename P>
+       unsigned assemble(const P &pkt, char *buf, unsigned size) const
+       {
+               unsigned id=PacketDef<P>::class_id;
+               const PacketDef<P> &pdef=static_cast<const PacketDef<P> &>(get_packet_by_class(id));
+               char *ptr=pdef.assemble(pkt, buf+4, buf+size);
+               assemble_header(buf, pdef.get_id(), (size=ptr-buf));
+               return size;
+       }
+
+       unsigned disassemble(ReceiverBase &, const char *, unsigned) const;
+
+       unsigned get_packet_size(const char *, unsigned) const;
+
+       unsigned get_hash() const;
+
+private:
+       static void assemble_header(char *, unsigned, unsigned);
+
+       template<typename T>
+       static char *assemble_field(const T &, char *, char *);
+
+       template<typename T>
+       static const char *disassemble_field(T &, const char *, const char *);
+};
+
+template<typename P>
+unsigned Protocol::PacketDef<P>::class_id=0;
+
+} // namespace Net
+} // namespace Msp
+
+#endif