Cosmetic improvements.
[invirt/third/libt4.git] / rpc / marshall.h
index 448240b..7b716e2 100644 (file)
 #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"
 
-struct reply_header {
-       reply_header(int x=0, int r=0): xid(x), ret(r) {}
-       int xid;
-       int ret;
-};
+//
+// Marshall and unmarshall objects
+//
 
-typedef int rpc_sz_t;
+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
 
-//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))
+    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;
+        }
 
-struct pass {
-    template<typename... Args> inline pass(Args&&...) {}
+        // 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;
+        }
 };
 
-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;
-               }
-
-        template <typename... Args> marshall(const Args&... args) : marshall() {
-            (void)pass{(*this << args)...};
+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;
         }
 
-               ~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 T> inline T _grab() { T t; *this >> t; return t; }
 };
 
-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;
+//
+// 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;
 }
 
-template <class A, class B> marshall &
-operator<<(marshall &m, const std::map<A,B> &d) {
-       typename std::map<A,B>::const_iterator i;
-
-       m << (unsigned int) d.size();
+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());
+}
 
-       for (i = d.begin(); i != d.end(); i++) {
-               m << i->first << i->second;
-       }
-       return m;
+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;
 }
 
-template <class A> marshall &
-operator<<(marshall &m, const std::list<A> &d) {
-    m << std::vector<A>(d.begin(), d.end());
-    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::pair<A,B> &d) {
-    m << d.first;
-    m << d.second;
+//
+// 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;
 }
 
-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;
-               }
-
-        template <class A>
-        inline A grab() {
-            A a;
-            *this >> a;
-            return a;
-        }
-};
+// 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;
+}
 
-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);
-    }
-       return u;
+// 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, 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;
+template <class A, class B> inline unmarshall &
+operator>>(unmarshall & u, pair<A,B> & d) {
+    return u >> d.first >> d.second;
 }
 
-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);
-    }
+// 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;
 }
 
-template <class A, class B> unmarshall &
-operator>>(unmarshall &u, std::pair<A,B> &d) {
-    return u >> d.first >> d.second;
+// std::string
+inline marshall & operator<<(marshall & m, const string & s) {
+    m << (uint32_t)s.size();
+    m.rawbytes(s.data(), s.size());
+    return m;
 }
 
-template <size_t...> struct tuple_indices {};
-template <size_t S, class IntTuple, size_t E> struct make_indices_imp;
-template <size_t S, size_t ...Indices, size_t E> struct make_indices_imp<S, tuple_indices<Indices...>, E> {
-    typedef typename make_indices_imp<S+1, tuple_indices<Indices..., S>, E>::type type;
-};
-template <size_t E, size_t ...Indices> struct make_indices_imp<E, tuple_indices<Indices...>, E> {
-    typedef tuple_indices<Indices...> type;
-};
-template <size_t E, size_t S = 0> struct make_tuple_indices {
-    typedef typename make_indices_imp<S, tuple_indices<>, E>::type type;
-};
-
-struct VerifyOnFailure {
-    static inline int unmarshall_args_failure() {
-        VERIFY(0);
-        return 0;
+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);
     }
-};
-
-typedef std::function<int(unmarshall &, marshall &)> handler;
-
-using std::move;
-using std::get;
-using std::tuple;
-using std::decay;
+    return u;
+}
 
-#include <iostream>
+//
+// Marshalling for strongly-typed enums
+//
 
-template <class F, class R, class args_type, size_t ...Indices>
-typename std::enable_if<!std::is_member_function_pointer<F>::value, int>::type
-invoke(F f, void *, R & r, args_type & t, tuple_indices<Indices...>) {
-    return f(r, move(get<Indices>(t))...);
+template <class E> typename enable_if<is_enum<E>::value, marshall>::type &
+operator<<(marshall & m, E e) {
+    return m << from_enum(e);
 }
 
-template <class F, class C, class R, class args_type, size_t ...Indices>
-typename std::enable_if<std::is_member_function_pointer<F>::value, int>::type
-invoke(F f, C *c, R & r, args_type & t, tuple_indices<Indices...>) {
-    return (c->*f)(r, move(get<Indices>(t))...);
+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 Functor,
-          class Instance,
-          class Signature,
-          class ErrorHandler=VerifyOnFailure> struct marshalled_func_imp;
-
-template <class F, class C, class ErrorHandler, class R, class... Args>
-struct marshalled_func_imp<F, C, int(R&, Args...), ErrorHandler> {
-    using result_type = R;
-    using args_type = tuple<typename decay<Args>::type...>;
-    using index_type = typename make_tuple_indices<sizeof...(Args)>::type;
-
-    static inline int call(F f, C *c, unmarshall &u, marshall &m) {
-        args_type t{std::move(std::tuple<Args...>{u.grab<Args>()...})};
-        if (!u.okdone())
-            return ErrorHandler::unmarshall_args_failure();
-        R r;
-        int b = invoke(f, c, r, t, index_type());
-        m << r;
-        return b;
-    }
-
-    static inline handler *wrap(F f, C *c=nullptr) {
-        typename decay<F>::type f_ = f;
-        return new handler([f_, c](unmarshall &u, marshall &m) -> int {
-            return call(f_, c, u, m);
-        });
-    }
-};
-
-template <class Functor,
-          class Signature,
-          class ErrorHandler=VerifyOnFailure> struct marshalled_func;
-
-template <class F, class ErrorHandler, class... Args>
-struct marshalled_func<F, int(*)(Args...), ErrorHandler> :
-    public marshalled_func_imp<F, void, int(Args...), ErrorHandler> {};
+//
+// Recursive marshalling
+//
 
-template <class F, class ErrorHandler, class C, class... Args>
-struct marshalled_func<F, int(C::*)(Args...), ErrorHandler> :
-    public marshalled_func_imp<F, C, int(Args...), ErrorHandler> {};
+inline marshall & operator<<(marshall & m, marshall & n) {
+    return m << n.content();
+}
 
-template <class F, class ErrorHandler, class Signature>
-struct marshalled_func<F, std::function<Signature>, ErrorHandler> :
-    public marshalled_func_imp<F, void, Signature, ErrorHandler> {};
+inline unmarshall & operator>>(unmarshall & u, unmarshall & v) {
+    v = unmarshall(u._grab<string>(), false);
+    return u;
+}
 
 #endif