X-Git-Url: http://xvm.mit.edu/gitweb/invirt/third/libt4.git/blobdiff_plain/5fd8cc8409d0efadc07dfe8d6774ad9ff477663d..f0dcb6b97d6d40f67698d1f71ac26970f1776f82:/rpc/marshall.h diff --git a/rpc/marshall.h b/rpc/marshall.h index e0370d1..7b716e2 100644 --- a/rpc/marshall.h +++ b/rpc/marshall.h @@ -1,261 +1,249 @@ #ifndef marshall_h #define marshall_h -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "lang/verify.h" -#include "lang/algorithm.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; -}; - -struct reply_header { - reply_header(int x=0, int r=0): xid(x), ret(r) {} - int xid; - int ret; -}; +#include "types.h" +#include "rpc_protocol.h" -typedef uint64_t rpc_checksum_t; -typedef int rpc_sz_t; - -enum { - //size of initial buffer allocation - DEFAULT_RPC_SZ = 1024, -#if RPC_CHECKSUMMING - //size of rpc_header includes a 4-byte int to be filled by tcpchan and uint64_t checksum - RPC_HEADER_SZ = static_max::value + sizeof(rpc_sz_t) + sizeof(rpc_checksum_t) -#else - RPC_HEADER_SZ = static_max::value + sizeof(rpc_sz_t) -#endif -}; +// +// Marshall and unmarshall objects +// 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); -#if RPC_CHECKSUMMING - _ind += sizeof(rpc_checksum_t); -#endif - 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); -#if RPC_CHECKSUMMING - _ind += sizeof(rpc_checksum_t); -#endif - 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; - } + 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 + 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 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; + } }; -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 &); 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); -#if RPC_CHECKSUMMING - _ind += sizeof(rpc_checksum_t); -#endif - 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); -#if RPC_CHECKSUMMING - _ind += sizeof(rpc_checksum_t); -#endif - unpack(&h->xid); - unpack(&h->ret); - _ind = RPC_HEADER_SZ; - } + private: + string buf_; + size_t index_ = 0; + bool ok_ = false; + + public: + template + 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 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 inline T _grab() { T t; *this >> t; return t; } }; -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 marshall & -operator<<(marshall &m, std::vector 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 inline marshall & +tuple_marshall_imp(marshall & m, tuple & t, tuple_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(t))...}; + return m; } -template unmarshall & -operator>>(unmarshall &u, std::vector &v) -{ - unsigned n; - u >> n; - for(unsigned i = 0; i < n; i++){ - C z; - u >> z; - v.push_back(z); - } - return u; +template marshall & +operator<<(marshall & m, tuple && t) { + using Indices = typename make_tuple_indices::type; + return tuple_marshall_imp(m, t, Indices()); } -template marshall & -operator<<(marshall &m, const std::map &d) { - typename std::map::const_iterator i; +template inline unmarshall & +tuple_unmarshall_imp(unmarshall & u, tuple t, tuple_indices) { + (void)pass{(u >> get(t))...}; + return u; +} - m << (unsigned int) d.size(); +template unmarshall & +operator>>(unmarshall & u, tuple && t) { + using Indices = typename make_tuple_indices::type; + return tuple_unmarshall_imp(u, t, Indices()); +} - for (i = d.begin(); i != d.end(); i++) { - m << i->first << i->second; - } - return m; +// +// 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 inline typename +enable_if::value, marshall>::type & +operator<<(marshall & m, const A & x) { + m << (unsigned int)x.size(); + for (const auto & a : x) + m << a; + return m; } -template unmarshall & -operator>>(unmarshall &u, std::map &d) { - unsigned int n; - u >> n; +// visible for type A if A::emplace_back(a) makes sense +template inline typename +enable_if::value, unmarshall>::type & +operator>>(unmarshall & u, A & x) { + unsigned n = u._grab(); + x.clear(); + while (n--) + x.emplace_back(u._grab()); + return u; +} - d.clear(); +// std::pair +template inline marshall & +operator<<(marshall & m, const pair & d) { + return m << d.first << d.second; +} + +template inline unmarshall & +operator>>(unmarshall & u, pair & d) { + return u >> d.first >> d.second; +} + +// std::map +template inline unmarshall & +operator>>(unmarshall & u, map & x) { + unsigned n = u._grab(); + x.clear(); + while (n--) + x.emplace(u._grab>()); + return u; +} + +// std::string +inline marshall & operator<<(marshall & m, const string & s) { + m << (uint32_t)s.size(); + m.rawbytes(s.data(), s.size()); + return m; +} + +inline unmarshall & operator>>(unmarshall & u, string & s) { + uint32_t sz = u._grab(); + if (u.ok()) { + s.resize(sz); + u.rawbytes(&s[0], sz); + } + return u; +} + +// +// Marshalling for strongly-typed enums +// + +template typename enable_if::value, marshall>::type & +operator<<(marshall & m, E e) { + return m << from_enum(e); +} + +template typename enable_if::value, unmarshall>::type & +operator>>(unmarshall & u, E & e) { + e = to_enum(u._grab>()); + return u; +} + +// +// Recursive marshalling +// + +inline marshall & operator<<(marshall & m, marshall & n) { + return m << n.content(); +} - for (unsigned int lcv = 0; lcv < n; lcv++) { - A a; - B b; - u >> a >> b; - d[a] = b; - } - return u; +inline unmarshall & operator>>(unmarshall & u, unmarshall & v) { + v = unmarshall(u._grab(), false); + return u; } #endif