X-Git-Url: http://xvm.mit.edu/gitweb/invirt/third/libt4.git/blobdiff_plain/a4175b2e216a20b86cc872dea8a08005c60617a5..46fb2b4bbe3a0a8516ab04cfafa895a882c70f86:/rpc/marshall.h diff --git a/rpc/marshall.h b/rpc/marshall.h index 644a220..98856e4 100644 --- a/rpc/marshall.h +++ b/rpc/marshall.h @@ -1,259 +1,438 @@ #ifndef marshall_h #define marshall_h -#include -#include -#include -#include -#include -#include -#include +#include "types.h" +#include #include -#include -#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 + +using proc_t = uint32_t; +using status_t = int32_t; + +struct request_header { + request_header(int x=0, proc_t p=0, unsigned c=0, unsigned s=0, int xi=0) : + xid(x), proc(p), clt_nonce(c), srv_nonce(s), xid_rep(xi) {} + int xid; + proc_t 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; + reply_header(int x=0, int r=0): xid(x), ret(r) {} + int xid; + int ret; }; -typedef uint64_t rpc_checksum_t; +template inline T hton(T t); + +constexpr union { uint32_t i; uint8_t is_little_endian; } endianness{1}; + +template<> inline uint8_t hton(uint8_t t) { return t; } +template<> inline int8_t hton(int8_t t) { return t; } +template<> inline uint16_t hton(uint16_t t) { return htons(t); } +template<> inline int16_t hton(int16_t t) { return (int16_t)htons((uint16_t)t); } +template<> inline uint32_t hton(uint32_t t) { return htonl(t); } +template<> inline int32_t hton(int32_t t) { return (int32_t)htonl((uint32_t)t); } +template<> inline uint64_t hton(uint64_t t) { + if (!endianness.is_little_endian) + return t; + return (uint64_t)htonl((uint32_t)(t >> 32)) | ((uint64_t)htonl((uint32_t)t) << 32); +} +template<> inline int64_t hton(int64_t t) { return (int64_t)hton((uint64_t)t); } +template<> inline request_header hton(request_header h) { return {hton(h.xid), hton(h.proc), hton(h.clt_nonce), hton(h.srv_nonce), hton(h.xid_rep)}; } +template<> inline reply_header hton(reply_header h) { return {hton(h.xid), hton(h.ret)}; } + +template inline T ntoh(T t) { return hton(t); } + typedef int rpc_sz_t; -//size of initial buffer allocation +//size of initial buffer allocation #define DEFAULT_RPC_SZ 1024 -#define RPC_HEADER_SZ_NO_CHECKSUM (std::max(sizeof(req_header), sizeof(reply_header)) + sizeof(rpc_sz_t)) -#if RPC_CHECKSUMMING -//size of rpc_header includes a 4-byte int to be filled by tcpchan and uint64_t checksum -#define RPC_HEADER_SZ (RPC_HEADER_SZ_NO_CHECKSUM + sizeof(rpc_checksum_t)) -#else -#define RPC_HEADER_SZ (RPC_HEADER_SZ_NO_CHECKSUM) -#endif +#define RPC_HEADER_SZ (max(sizeof(request_header), sizeof(reply_header)) + sizeof(rpc_sz_t)) + +struct pass { template inline pass(Args&&...) {} }; 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: + char *buf_; // Base of the raw bytes buffer (dynamically readjusted) + size_t capacity_; // Capacity of the buffer + size_t index_; // Read/write head position + + inline void reserve(size_t n) { + if((index_+n) > capacity_){ + capacity_ += max(capacity_, n); + VERIFY (buf_ != NULL); + buf_ = (char *)realloc(buf_, capacity_); + VERIFY(buf_); + } + } + public: + template + marshall(const Args&... args) { + buf_ = (char *) malloc(sizeof(char)*DEFAULT_RPC_SZ); + VERIFY(buf_); + capacity_ = DEFAULT_RPC_SZ; + index_ = RPC_HEADER_SZ; + (void)pass{(*this << args)...}; + } + + ~marshall() { + if (buf_) + free(buf_); + } + + size_t size() { return index_;} + char *cstr() { return buf_;} + const char *cstr() const { return buf_;} + + void rawbyte(uint8_t x) { + reserve(1); + buf_[index_++] = (int8_t)x; + } + + void rawbytes(const char *p, size_t n) { + reserve(n); + memcpy(buf_+index_, p, n); + index_ += n; + } + + // Return the current content (excluding header) as a string + string get_content() { + return string(buf_+RPC_HEADER_SZ,index_-RPC_HEADER_SZ); + } + + // Return the current content (excluding header) as a string + string str() { + return get_content(); + } + + void pack_req_header(const request_header &h); + void pack_reply_header(const reply_header &h); + + void take_buf(char **b, size_t *s) { + *b = buf_; + *s = index_; + buf_ = NULL; + index_ = 0; + return; + } }; + 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 &); +marshall& operator<<(marshall &, uint32_t); +marshall& operator<<(marshall &, int32_t); +marshall& operator<<(marshall &, uint8_t); +marshall& operator<<(marshall &, int8_t); +marshall& operator<<(marshall &, uint16_t); +marshall& operator<<(marshall &, int16_t); +marshall& operator<<(marshall &, uint64_t); +marshall& operator<<(marshall &, const string &); + +template 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 marshall & +operator<<(marshall &m, const pair &d) { + return m << d.first << d.second; +} + +template +using enum_type_t = typename enable_if::value, typename underlying_type::type>::type; +template constexpr inline enum_type_t from_enum(E e) noexcept { return (enum_type_t)e; } +template constexpr inline E to_enum(enum_type_t value) noexcept { return (E)value; } + +template typename enable_if::value, marshall>::type & +operator<<(marshall &m, E e) { + return m << from_enum(e); +} + +class unmarshall; + +unmarshall& operator>>(unmarshall &, bool &); +unmarshall& operator>>(unmarshall &, uint8_t &); +unmarshall& operator>>(unmarshall &, int8_t &); +unmarshall& operator>>(unmarshall &, uint16_t &); +unmarshall& operator>>(unmarshall &, int16_t &); +unmarshall& operator>>(unmarshall &, uint32_t &); +unmarshall& operator>>(unmarshall &, int32_t &); +unmarshall& operator>>(unmarshall &, size_t &); +unmarshall& operator>>(unmarshall &, uint64_t &); +unmarshall& operator>>(unmarshall &, int64_t &); +unmarshall& operator>>(unmarshall &, string &); +template typename enable_if::value, unmarshall>::type & +operator>>(unmarshall &u, E &e); 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: + char *buf_; + size_t sz_; + size_t index_; + bool ok_; + + inline bool ensure(size_t n); + public: + unmarshall(): buf_(NULL),sz_(0),index_(0),ok_(false) {} + unmarshall(char *b, size_t sz): buf_(b),sz_(sz),index_(),ok_(true) {} + unmarshall(const string &s) : buf_(NULL),sz_(0),index_(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 string &s) { + sz_ = s.size()+RPC_HEADER_SZ; + buf_ = (char *)realloc(buf_,sz_); + VERIFY(buf_); + index_ = RPC_HEADER_SZ; + memcpy(buf_+index_, s.data(), s.size()); + ok_ = true; + } + + bool ok() const { return ok_; } + char *cstr() { return buf_;} + bool okdone() const { return ok_ && index_ == sz_; } + + uint8_t rawbyte(); + void rawbytes(string &s, size_t n); + template void rawbytes(T &t); + + size_t ind() { return index_;} + size_t size() { return sz_;} + void take_buf(char **b, size_t *sz) { + *b = buf_; + *sz = sz_; + sz_ = index_ = 0; + buf_ = NULL; + } + + void unpack_req_header(request_header *h) { + //the first 4-byte is for channel to fill size of pdu + index_ = sizeof(rpc_sz_t); + *this >> h->xid >> h->proc >> h->clt_nonce >> h->srv_nonce >> h->xid_rep; + index_ = RPC_HEADER_SZ; + } + + void unpack_reply_header(reply_header *h) { + //the first 4-byte is for channel to fill size of pdu + index_ = sizeof(rpc_sz_t); + *this >> h->xid >> h->ret; + index_ = RPC_HEADER_SZ; + } + + template + inline A grab() { + A a; + *this >> a; + return a; + } }; -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; +template 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; } -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 unmarshall & +operator>>(unmarshall &u, map &x) { + unsigned n = u.grab(); + x.clear(); + while (n--) + x.emplace(u.grab>()); + return u; } -template marshall & -operator<<(marshall &m, const std::map &d) { - typename std::map::const_iterator i; +template unmarshall & +operator>>(unmarshall &u, pair &d) { + return u >> d.first >> d.second; +} - m << (unsigned int) d.size(); +template typename enable_if::value, unmarshall>::type & +operator>>(unmarshall &u, E &e) { + e = to_enum(u.grab>()); + return u; +} - for (i = d.begin(); i != d.end(); i++) { - m << i->first << i->second; - } - return m; +typedef function handler; + +// +// Automatic marshalling wrappers for RPC handlers +// + +// PAI 2013/09/19 +// C++11 does neither of these two things for us: +// 1) Declare variables using a parameter pack expansion, like so +// Args ...args; +// 2) Call a function with a tuple of the arguments it expects +// +// We implement an 'invoke' function for functions of the RPC handler +// signature, i.e. int(R & r, const Args...) +// +// One thing we need in order to accomplish this is a way to cause the compiler +// to specialize 'invoke' with a parameter pack containing a list of indices +// for the elements of the tuple. This will allow us to call the underlying +// function with the exploded contents of the tuple. The empty type +// tuple_indices accomplishes this. It will be passed in to +// 'invoke' as a parameter which will be ignored, but its type will force the +// compiler to specialize 'invoke' appropriately. + +// The following implementation of tuple_indices is redistributed under the MIT +// License as an insubstantial portion of the LLVM compiler infrastructure. + +template struct tuple_indices {}; +template struct make_indices_imp; +template struct make_indices_imp, E> { + typedef typename make_indices_imp, E>::type type; +}; +template struct make_indices_imp, E> { + typedef tuple_indices type; +}; +template struct make_tuple_indices { + typedef typename make_indices_imp, E>::type type; +}; + +// This class encapsulates the default response to runtime unmarshalling +// failures. The templated wrappers below may optionally use a different +// class. + +struct VerifyOnFailure { + static inline int unmarshall_args_failure() { + VERIFY(0); + return 0; + } +}; + +// Here's the implementation of 'invoke'. It could be more general, but this +// meets our needs. + +// One for function pointers... + +template +typename enable_if::value, RV>::type +invoke(RV, F f, void *, R & r, args_type & t, tuple_indices) { + return f(r, move(get(t))...); } -template unmarshall & -operator>>(unmarshall &u, std::map &d) { - unsigned int n; - u >> n; - - d.clear(); - - for (unsigned int lcv = 0; lcv < n; lcv++) { - A a; - B b; - u >> a >> b; - d[a] = b; - } - return u; +// And one for pointers to member functions... + +template +typename enable_if::value, RV>::type +invoke(RV, F f, C *c, R & r, args_type & t, tuple_indices) { + return (c->*f)(r, move(get(t))...); } +// The class marshalled_func_imp uses partial template specialization to +// implement the ::wrap static function. ::wrap takes a function pointer or a +// pointer to a member function and returns a handler * object which +// unmarshalls arguments, verifies successful unmarshalling, calls the supplied +// function, and marshalls the response. + +template struct marshalled_func_imp; + +// Here we specialize on the Signature template parameter to obtain the list of +// argument types. Note that we do not assume that the Functor parameter has +// the same pattern as Signature; this allows us to ignore the distinctions +// between various types of callable objects at this level of abstraction. + +template +struct marshalled_func_imp { + static inline handler *wrap(F f, C *c=nullptr) { + // This type definition corresponds to an empty struct with + // template parameters running from 0 up to (# args) - 1. + using Indices = typename make_tuple_indices::type; + // This type definition represents storage for f's unmarshalled + // arguments. decay is (most notably) stripping off const + // qualifiers. + using ArgsStorage = tuple::type...>; + // Allocate a handler (i.e. function) to hold the lambda + // which will unmarshall RPCs and call f. + return new handler([=](unmarshall &u, marshall &m) -> RV { + // Unmarshall each argument with the correct type and store the + // result in a tuple. + ArgsStorage t = {u.grab::type>()...}; + // Verify successful unmarshalling of the entire input stream. + if (!u.okdone()) + return (RV)ErrorHandler::unmarshall_args_failure(); + // Allocate space for the RPC response -- will be passed into the + // function as an lvalue reference. + R r; + // Perform the invocation. Note that Indices() calls the default + // constructor of the empty struct with the special template + // parameters. + RV b = invoke(RV(), f, c, r, t, Indices()); + // Marshall the response. + m << r; + // Make like a tree. + return b; + }); + } +}; + +// More partial template specialization shenanigans to reduce the number of +// parameters which must be provided explicitly and to support a few common +// callable types. C++11 doesn't allow partial function template +// specialization, so we use classes (structs). + +template struct marshalled_func; + +template +struct marshalled_func : + public marshalled_func_imp {}; + +template +struct marshalled_func : + public marshalled_func_imp {}; + +template +struct marshalled_func> : + public marshalled_func_imp {}; + +template unmarshall & +tuple_unmarshall_imp(unmarshall & u, tuple t, tuple_indices) { + (void)pass{(u >> get(t))...}; + return u; +} + +template unmarshall & +operator>>(unmarshall & u, tuple && t) { + using Indices = typename make_tuple_indices::type; + return tuple_unmarshall_imp(u, t, Indices()); +} + +template marshall & +tuple_marshall_imp(marshall & m, tuple & t, tuple_indices) { + (void)pass{(m << get(t))...}; + return m; +} + +template marshall & +operator<<(marshall & m, tuple && t) { + using Indices = typename make_tuple_indices::type; + return tuple_marshall_imp(m, t, Indices()); +} + +// for structs or classes containing a MEMBERS declaration +#define MARSHALLABLE(_c_) \ +inline unmarshall & operator>>(unmarshall &u, _c_ &a) { return u >> a._tuple_(); } \ +inline marshall & operator<<(marshall &m, _c_ a) { return m << a._tuple_(); } + #endif