#ifndef marshall_h
#define marshall_h
-#include <iostream>
-#include <sstream>
-#include <string>
-#include <vector>
-#include <map>
-#include <stdlib.h>
-#include <string.h>
-#include <cstddef>
-#include <inttypes.h>
-#include "lang/verify.h"
-
-struct req_header {
- req_header(int x=0, int p=0, int c = 0, int s = 0, int xi = 0):
- xid(x), proc(p), clt_nonce(c), srv_nonce(s), xid_rep(xi) {}
- int xid;
- int proc;
- unsigned int clt_nonce;
- unsigned int srv_nonce;
- int xid_rep;
+#include "types.h"
+#include "rpc_protocol.h"
+
+//
+// Marshall and unmarshall objects
+//
+
+class marshall {
+ private:
+ string buf_ = string(rpc_protocol::DEFAULT_RPC_SZ, 0); // Raw bytes buffer
+ size_t index_ = rpc_protocol::RPC_HEADER_SZ; // Read/write head position
+
+ public:
+ template <typename... Args>
+ marshall(const Args & ... args) {
+ (void)pass{(*this << args)...};
+ }
+
+ void rawbytes(const void *p, size_t n) {
+ if (index_+n > buf_.size())
+ buf_.resize(index_+n);
+ copy((char *)p, (char *)p+n, &buf_[index_]);
+ index_ += n;
+ }
+
+ // with header
+ inline operator string() const { return buf_.substr(0,index_); }
+ // without header
+ inline string content() const { return buf_.substr(rpc_protocol::RPC_HEADER_SZ,index_-rpc_protocol::RPC_HEADER_SZ); }
+
+ // letting S be a defaulted template parameter forces the compiler to
+ // delay looking up operator<<(marshall &, rpc_sz_t) until we define it
+ // (i.e. we define an operator for marshalling uint32_t)
+ template <class T, class S=rpc_protocol::rpc_sz_t> inline void
+ pack_header(const T & h) {
+ VERIFY(sizeof(T)+sizeof(S) <= rpc_protocol::RPC_HEADER_SZ);
+ size_t saved_sz = index_;
+ index_ = 0;
+ *this << (S)(saved_sz - sizeof(S)) << (T)h;
+ index_ = saved_sz;
+ }
};
-struct reply_header {
- reply_header(int x=0, int r=0): xid(x), ret(r) {}
- int xid;
- int ret;
+class unmarshall {
+ private:
+ string buf_;
+ size_t index_ = 0;
+ bool ok_ = false;
+
+ public:
+ template <typename... Args>
+ unmarshall(const string & s, bool has_header, Args && ... args)
+ : buf_(s),index_(rpc_protocol::RPC_HEADER_SZ) {
+ if (!has_header)
+ buf_.insert(0, rpc_protocol::RPC_HEADER_SZ, 0);
+ ok_ = (buf_.size() >= rpc_protocol::RPC_HEADER_SZ);
+ (void)pass{(*this >> args)...};
+ }
+
+ bool ok() const { return ok_; }
+ bool okdone() const { return ok_ && index_ == buf_.size(); }
+
+ void rawbytes(void * t, size_t n) {
+ if (index_+n > buf_.size())
+ ok_ = false;
+ VERIFY(ok_);
+ copy(&buf_[index_], &buf_[index_+n], (char *)t);
+ index_ += n;
+ }
+
+ template <class T> inline void
+ unpack_header(T & h) {
+ VERIFY(sizeof(T)+sizeof(rpc_protocol::rpc_sz_t) <= rpc_protocol::RPC_HEADER_SZ);
+ // first 4 bytes hold length field
+ index_ = sizeof(rpc_protocol::rpc_sz_t);
+ *this >> h;
+ index_ = rpc_protocol::RPC_HEADER_SZ;
+ }
+
+ template <class T> inline T _grab() { T t; *this >> t; return t; }
};
-typedef int rpc_sz_t;
+//
+// Marshalling for plain old data
+//
+
+#define MARSHALL_RAW_NETWORK_ORDER_AS(_c_, _d_) \
+inline marshall & operator<<(marshall & m, _c_ x) { _d_ y = hton((_d_)x); m.rawbytes(&y, sizeof(_d_)); return m; } \
+inline unmarshall & operator>>(unmarshall & u, _c_ & x) { _d_ y; u.rawbytes(&y, sizeof(_d_)); x = (_c_)ntoh(y); return u; }
+
+#define MARSHALL_RAW_NETWORK_ORDER(_c_) MARSHALL_RAW_NETWORK_ORDER_AS(_c_, _c_)
+
+MARSHALL_RAW_NETWORK_ORDER_AS(bool, uint8_t)
+MARSHALL_RAW_NETWORK_ORDER(uint8_t)
+MARSHALL_RAW_NETWORK_ORDER(int8_t)
+MARSHALL_RAW_NETWORK_ORDER(uint16_t)
+MARSHALL_RAW_NETWORK_ORDER(int16_t)
+MARSHALL_RAW_NETWORK_ORDER(uint32_t)
+MARSHALL_RAW_NETWORK_ORDER(int32_t)
+MARSHALL_RAW_NETWORK_ORDER_AS(size_t, uint32_t)
+MARSHALL_RAW_NETWORK_ORDER(uint64_t)
+MARSHALL_RAW_NETWORK_ORDER(int64_t)
+
+//
+// Marshalling for tuples (used to implement marshalling for structs)
+//
+
+// In order to iterate over the tuple elements, we first need a template
+// parameter pack containing the tuple's indices. The function templates named
+// *_imp below accept an empty tag struct as their last argument, and use its
+// template arguments to index the tuple. The operator<< overloads instantiate
+// the appropriate tag struct to make this possible.
+
+template <class... Args, size_t... Indices> inline marshall &
+tuple_marshall_imp(marshall & m, tuple<Args...> & t, tuple_indices<Indices...>) {
+ // Note that brace initialization is used for the empty structure "pack",
+ // forcing the comma-separated expressions expanded from the parameter pack
+ // to be evaluated in order. Order matters because the elements must be
+ // serialized consistently! The empty struct resulting from construction
+ // is discarded.
+ (void)pass{(m << get<Indices>(t))...};
+ return m;
+}
-//size of initial buffer allocation
-#define DEFAULT_RPC_SZ 1024
-#define RPC_HEADER_SZ (std::max(sizeof(req_header), sizeof(reply_header)) + sizeof(rpc_sz_t))
+template <class... Args> marshall &
+operator<<(marshall & m, tuple<Args...> && t) {
+ using Indices = typename make_tuple_indices<sizeof...(Args)>::type;
+ return tuple_marshall_imp(m, t, Indices());
+}
-class marshall {
- private:
- char *_buf; // Base of the raw bytes buffer (dynamically readjusted)
- int _capa; // Capacity of the buffer
- int _ind; // Read/write head position
-
- public:
- marshall() {
- _buf = (char *) malloc(sizeof(char)*DEFAULT_RPC_SZ);
- VERIFY(_buf);
- _capa = DEFAULT_RPC_SZ;
- _ind = RPC_HEADER_SZ;
- }
-
- ~marshall() {
- if (_buf)
- free(_buf);
- }
-
- int size() { return _ind;}
- char *cstr() { return _buf;}
-
- void rawbyte(unsigned char);
- void rawbytes(const char *, int);
-
- // Return the current content (excluding header) as a string
- std::string get_content() {
- return std::string(_buf+RPC_HEADER_SZ,_ind-RPC_HEADER_SZ);
- }
-
- // Return the current content (excluding header) as a string
- std::string str() {
- return get_content();
- }
-
- void pack(int i);
-
- void pack_req_header(const req_header &h) {
- int saved_sz = _ind;
- //leave the first 4-byte empty for channel to fill size of pdu
- _ind = sizeof(rpc_sz_t);
- pack(h.xid);
- pack(h.proc);
- pack((int)h.clt_nonce);
- pack((int)h.srv_nonce);
- pack(h.xid_rep);
- _ind = saved_sz;
- }
-
- void pack_reply_header(const reply_header &h) {
- int saved_sz = _ind;
- //leave the first 4-byte empty for channel to fill size of pdu
- _ind = sizeof(rpc_sz_t);
- pack(h.xid);
- pack(h.ret);
- _ind = saved_sz;
- }
-
- void take_buf(char **b, int *s) {
- *b = _buf;
- *s = _ind;
- _buf = NULL;
- _ind = 0;
- return;
- }
-};
+template <class... Args, size_t... Indices> inline unmarshall &
+tuple_unmarshall_imp(unmarshall & u, tuple<Args & ...> t, tuple_indices<Indices...>) {
+ (void)pass{(u >> get<Indices>(t))...};
+ return u;
+}
-marshall& operator<<(marshall &, bool);
-marshall& operator<<(marshall &, unsigned int);
-marshall& operator<<(marshall &, int);
-marshall& operator<<(marshall &, unsigned char);
-marshall& operator<<(marshall &, char);
-marshall& operator<<(marshall &, unsigned short);
-marshall& operator<<(marshall &, short);
-marshall& operator<<(marshall &, unsigned long long);
-marshall& operator<<(marshall &, const std::string &);
-
-template <class C> marshall &
-operator<<(marshall &m, std::vector<C> v)
-{
- m << (unsigned int) v.size();
- for(unsigned i = 0; i < v.size(); i++)
- m << v[i];
- return m;
+template <class... Args> unmarshall &
+operator>>(unmarshall & u, tuple<Args & ...> && t) {
+ using Indices = typename make_tuple_indices<sizeof...(Args)>::type;
+ return tuple_unmarshall_imp(u, t, Indices());
}
-template <class A, class B> marshall &
-operator<<(marshall &m, const std::map<A,B> &d) {
- typename std::map<A,B>::const_iterator i;
+//
+// Marshalling for structs or classes containing a MEMBERS declaration
+//
+
+// Implements struct marshalling via tuple marshalling of members.
+#define MARSHALLABLE_STRUCT(_c_) \
+inline unmarshall & operator>>(unmarshall & u, _c_ & a) { return u >> a._tuple_(); } \
+inline marshall & operator<<(marshall & m, const _c_ a) { return m << a._tuple_(); }
+
+// our first two marshallable structs...
+MARSHALLABLE_STRUCT(rpc_protocol::request_header)
+MARSHALLABLE_STRUCT(rpc_protocol::reply_header)
+
+//
+// Marshalling for STL containers
+//
+
+// this overload is visible for type A only if A::cbegin and A::cend exist
+template <class A> inline typename
+enable_if<is_const_iterable<A>::value, marshall>::type &
+operator<<(marshall & m, const A & x) {
+ m << (unsigned int)x.size();
+ for (const auto & a : x)
+ m << a;
+ return m;
+}
- m << (unsigned int) d.size();
+// visible for type A if A::emplace_back(a) makes sense
+template <class A> inline typename
+enable_if<supports_emplace_back<A>::value, unmarshall>::type &
+operator>>(unmarshall & u, A & x) {
+ unsigned n = u._grab<unsigned>();
+ x.clear();
+ while (n--)
+ x.emplace_back(u._grab<typename A::value_type>());
+ return u;
+}
- for (i = d.begin(); i != d.end(); i++) {
- m << i->first << i->second;
- }
- return m;
+// std::pair<A, B>
+template <class A, class B> inline marshall &
+operator<<(marshall & m, const pair<A,B> & d) {
+ return m << d.first << d.second;
}
-template <class A> marshall &
-operator<<(marshall &m, const std::list<A> &d) {
- m << std::vector<A>(d.begin(), d.end());
- return m;
+template <class A, class B> inline unmarshall &
+operator>>(unmarshall & u, pair<A,B> & d) {
+ return u >> d.first >> d.second;
}
-template <class A, class B> marshall &
-operator<<(marshall &m, const std::pair<A,B> &d) {
- m << d.first;
- m << d.second;
- return m;
+// std::map<A, B>
+template <class A, class B> inline unmarshall &
+operator>>(unmarshall & u, map<A,B> & x) {
+ unsigned n = u._grab<unsigned>();
+ x.clear();
+ while (n--)
+ x.emplace(u._grab<pair<A,B>>());
+ return u;
}
-class unmarshall {
- private:
- char *_buf;
- int _sz;
- int _ind;
- bool _ok;
- public:
- unmarshall(): _buf(NULL),_sz(0),_ind(0),_ok(false) {}
- unmarshall(char *b, int sz): _buf(b),_sz(sz),_ind(),_ok(true) {}
- unmarshall(const std::string &s) : _buf(NULL),_sz(0),_ind(0),_ok(false)
- {
- //take the content which does not exclude a RPC header from a string
- take_content(s);
- }
- ~unmarshall() {
- if (_buf) free(_buf);
- }
-
- //take contents from another unmarshall object
- void take_in(unmarshall &another);
-
- //take the content which does not exclude a RPC header from a string
- void take_content(const std::string &s) {
- _sz = s.size()+RPC_HEADER_SZ;
- _buf = (char *)realloc(_buf,_sz);
- VERIFY(_buf);
- _ind = RPC_HEADER_SZ;
- memcpy(_buf+_ind, s.data(), s.size());
- _ok = true;
- }
-
- bool ok() { return _ok; }
- char *cstr() { return _buf;}
- bool okdone();
- unsigned int rawbyte();
- void rawbytes(std::string &s, unsigned int n);
-
- int ind() { return _ind;}
- int size() { return _sz;}
- void unpack(int *); //non-const ref
- void take_buf(char **b, int *sz) {
- *b = _buf;
- *sz = _sz;
- _sz = _ind = 0;
- _buf = NULL;
- }
-
- void unpack_req_header(req_header *h) {
- //the first 4-byte is for channel to fill size of pdu
- _ind = sizeof(rpc_sz_t);
- unpack(&h->xid);
- unpack(&h->proc);
- unpack((int *)&h->clt_nonce);
- unpack((int *)&h->srv_nonce);
- unpack(&h->xid_rep);
- _ind = RPC_HEADER_SZ;
- }
-
- void unpack_reply_header(reply_header *h) {
- //the first 4-byte is for channel to fill size of pdu
- _ind = sizeof(rpc_sz_t);
- unpack(&h->xid);
- unpack(&h->ret);
- _ind = RPC_HEADER_SZ;
- }
-};
+// std::string
+inline marshall & operator<<(marshall & m, const string & s) {
+ m << (uint32_t)s.size();
+ m.rawbytes(s.data(), s.size());
+ return m;
+}
-unmarshall& operator>>(unmarshall &, bool &);
-unmarshall& operator>>(unmarshall &, unsigned char &);
-unmarshall& operator>>(unmarshall &, char &);
-unmarshall& operator>>(unmarshall &, unsigned short &);
-unmarshall& operator>>(unmarshall &, short &);
-unmarshall& operator>>(unmarshall &, unsigned int &);
-unmarshall& operator>>(unmarshall &, int &);
-unmarshall& operator>>(unmarshall &, unsigned long long &);
-unmarshall& operator>>(unmarshall &, std::string &);
-
-template <class C> unmarshall &
-operator>>(unmarshall &u, std::vector<C> &v)
-{
- unsigned n;
- u >> n;
- v.clear();
- while (n--) {
- C c;
- u >> c;
- v.push_back(c);
+inline unmarshall & operator>>(unmarshall & u, string & s) {
+ uint32_t sz = u._grab<uint32_t>();
+ if (u.ok()) {
+ s.resize(sz);
+ u.rawbytes(&s[0], sz);
}
- return u;
+ return u;
}
-template <class A, class B> unmarshall &
-operator>>(unmarshall &u, std::map<A,B> &d) {
- unsigned n;
- u >> n;
- d.clear();
- while (n--) {
- A a;
- B b;
- u >> a >> b;
- d[a] = b;
- }
- return u;
+//
+// Marshalling for strongly-typed enums
+//
+
+template <class E> typename enable_if<is_enum<E>::value, marshall>::type &
+operator<<(marshall & m, E e) {
+ return m << from_enum(e);
}
-template <class C> unmarshall &
-operator>>(unmarshall &u, std::list<C> &l) {
- unsigned n;
- u >> n;
- l.clear();
- while (n--) {
- C c;
- u >> c;
- l.push_back(c);
- }
+template <class E> typename enable_if<is_enum<E>::value, unmarshall>::type &
+operator>>(unmarshall & u, E & e) {
+ e = to_enum<E>(u._grab<enum_type_t<E>>());
return u;
}
-template <class A, class B> unmarshall &
-operator>>(unmarshall &u, std::pair<A,B> &d) {
- return u >> d.first >> d.second;
+//
+// Recursive marshalling
+//
+
+inline marshall & operator<<(marshall & m, marshall & n) {
+ return m << n.content();
+}
+
+inline unmarshall & operator>>(unmarshall & u, unmarshall & v) {
+ v = unmarshall(u._grab<string>(), false);
+ return u;
}
#endif