X-Git-Url: http://xvm.mit.edu/gitweb/invirt/third/libt4.git/blobdiff_plain/2546a41ad36fdc9ef6471cb35a1d56930ae1b527..5d99dbf06a14904944f5593c63705934bdfdcfb7:/rpc/marshall.h?ds=sidebyside diff --git a/rpc/marshall.h b/rpc/marshall.h index abeaae7..d7f1dff 100644 --- a/rpc/marshall.h +++ b/rpc/marshall.h @@ -1,261 +1,177 @@ #ifndef marshall_h #define marshall_h -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "lang/verify.h" +#include "types.h" + +// for structs or classes containing a MEMBERS declaration +class marshall; +class unmarshall; +#define FORWARD_MARSHALLABLE(_c_) \ +extern unmarshall & operator>>(unmarshall &u, typename remove_reference<_c_>::type &a); \ +extern marshall & operator<<(marshall &m, const _c_ a); +#define MARSHALLABLE(_c_) \ +inline unmarshall & operator>>(unmarshall &u, _c_ &a) { return u >> a._tuple_(); } \ +inline marshall & operator<<(marshall &m, const _c_ a) { return m << a._tuple_(); } + +// for plain old data +#define MARSHALL_RAW_NETWORK_ORDER_AS(_c_, _d_) \ +marshall & operator<<(marshall &m, _c_ x) { _d_ y = hton((_d_)x); m.rawbytes(&y, sizeof(_d_)); return m; } \ +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_) 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; + + MEMBERS(xid, proc, clt_nonce, srv_nonce, xid_rep) }; +FORWARD_MARSHALLABLE(request_header) +ENDIAN_SWAPPABLE(request_header) + struct reply_header { - reply_header(int x=0, int r=0): xid(x), ret(r) {} int xid; int ret; -}; - -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)}; } + MEMBERS(xid, ret) +}; -template inline T ntoh(T t) { return hton(t); } +FORWARD_MARSHALLABLE(reply_header) +ENDIAN_SWAPPABLE(reply_header) typedef int rpc_sz_t; -//size of initial buffer allocation -#define DEFAULT_RPC_SZ 1024 -#define RPC_HEADER_SZ (std::max(sizeof(request_header), sizeof(reply_header)) + sizeof(rpc_sz_t)) +const size_t RPC_HEADER_SZ = max(sizeof(request_header), sizeof(reply_header)) + sizeof(rpc_sz_t); +const size_t DEFAULT_RPC_SZ = 1024; // size of initial buffer allocation + +// Template parameter pack expansion is not allowed in certain contexts, but +// brace initializers (for instance, calls to constructors of empty structs) +// are fair game. +struct pass { template inline pass(Args&&...) {} }; class marshall { 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 + string buf_ = string(DEFAULT_RPC_SZ, 0); // Raw bytes buffer + size_t index_ = RPC_HEADER_SZ; // Read/write head position inline void reserve(size_t n) { - if((index_+n) > capacity_){ - capacity_ += std::max(capacity_, n); - VERIFY (buf_ != NULL); - buf_ = (char *)realloc(buf_, capacity_); - VERIFY(buf_); - } + if (index_+n > buf_.size()) + buf_.resize(index_+n); } public: - struct pass { template inline pass(Args&&...) {} }; - 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) { + void rawbytes(const void *p, size_t n) { reserve(n); - memcpy(buf_+index_, p, n); + copy((char *)p, (char *)p+n, &buf_[index_]); index_ += n; } - // Return the current content (excluding header) as a string - std::string get_content() { - return std::string(buf_+RPC_HEADER_SZ,index_-RPC_HEADER_SZ); - } - - // Return the current content (excluding header) as a string - std::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; + // with header + operator string () const { return buf_.substr(0,index_); } + // without header + string content() { return buf_.substr(RPC_HEADER_SZ,index_-RPC_HEADER_SZ); } + + template + void pack_header(const T &h) { + VERIFY(sizeof(T)+sizeof(rpc_sz_t) <= RPC_HEADER_SZ); + size_t saved_sz = index_; + index_ = sizeof(rpc_sz_t); // first 4 bytes hold length field + *this << h; + index_ = saved_sz; } }; -marshall& operator<<(marshall &, bool); -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 std::string &); - -template -struct is_enumerable : std::false_type {}; - -template struct is_enumerable().cbegin(), std::declval().cend(), void()) -> : std::true_type {}; - -template typename std::enable_if::value, marshall>::type & +FORWARD_MARSHALLABLE(bool); +FORWARD_MARSHALLABLE(uint8_t); +FORWARD_MARSHALLABLE(int8_t); +FORWARD_MARSHALLABLE(uint16_t); +FORWARD_MARSHALLABLE(int16_t); +FORWARD_MARSHALLABLE(uint32_t); +FORWARD_MARSHALLABLE(int32_t); +FORWARD_MARSHALLABLE(size_t); +FORWARD_MARSHALLABLE(uint64_t); +FORWARD_MARSHALLABLE(int64_t); +FORWARD_MARSHALLABLE(string &); + +template typename enable_if::value, marshall>::type & operator<<(marshall &m, const A &x) { - m << (unsigned int) x.size(); + m << (unsigned int)x.size(); for (const auto &a : x) m << a; return m; } template marshall & -operator<<(marshall &m, const std::pair &d) { +operator<<(marshall &m, const pair &d) { return m << d.first << d.second; } template -using enum_type_t = typename std::enable_if::value, typename std::underlying_type::type>::type; +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 std::enable_if::value, marshall>::type & +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 &, std::string &); -template typename std::enable_if::value, unmarshall>::type & +template typename enable_if::value, unmarshall>::type & operator>>(unmarshall &u, E &e); class unmarshall { 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 std::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_); + string buf_; + size_t index_ = 0; + bool ok_ = false; + + inline bool ensure(size_t n) { + if (index_+n > buf_.size()) + ok_ = false; + return ok_; } - - //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_); - index_ = RPC_HEADER_SZ; - memcpy(buf_+index_, s.data(), s.size()); - ok_ = true; + public: + unmarshall() {} + unmarshall(const string &s, bool has_header) + : buf_(s),index_(RPC_HEADER_SZ) { + if (!has_header) + buf_.insert(0, RPC_HEADER_SZ, 0); + ok_ = (buf_.size() >= RPC_HEADER_SZ); } bool ok() const { return ok_; } - char *cstr() { return buf_;} - bool okdone() const { return ok_ && index_ == sz_; } - - uint8_t rawbyte(); - void rawbytes(std::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; - } + bool okdone() const { return ok_ && index_ == buf_.size(); } - 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 rawbytes(void * t, size_t n) { + VERIFY(ensure(n)); + copy(&buf_[index_], &buf_[index_+n], (char *)t); + index_ += n; } - void unpack_reply_header(reply_header *h) { - //the first 4-byte is for channel to fill size of pdu + template + void unpack_header(T & h) { + // first 4 bytes hold length field + VERIFY(sizeof(T)+sizeof(rpc_sz_t) <= RPC_HEADER_SZ); index_ = sizeof(rpc_sz_t); - *this >> h->xid >> h->ret; + *this >> h; index_ = RPC_HEADER_SZ; } - template - inline A grab() { - A a; - *this >> a; - return a; - } + template inline T grab() { T t; *this >> t; return t; } }; -template typename std::enable_if::value, unmarshall>::type & +template typename enable_if::value, unmarshall>::type & operator>>(unmarshall &u, A &x) { unsigned n = u.grab(); x.clear(); @@ -265,26 +181,26 @@ operator>>(unmarshall &u, A &x) { } template unmarshall & -operator>>(unmarshall &u, std::map &x) { +operator>>(unmarshall &u, map &x) { unsigned n = u.grab(); x.clear(); while (n--) - x.emplace(u.grab>()); + x.emplace(u.grab>()); return u; } template unmarshall & -operator>>(unmarshall &u, std::pair &d) { +operator>>(unmarshall &u, pair &d) { return u >> d.first >> d.second; } -template typename std::enable_if::value, unmarshall>::type & +template typename enable_if::value, unmarshall>::type & operator>>(unmarshall &u, E &e) { e = to_enum(u.grab>()); return u; } -typedef std::function handler; +typedef function handler; // // Automatic marshalling wrappers for RPC handlers @@ -293,8 +209,8 @@ typedef std::function handler; // 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 std::tuple of the arguments it expects +// 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...) @@ -307,21 +223,6 @@ typedef std::function handler; // '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. @@ -338,18 +239,18 @@ struct VerifyOnFailure { // One for function pointers... -template -typename std::enable_if::value, RV>::type +template +typename enable_if::value, RV>::type invoke(RV, F f, void *, R & r, args_type & t, tuple_indices) { - return f(r, std::move(std::get(t))...); + return f(r, move(get(t))...); } // And one for pointers to member functions... -template -typename std::enable_if::value, RV>::type +template +typename enable_if::value, RV>::type invoke(RV, F f, C *c, R & r, args_type & t, tuple_indices) { - return (c->*f)(r, std::move(std::get(t))...); + return (c->*f)(r, move(get(t))...); } // The class marshalled_func_imp uses partial template specialization to @@ -373,15 +274,15 @@ struct marshalled_func_imp { // 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. std::decay is (most notably) stripping off const + // arguments. decay is (most notably) stripping off const // qualifiers. - using ArgsStorage = std::tuple::type...>; - // Allocate a handler (i.e. std::function) to hold the lambda + 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>()...}; + ArgsStorage t = {u.grab::type>()...}; // Verify successful unmarshalling of the entire input stream. if (!u.okdone()) return (RV)ErrorHandler::unmarshall_args_failure(); @@ -417,7 +318,34 @@ struct marshalled_func : public marshalled_func_imp {}; template -struct marshalled_func> : +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()); +} + +MARSHALLABLE(request_header) +MARSHALLABLE(reply_header) + #endif