#include <cstring>
#include <string>
+#include <msp/core/hash.h>
#include <msp/core/maputils.h>
+#include <msp/strings/format.h>
#include <msp/strings/lexicalcast.h>
#include "protocol.h"
+#include "protocol_impl.h"
using namespace std;
-namespace {
-
-using Msp::Net::buffer_error;
-
-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 buffer_error("overflow");
- *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 buffer_error("overflow");
- 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 buffer_error("underflow");
- *--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 buffer_error("underflow");
- 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):
+ header_def(0),
next_packet_id(npi)
-{ }
+{
+ PacketDefBuilder<PacketHeader, NullSerializer<PacketHeader> >(*this, header_def, NullSerializer<PacketHeader>())
+ (&PacketHeader::type)(&PacketHeader::length);
+}
Protocol::~Protocol()
{
delete i->second;
}
-void Protocol::add_packet(PacketDefBase &pdef)
+void Protocol::add_packet(PacketDefBase *pdef)
{
- PacketDefBase *&ptr = packet_class_defs[pdef.get_class_id()];
+ PacketDefBase *&ptr = packet_class_defs[pdef->get_class_id()];
if(ptr)
+ {
+ packet_id_defs.erase(ptr->get_id());
delete ptr;
- ptr = &pdef;
- packet_id_defs[pdef.get_id()] = &pdef;
+ }
+ ptr = pdef;
+ if(unsigned id = pdef->get_id())
+ packet_id_defs[id] = pdef;
}
-const Protocol::PacketDefBase &Protocol::get_packet_by_class(unsigned id) const
+const Protocol::PacketDefBase &Protocol::get_packet_by_class_id(unsigned id) const
{
return *get_item(packet_class_defs, id);
}
return *get_item(packet_id_defs, id);
}
-unsigned Protocol::disassemble(ReceiverBase &rcv, const char *data, unsigned size) const
+unsigned Protocol::dispatch(ReceiverBase &rcv, const char *buf, 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)
+ PacketHeader header;
+ buf = header_def.deserialize(header, buf, buf+size);
+ if(header.length>size)
throw bad_packet("truncated");
- const PacketDefBase &pdef = get_packet_by_id(id);
- const char *ptr = pdef.disassemble(rcv, data+4, data+psz);
- return ptr-data;
+ const PacketDefBase &pdef = get_packet_by_id(header.type);
+ const char *ptr = pdef.dispatch(rcv, buf, buf+header.length);
+ return ptr-buf;
}
-unsigned Protocol::get_packet_size(const char *data, unsigned size) const
+unsigned Protocol::get_packet_size(const char *buf, unsigned size) const
{
if(size<4)
return 0;
- const unsigned char *udata = reinterpret_cast<const unsigned char *>(data);
- return (udata[2]<<8)+udata[3];
+ PacketHeader header;
+ header_def.deserialize(header, buf, buf+size);
+ return header.length;
}
-unsigned Protocol::get_hash() const
+UInt64 Protocol::get_hash() const
{
- // TODO
- return 123;
+ string description;
+ for(PacketMap::const_iterator i=packet_id_defs.begin(); i!=packet_id_defs.end(); ++i)
+ description += format("%d:%s\n", i->first, i->second->describe());
+ return hash64(description);
}
-void Protocol::assemble_header(char *buf, unsigned id, unsigned size)
+
+/* TODO These assumes the machine is little-endian; are there any relevant
+big-endian platforms these days? */
+template<typename T>
+char *Protocol::BasicSerializer<T>::serialize(const T &value, char *buf, char *end) const
{
- buf[0] = (id>>8)&0xFF;
- buf[1] = id&0xFF;
- buf[2] = (size>>8)&0xFF;
- buf[3] = size&0xFF;
+ if(end-buf<static_cast<int>(sizeof(T)))
+ throw buffer_error("overflow");
+
+ const char *ptr = reinterpret_cast<const char *>(&value)+sizeof(T);
+ for(unsigned i=0; i<sizeof(T); ++i)
+ *buf++ = *--ptr;
+
+ return buf;
}
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);
+const char *Protocol::BasicSerializer<T>::deserialize(T &value, const char *buf, const char *end) const
+{
+ if(end-buf<static_cast<int>(sizeof(T)))
+ throw buffer_error("underflow");
+
+ char *ptr = reinterpret_cast<char *>(&value)+sizeof(T);
+ for(unsigned i=0; i<sizeof(T); ++i)
+ *--ptr = *buf++;
+
+ return buf;
+}
+
+template char *Protocol::BasicSerializer<Int8>::serialize(const Int8 &, char *, char *) const;
+template char *Protocol::BasicSerializer<Int16>::serialize(const Int16 &, char *, char *) const;
+template char *Protocol::BasicSerializer<Int32>::serialize(const Int32 &, char *, char *) const;
+template char *Protocol::BasicSerializer<Int64>::serialize(const Int64 &, char *, char *) const;
+template char *Protocol::BasicSerializer<UInt8>::serialize(const UInt8 &, char *, char *) const;
+template char *Protocol::BasicSerializer<UInt16>::serialize(const UInt16 &, char *, char *) const;
+template char *Protocol::BasicSerializer<UInt32>::serialize(const UInt32 &, char *, char *) const;
+template char *Protocol::BasicSerializer<UInt64>::serialize(const UInt64 &, char *, char *) const;
+template char *Protocol::BasicSerializer<float>::serialize(const float &, char *, char *) const;
+template char *Protocol::BasicSerializer<double>::serialize(const double &, char *, char *) const;
+template const char *Protocol::BasicSerializer<Int8>::deserialize(Int8 &, const char *, const char *) const;
+template const char *Protocol::BasicSerializer<Int16>::deserialize(Int16 &, const char *, const char *) const;
+template const char *Protocol::BasicSerializer<Int32>::deserialize(Int32 &, const char *, const char *) const;
+template const char *Protocol::BasicSerializer<Int64>::deserialize(Int64 &, const char *, const char *) const;
+template const char *Protocol::BasicSerializer<UInt8>::deserialize(UInt8 &, const char *, const char *) const;
+template const char *Protocol::BasicSerializer<UInt16>::deserialize(UInt16 &, const char *, const char *) const;
+template const char *Protocol::BasicSerializer<UInt32>::deserialize(UInt32 &, const char *, const char *) const;
+template const char *Protocol::BasicSerializer<UInt64>::deserialize(UInt64 &, const char *, const char *) const;
+template const char *Protocol::BasicSerializer<float>::deserialize(float &, const char *, const char *) const;
+template const char *Protocol::BasicSerializer<double>::deserialize(double &, const char *, const char *) const;
+
+
+Protocol::StringSerializer::StringSerializer(const Protocol &p):
+ length_serializer(p)
+{ }
+
+char *Protocol::StringSerializer::serialize(const string &str, char *buf, char *end) const
+{
+ buf = length_serializer.serialize(str.size(), buf, end);
+ if(end-buf<static_cast<int>(str.size()))
+ throw buffer_error("overflow");
+ copy(str.begin(), str.end(), buf);
+ return buf+str.size();
+}
+
+const char *Protocol::StringSerializer::deserialize(string &str, const char *buf, const char *end) const
+{
+ UInt16 length;
+ buf = length_serializer.deserialize(length, buf, end);
+ if(end-buf<static_cast<int>(length))
+ throw buffer_error("underflow");
+ str.assign(buf, buf+length);
+ return buf+length;
+}
-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;
+Protocol::PacketDefBase::PacketDefBase(unsigned i):
+ id(i)
+{ }
+
+
+Protocol::PacketHeader::PacketHeader():
+ type(0),
+ length(0)
+{ }
+
+Protocol::PacketHeader::PacketHeader(UInt16 t, UInt16 l):
+ type(t),
+ length(l)
+{ }
+
} // namespace Net
} // namespace Msp