X-Git-Url: http://xvm.mit.edu/gitweb/invirt/third/libt4.git/blobdiff_plain/eeab3e6cade87c1fe0a5f3d93522e12ccb9ec2ab..3abd3952c1f4441f0dd6eae9883b2d01ed9cd56b:/rpc/marshall.h?ds=sidebyside diff --git a/rpc/marshall.h b/rpc/marshall.h index 7a85d6b..69b10df 100644 --- a/rpc/marshall.h +++ b/rpc/marshall.h @@ -1,376 +1,250 @@ #ifndef marshall_h #define marshall_h -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "lang/verify.h" - -struct request_header { - request_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; - request_header hton() const { - return { - htonl(xid), htonl(proc), htonl(clt_nonce), htonl(srv_nonce), htonl(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; - reply_header hton() const { - return { - htonl(xid), htonl(ret) - }; - } -}; +class marshall; +class unmarshall; -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)) +// +// Marshall and unmarshall objects +// 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 - - 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_); - } - } - public: - struct pass { template inline pass(Args&&...) {} }; + string buf_ = string(DEFAULT_RPC_SZ, 0); // Raw bytes buffer + size_t index_ = RPC_HEADER_SZ; // Read/write head position + 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_); - } - - int size() { return index_;} - char *cstr() { return buf_;} - const char *cstr() const { return buf_;} - - void rawbyte(unsigned char x) { - reserve(1); - buf_[index_++] = x; - } - - void rawbytes(const char *p, int n) { - reserve(n); - memcpy(buf_+index_, p, n); + 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; } - // 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, int *s) { - *b = buf_; - *s = index_; - buf_ = NULL; + // with header + inline operator string() const { return buf_.substr(0,index_); } + // without header + inline string content() const { return buf_.substr(RPC_HEADER_SZ,index_-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_HEADER_SZ); + size_t saved_sz = index_; index_ = 0; - return; + *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 &); - -template marshall & -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 std::pair &d) { - m << d.first; - m << d.second; - return m; -} - class unmarshall { private: - char *buf_; - int sz_; - int index_; - bool ok_; + string buf_; + size_t index_ = 0; + bool ok_ = false; - inline bool ensure(size_t n); public: - unmarshall(): buf_(NULL),sz_(0),index_(0),ok_(false) {} - unmarshall(char *b, int 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_); - } - - //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; + 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_; } - - unsigned int rawbyte(); - void rawbytes(std::string &s, size_t n); - - int ind() { return index_;} - int size() { return sz_;} - void unpack(int *); //non-const ref - void take_buf(char **b, int *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); - unpack(&h->xid); - unpack(&h->proc); - unpack((int *)&h->clt_nonce); - unpack((int *)&h->srv_nonce); - unpack(&h->xid_rep); - index_ = RPC_HEADER_SZ; + 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; } - void unpack_reply_header(reply_header *h) { - //the first 4-byte is for channel to fill size of pdu + template inline void + unpack_header(T & h) { + VERIFY(sizeof(T)+sizeof(rpc_sz_t) <= RPC_HEADER_SZ); + // first 4 bytes hold length field index_ = sizeof(rpc_sz_t); - unpack(&h->xid); - unpack(&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 unmarshall & operator>>(unmarshall &u, A &x) { - unsigned n = u.grab(); - x.clear(); - while (n--) - x.emplace_back(u.grab()); - return u; +// +// 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::map &x) { - unsigned n = u.grab(); - x.clear(); - while (n--) - x.emplace(u.grab>()); - return u; +template marshall & +operator<<(marshall & m, tuple && t) { + using Indices = typename make_tuple_indices::type; + return tuple_marshall_imp(m, t, Indices()); } -template unmarshall & -operator>>(unmarshall &u, std::pair &d) { - return u >> d.first >> d.second; +template inline unmarshall & +tuple_unmarshall_imp(unmarshall & u, tuple t, tuple_indices) { + (void)pass{(u >> get(t))...}; + return u; } -typedef std::function handler; +template unmarshall & +operator>>(unmarshall & u, tuple && t) { + using Indices = typename make_tuple_indices::type; + return tuple_unmarshall_imp(u, t, Indices()); +} // -// Automatic marshalling wrappers for RPC handlers +// Marshalling for structs or classes containing a MEMBERS declaration // -// 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 +// Implements struct marshalling via tuple marshalling of members. +#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_(); } + +// our first two marshallable structs... +MARSHALLABLE(request_header) +MARSHALLABLE(reply_header) + // -// We implement an 'invoke' function for functions of the RPC handler -// signature, i.e. int(R & r, const Args...) +// Marshalling for STL containers // -// 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; - } -}; +// 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; +} -// Here's the implementation of 'invoke'. It could be more general, but this -// meets our needs. +// 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; +} -// One for function pointers... +// std::pair +template inline marshall & +operator<<(marshall &m, const pair &d) { + return m << d.first << d.second; +} -template -typename std::enable_if::value, int>::type -invoke(F f, void *, R & r, args_type & t, tuple_indices) { - return f(r, std::move(std::get(t))...); +template inline unmarshall & +operator>>(unmarshall &u, pair &d) { + return u >> d.first >> d.second; } -// And one for pointers to member functions... +// std::map +template inline unmarshall & +operator>>(unmarshall &u, map &x) { + unsigned n = u._grab(); + x.clear(); + while (n--) + x.emplace(u._grab>()); + return u; +} -template -typename std::enable_if::value, int>::type -invoke(F f, C *c, R & r, args_type & t, tuple_indices) { - return (c->*f)(r, std::move(std::get(t))...); +// std::string +inline marshall & operator<<(marshall &m, const string &s) { + m << (uint32_t)s.size(); + m.rawbytes(s.data(), s.size()); + return m; } -// 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. std::decay is (most notably) stripping off const - // qualifiers. - using ArgsStorage = std::tuple::type...>; - // Allocate a handler (i.e. std::function) to hold the lambda - // which will unmarshall RPCs and call f. - return new handler([=](unmarshall &u, marshall &m) -> int { - // 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 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. - int b = invoke(f, c, r, t, Indices()); - // Marshall the response. - m << r; - // Make like a tree. - return b; - }); +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 +// -// 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 typename enable_if::value, marshall>::type & +operator<<(marshall &m, E e) { + return m << from_enum(e); +} -template struct marshalled_func; +template typename enable_if::value, unmarshall>::type & +operator>>(unmarshall &u, E &e) { + e = to_enum(u._grab>()); + return u; +} -template -struct marshalled_func : - public marshalled_func_imp {}; +// +// Recursive marshalling +// -template -struct marshalled_func : - public marshalled_func_imp {}; +inline marshall & operator<<(marshall &m, marshall &n) { + return m << n.content(); +} -template -struct marshalled_func> : - public marshalled_func_imp {}; +inline unmarshall & operator>>(unmarshall &u, unmarshall &v) { + v = unmarshall(u._grab(), false); + return u; +} #endif