// -*-c++-*-
/* $Id: rpctypes.h,v 1.40 2001/08/23 22:55:38 dm Exp $ */

/*
 *
 * Copyright (C) 1998 David Mazieres (dm@uun.org)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 *
 */

#ifndef _RPCTYPES_H_
#define _RPCTYPES_H_ 1

#include "str.h"
#include "vec.h"
#include "array.h"
#include "union.h"
#include "keyfunc.h"
#include "err.h"

struct rpcgen_table {
  const char *name;

  const std::type_info *type_arg;
  void *(*alloc_arg) ();
  xdrproc_t xdr_arg;
  void (*print_arg) (const void *, const strbuf *, int,
		     const char *, const char *);

  const std::type_info *type_res;
  void *(*alloc_res) ();
  xdrproc_t xdr_res;
  void (*print_res) (const void *, const strbuf *, int,
		     const char *, const char *);
};

struct rpc_program {
  u_int32_t progno;
  u_int32_t versno;
  const struct rpcgen_table *tbl;
  size_t nproc;
  const char *name;
};

enum { RPC_INFINITY = 0x7fffffff };

template<class T> class rpc_ptr {
  T *p;

public:
  rpc_ptr () { p = NULL; }
  rpc_ptr (const rpc_ptr &rp) { p = rp ? New T (*rp) : NULL; }
  ~rpc_ptr () { delete p; }

  void clear () { delete p; p = NULL; }
  rpc_ptr &alloc () { if (!p) p = New T; return *this; }
  rpc_ptr &assign (T *tp) { clear (); p = tp; return *this; }
  T *release () { T *r = p; p = NULL; return r; }

  operator T *() const { return p; }
  T *operator-> () const { return p; }
  T &operator* () const { return *p; }

  rpc_ptr &operator= (const rpc_ptr &rp) {
    if (!rp.p)
      clear ();
    else if (p)
      *p = *rp.p;
    else
      p = New T (*rp.p);
    return *this;
  }
  void swap (rpc_ptr &a) { T *ap = a.p; a.p = p; p = ap; }
};

template<class T> inline void
swap (rpc_ptr<T> &a, rpc_ptr<T> &b)
{
  a.swap (b);
}


struct freemode {
  enum freemode_t { NOFREE, DELARRAY, XFREE, DELETE };
  freemode_t mode;
};

template<class T, size_t max> class rpc_vec : protected freemode {
public:
  typedef toarray (T) elm_t;
  enum { maxsize = max };

private:
  size_t nelm;
  elm_t *vec;

#ifdef CHECK_BOUNDS
#define bcheck(i) assert (size_t (i) < nelm)
#define mcheck(i) assert (size_t (i) <= maxsize)
#else /* !CHECK_BOUNDS */
#define bcheck(i)
#define mcheck(i)
#endif /* !CHECK_BOUNDS */

#define assign(v)				\
  do {						\
    setsize (v.size ());			\
    elm_t *d = vec;				\
    const elm_t *s = v.base (), *e = v.lim ();	\
    while (s < e)				\
      *d++ = *s++;				\
  } while (0)

#define sassign(v)				\
  if (&v != this)				\
    assign (v)

  void init () { mode = NOFREE; nelm = 0; vec = NULL; }
  void del () {
    switch (mode) {
    case NOFREE:
      break;
    case DELARRAY:
      delete[] vec;
      break;
    case XFREE:
      xfree (vec);
      break;
    case DELETE:
      delete vec;
      break;
    }
  }

public:
  rpc_vec () { init (); }
  rpc_vec (const rpc_vec &v) { init (); sassign (v); }
#if 0
  template<size_t m> rpc_vec (const rpc_vec<T, m> &v) { init (); assign (v); }
  template<size_t m> rpc_vec (const array<T, m> &v)
    { switch (0) case 0: case m <= max:; init (); assign (v); }
#endif
  ~rpc_vec () { del (); }
  void clear () { del (); init (); }

  rpc_vec &operator= (const rpc_vec &v) { sassign (v); return *this; }
  template<size_t m> rpc_vec &operator= (const rpc_vec<T, m> &v)
    { assign (v); return *this; }
  template<size_t m> rpc_vec &operator= (const ::vec<T, m> &v)
    { assign (v); return *this; }
  template<size_t m> rpc_vec &operator= (const array<T, m> &v)
    { switch (0) case 0: case m <= max:; assign (v); return *this; }

#if 0
  /* These were renamed operator=.  operator= copies elements, while
   * set just sets the pointers. */
  template<size_t m> rpc_vec &set (const rpc_vec<T, m> &v)
    { assign (v); return *this; }
  template<size_t m> rpc_vec &set (const array<T, m> &v)
    { switch (0) case 0: case m <= max:; assign (v); return *this; }
#endif
  rpc_vec &set (elm_t *base, size_t len, freemode_t m = NOFREE)
    { mcheck (len); del (); mode = m; nelm = len; vec = base; return *this; }
  template<size_t m> rpc_vec &set (const ::vec<T, m> &v)
    { set (v.base (), v.size ()); }

  void swap (rpc_vec &a) {
    freemode_t m = a.mode;
    a.mode = mode;
    mode = m;

    size_t n = a.nelm;
    a.nelm = nelm;
    nelm = n;

    elm_t *v = a.vec;
    a.vec = vec;
    vec = v;
  }

  void setsize (size_t n) {
    mcheck (n);
#ifdef DMALLOC
    if (true)
#else /* !DMALLOC */
    if (mode != DELARRAY || n != nelm)
#endif /* !DMALLOC */
      {
	del ();
	if (n) {
	  mode = DELARRAY;
	  nelm = n;
#ifndef DMALLOC
	  vec = new elm_t[nelm];
#else /* DMALLOC */
	  vec = new (dmalloc, stktrace (__FILE__), __LINE__) elm_t[nelm];
#endif /* DMALLOC */
	}
	else
	  init ();
      }
  }

  size_t size () const { return nelm; }

  elm_t *base () { return vec; }
  const elm_t *base () const { return vec; }

  elm_t *lim () { return vec + nelm; }
  const elm_t *lim () const { return vec + nelm; }

  elm_t &operator[] (size_t i) { bcheck (i); return vec[i]; }
  const elm_t &operator[] (size_t i) const { bcheck (i); return vec[i]; }
  elm_t &at (size_t i) { bcheck (i); return vec[i]; }
  const elm_t &at (size_t i) const { bcheck (i); return vec[i]; }
#undef bcheck
#undef mcheck
#undef assign
#undef sassign
};

template<class T, size_t max> void
swap (rpc_vec<T, max> &a, rpc_vec<T, max> &b)
{
  a.swap (b);
}

template<size_t max = RPC_INFINITY> struct rpc_str : str
{
  enum { maxsize = max };

private:
  void check () {
    assert (len () == strlen (cstr ()));
    assert (len () <= maxsize);
  }

public:
  rpc_str () : str (rpc_emptystr) {}
  rpc_str (const rpc_str &s) : str (s) {}
  rpc_str (const str &s) : str (s) { check (); }
  rpc_str (const char *p) : str (p) { assert (len () <= maxsize); }
  rpc_str (const strbuf &b) : str (b) { check (); }
  rpc_str (const char *buf, size_t len) : str (buf, len) { check (); }
  rpc_str (const iovec *iov, int cnt) : str (iov, cnt) { check (); }
  rpc_str (mstr &m) : str (m) { check (); }

  rpc_str &operator= (const rpc_str &s)
    { str::operator= (s); return *this; }
  rpc_str &operator= (const char *p)
    { str::operator= (p); if (p) assert (len () <= maxsize); return *this; }
  template<class T> rpc_str &operator= (const T &t)
    { str::operator= (t); check (); return *this; }
  rpc_str &operator= (mstr &m)
    { str::operator= (m); check (); return *this; }
  rpc_str &setbuf (const char *buf, size_t len)
    { str::setbuf (buf, len); check (); return *this; }
  rpc_str &setiov (const iovec *iov, int cnt)
    { str::setiov (iov, cnt); check (); return *this; }
};

template<size_t n = RPC_INFINITY> struct rpc_opaque : array<char, n> {
  rpc_opaque () { bzero (base (), size ()); }
};
template<size_t n = RPC_INFINITY> struct rpc_bytes : rpc_vec<char, n> {
  void setstrmem (const str &s) { set (s.cstr (), s.len (), NOFREE); }
  rpc_bytes &operator= (const str &s)
    { setsize (s.len ()); memcpy (base (), s.cstr (), size ()); return *this; }
  template<size_t m> rpc_bytes &operator= (const rpc_vec<char, m> &v)
    { rpc_vec<char, n>::operator= (v); return *this; }
  template<size_t m> rpc_bytes &operator= (const array<char, m> &v)
    { rpc_vec<char, n>::operator= (v); return *this; }
};

#if 0
template<size_t n> struct equals<rpc_opaque<n> > {
  equals () {}
  bool operator() (const rpc_opaque<n> &a, const rpc_opaque<n> &b) const
    { return !memcmp (a.base (), b.base (), n); }
};

template<size_t n> struct equals<rpc_bytes<n> > {
  equals () {}
  bool operator() (const rpc_bytes<n> &a, const rpc_bytes<n> &b) const
    { return a.size () == b.size ()
	&& !memcmp (a.base (), b.base (), a.size ()); }
};
#endif

template<size_t n> inline bool
operator== (const rpc_opaque<n> &a, const rpc_opaque<n> &b)
{
  return !memcmp (a.base (), b.base (), n);
}
template<size_t n> inline bool
operator== (const rpc_bytes<n> &a, const rpc_bytes<n> &b)
{
  return a.size () == b.size () && !memcmp (a.base (), b.base (), a.size ());
}

template<size_t n> inline bool
operator!= (const rpc_opaque<n> &a, const rpc_opaque<n> &b)
{
  return memcmp (a.base (), b.base (), n);
}
template<size_t n> inline bool
operator!= (const rpc_bytes<n> &a, const rpc_bytes<n> &b)
{
  return a.size () != b.size () || memcmp (a.base (), b.base (), a.size ());
}

#if 0
template<size_t n, size_t m> inline bool
operator== (const rpc_bytes<n> &a, const rpc_bytes<m> &b)
{
  return a.size () == b.size () && !memcmp (a.base (), b.base (), a.size ());
}
template<size_t n, size_t m> inline bool
operator== (const rpc_bytes<n> &a, const rpc_opaque<m> &b)
{
  return a.size () == b.size () && !memcmp (a.base (), b.base (), a.size ());
}
template<size_t n, size_t m> inline bool
operator== (const rpc_opaque<n> &a, const rpc_bytes<m> &b)
{
  return a.size () == b.size () && !memcmp (a.base (), b.base (), a.size ());
}

template<size_t n, size_t m> inline bool
operator!= (const rpc_bytes<n> &a, const rpc_bytes<m> &b)
{
  return a.size () != b.size () || memcmp (a.base (), b.base (), a.size ());
}
template<size_t n, size_t m> inline bool
operator!= (const rpc_bytes<n> &a, const rpc_opaque<m> &b)
{
  return a.size () != b.size () || memcmp (a.base (), b.base (), a.size ());
}
template<size_t n, size_t m> inline bool
operator!= (const rpc_opaque<n> &a, const rpc_bytes<m> &b)
{
  return a.size () != b.size () || memcmp (a.base (), b.base (), a.size ());
}
#endif

template<size_t n> struct hashfn<rpc_opaque<n> > {
  hashfn () {}
  bool operator () (const rpc_opaque<n> &a) const
    { return hash_bytes (a.base (), n); }
};
template<size_t n> struct hashfn<rpc_bytes<n> > {
  hashfn () {}
  bool operator () (const rpc_bytes<n> &a) const
    { return hash_bytes (a.base (), a.size ()); }
};


/*
 * Default traversal functions
 */

template<class T, class R, size_t n> inline bool
rpc_traverse (T &t, array<R, n> &obj)
{
  typedef array<R, n>::elm_t elm_t;

  elm_t *p = obj.base ();
  elm_t *e = obj.lim ();
  while (p < e)
    if (!rpc_traverse (t, *p++))
      return false;
  return true;
}

template<class T, class R, size_t n> inline bool
rpc_traverse (T &t, rpc_vec<R, n> &obj)
{
  typedef rpc_vec<R, n>::elm_t elm_t;

  u_int32_t size = obj.size ();
  if (!rpc_traverse (t, size) || size > obj.maxsize)
    return false;
  if (size != obj.size ())
    obj.setsize (size);

  elm_t *p = obj.base ();
  elm_t *e = obj.lim ();
  while (p < e)
    if (!rpc_traverse (t, *p++))
      return false;
  return true;
}

template<class T, class R> inline bool
rpc_traverse (T &t, rpc_ptr<R> &obj)
{
  bool nonnil = obj;
  if (!rpc_traverse (t, nonnil))
    return false;
  if (nonnil)
    return rpc_traverse (t, *obj.alloc ());
  obj.clear ();
  return true;
}

template<class T> inline bool
rpc_traverse (T &t, bool &obj)
{
  u_int32_t val = obj;
  if (!rpc_traverse (t, val))
    return false;
  obj = val;
  return true;
}

template<class T> inline bool
rpc_traverse (T &t, u_int64_t &obj)
{
  u_int32_t hi = obj >> 32;
  u_int32_t lo = obj;
  if (!rpc_traverse (t, hi) || !rpc_traverse (t, lo))
    return false;
  obj = u_int64_t (hi) << 32 | lo;
  return true;
}

template<class T> inline bool
rpc_traverse (T &t, int32_t &obj)
{
  return rpc_traverse (t, reinterpret_cast<u_int32_t &> (obj));
}

template<class T> inline bool
rpc_traverse (T &t, int64_t &obj)
{
  return rpc_traverse (t, reinterpret_cast<u_int64_t &> (obj));
}

#define DUMBTRANS(T, type)			\
inline bool					\
rpc_traverse (T &, type &)			\
{						\
  return true;					\
}

#define DUMBTRAVERSE(T)				\
DUMBTRANS(T, char)				\
DUMBTRANS(T, bool)				\
DUMBTRANS(T, u_int32_t)				\
DUMBTRANS(T, u_int64_t)				\
template<size_t n> DUMBTRANS(T, rpc_str<n>)	\
template<size_t n> DUMBTRANS(T, rpc_opaque<n>)	\
template<size_t n> DUMBTRANS(T, rpc_bytes<n>)


/*
 * Stompcast support
 */

struct stompcast_t {};
extern const stompcast_t _stompcast;

DUMBTRAVERSE(const stompcast_t)

template<class T> inline bool
stompcast (T &t)
{
  return rpc_traverse (_stompcast, t);
}

/*
 * Clearing support
 */

struct rpc_clear_t {};
extern struct rpc_clear_t _rpcclear;
extern const str rpc_emptystr;

inline bool
rpc_traverse (rpc_clear_t &, u_int32_t &obj)
{
  obj = 0;
  return true;
}
template<size_t n> inline bool
rpc_traverse (rpc_clear_t &, rpc_opaque<n> &obj)
{
  bzero (obj.base (), obj.size ());
  return true;
}
template<size_t n> inline bool
rpc_traverse (rpc_clear_t &, rpc_bytes<n> &obj)
{
  obj.setsize (0);
  return true;
}
template<size_t n> inline bool
rpc_traverse (rpc_clear_t &, rpc_str<n> &obj)
{
  obj = rpc_emptystr;
  return true;
}
template<class T> inline bool
rpc_traverse (rpc_clear_t &, rpc_ptr<T> &obj)
{
  obj.clear ();
  return true;
}
template<class T, size_t n> inline bool
rpc_traverse (rpc_clear_t &, rpc_vec<T, n> &obj)
{
  obj.setsize (0);
  return true;
}

template<class T> inline void
rpc_clear (T &obj)
{
  rpc_traverse (_rpcclear, obj);
}

/*
 *  Pretty-printing functions
 */

#define RPC_PRINT_TYPE_DECL(type)					\
void print_##type (const void *objp, const strbuf *,			\
                   int recdepth = RPC_INFINITY, const char *name = "",	\
		   const char *prefix = "");

#define RPC_PRINT_DECL(type)						     \
const strbuf &rpc_print (const strbuf &sb, const type &obj,		     \
			 int recdepth = RPC_INFINITY, const char *name = "", \
			 const char *prefix = "");

#define RPC_PRINT_DEFINE(T)						\
void									\
print_##T (const void *objp, const strbuf *sbp, int recdepth,		\
	   const char *name, const char *prefix)			\
{									\
  rpc_print (sbp ? *sbp : warnx, *static_cast<const T *> (objp),	\
             recdepth, name, prefix);					\
}
#define print_void NULL
#define print_false NULL

template<class T> struct rpc_type2str {
  static const char *type () { return typeid (T).name (); }
};
template<> struct rpc_type2str<char> {
  static const char *type () { return "opaque"; }
};
#define RPC_TYPE2STR_DECL(T)			\
template<> struct rpc_type2str<T> {		\
  static const char *type () { return #T; }	\
};

#define RPC_PRINT_GEN(T, expr)					\
const strbuf &							\
rpc_print (const strbuf &sb, const T &obj, int recdepth,	\
	   const char *name, const char *prefix)		\
{								\
  if (name) {							\
    if (prefix)							\
      sb << prefix;						\
    sb << rpc_namedecl<T >::decl (name) << " = ";		\
  }								\
  expr;								\
  if (prefix)							\
    sb << ";\n";						\
  return sb;							\
}

RPC_TYPE2STR_DECL (bool)
RPC_TYPE2STR_DECL (int32_t)
RPC_TYPE2STR_DECL (u_int32_t)
RPC_TYPE2STR_DECL (int64_t)
RPC_TYPE2STR_DECL (u_int64_t)

RPC_PRINT_TYPE_DECL (bool)
RPC_PRINT_TYPE_DECL (int32_t)
RPC_PRINT_TYPE_DECL (u_int32_t)
RPC_PRINT_TYPE_DECL (int64_t)
RPC_PRINT_TYPE_DECL (u_int64_t)

RPC_PRINT_DECL (char);
RPC_PRINT_DECL (int32_t);
RPC_PRINT_DECL (u_int32_t);
RPC_PRINT_DECL (int64_t);
RPC_PRINT_DECL (u_int64_t);
RPC_PRINT_DECL (bool);

#ifdef MAINTAINER

static inline str
rpc_dynsize (size_t n)
{
  if (n == (size_t) RPC_INFINITY)
    return "<>";
  return strbuf () << "<" << n << ">";
}
static inline str
rpc_parenptr (const str &name)
{
  if (name[0] == '*')
    return strbuf () << "(" << name << ")";
  return name;
}

template<class T> struct rpc_namedecl {
  static str decl (const char *name) {
    return strbuf () << rpc_type2str<T>::type () << " " << name;
  }
};

template<size_t n> struct rpc_namedecl<rpc_str<n> > {
  static str decl (const char *name) {
    return strbuf () << "string " << rpc_parenptr (name) << rpc_dynsize (n);
  }
};
template<class T> struct rpc_namedecl<rpc_ptr<T> > {
  static str decl (const char *name) {
    return rpc_namedecl<T>::decl (str (strbuf () << "*" << name));
  }
};
template<class T, size_t n> struct rpc_namedecl<rpc_vec<T, n> > {
  static str decl (const char *name) {
    return strbuf () << rpc_namedecl<T>::decl (rpc_parenptr (name))
		     << rpc_dynsize (n);
  }
};
template<class T, size_t n> struct rpc_namedecl<array<T, n> > {
  static str decl (const char *name) {
    return rpc_namedecl<T>::decl (rpc_parenptr (name)) << "[" << n << "]";
  }
};
template<size_t n> struct rpc_namedecl<rpc_bytes<n> > {
  static str decl (const char *name) {
    return rpc_namedecl<rpc_vec<char,n> >::decl (name);
  }
};
template<size_t n> struct rpc_namedecl<rpc_opaque<n> > {
  static str decl (const char *name) {
    return rpc_namedecl<array<char,n> >::decl (name);
  }
};

template<size_t n> const strbuf &
rpc_print (const strbuf &sb, const rpc_str<n> &obj,
	   int recdepth = RPC_INFINITY,
	   const char *name = NULL, const char *prefix = NULL)
{
  if (prefix)
    sb << prefix;
  if (name)
    sb << rpc_namedecl<rpc_str<n> >::decl (name) << " = ";
  if (obj)
    sb << "\"" << obj << "\"";	// XXX should map " to \" in string
  else
    sb << "NULL";
  if (prefix)
    sb << ";\n";
  return sb;
}

template<class T> const strbuf &
rpc_print (const strbuf &sb, const rpc_ptr<T> &obj,
	   int recdepth = RPC_INFINITY,
	   const char *name = NULL, const char *prefix = NULL)
{
  if (name) {
    if (prefix)
      sb << prefix;
    sb << rpc_namedecl<rpc_ptr<T> >::decl (name) << " = ";
  }
  if (!obj)
    sb << "NULL;\n";
  else if (!recdepth)
    sb << "...\n";
  else {
    sb << "&";
    rpc_print (sb, *obj, recdepth - 1, NULL, prefix);
  }
  return sb;
}

struct made_by_user_conversion {
  template<class T> made_by_user_conversion (const T &s) {}
};
inline bool
rpc_isstruct (const made_by_user_conversion &)
{
  return true;
}
inline bool
rpc_isstruct (u_int64_t)
{
  return false;
}

template<class T> const strbuf &
rpc_print_array_vec (const strbuf &sb, const T &obj,
		     int recdepth = RPC_INFINITY,
		     const char *name = NULL, const char *prefix = NULL)
{
  if (name) {
    if (prefix)
      sb << prefix;
    sb << rpc_namedecl<T >::decl (name) << " = ";
  }
  if (obj.size ()) {
    const char *sep;
    str npref;
    if (prefix) {
      npref = strbuf ("%s  ", prefix);
      sep = "";
      sb << "[" << obj.size () << "] {\n";
    }
    else {
      sep = ", ";
      sb << "[" << obj.size () << "] { ";
    }

    if (rpc_isstruct (obj[0])) {
      size_t i;
      size_t n = min<size_t> (obj.size (), recdepth);
      for (i = 0; i < n; i++) {
	if (i)
	  sb << sep;
	if (npref)
	  sb << npref;
	sb << "[" << i << "] = ";
	rpc_print (sb, obj[i], recdepth, NULL, npref);
      }
      if (i < obj.size ())
	sb << (i ? sep : "") << npref << "..." << (npref ? "\n" : " ");
    }
    else {
      size_t i;
      size_t n = recdepth == RPC_INFINITY ? obj.size ()
	: min  ((size_t) recdepth * 8, obj.size ());;
      if (npref)
	sb << npref;
      for (i = 0; i < n; i++) {
	if (i & 7)
	  sb << ", ";
	else if (i) {
	  sb << ",\n";
	  if (npref)
	    sb << npref;
	}
	rpc_print (sb, obj[i], recdepth, NULL, NULL);
      }
      if (i < obj.size ()) {
	if (i) {
	  sb << ",\n";
	  if (npref)
	    sb << npref;
	}
	sb << "...";
      }
      sb << (npref ? "\n" : " ");
    }

    if (prefix)
      sb << prefix << "};\n";
    else
      sb << " }";
  }
  else if (prefix)
    sb << "[0] {};\n";
  else
    sb << "[0] {}";
  return sb;
}

#define RPC_ARRAYVEC_DECL(TEMP)					\
template<class T, size_t n> const strbuf &			\
rpc_print (const strbuf &sb, const TEMP<T, n> &obj,		\
	   int recdepth = RPC_INFINITY,				\
	   const char *name = NULL, const char *prefix = NULL)	\
{								\
  return rpc_print_array_vec (sb, obj, recdepth, name, prefix);	\
}

RPC_ARRAYVEC_DECL (array)
RPC_ARRAYVEC_DECL (rpc_vec)

#undef RPC_ARRAYVEC_DECL
#define RPC_ARRAYVEC_DECL(TEMP)					\
template<size_t n> const strbuf &				\
rpc_print (const strbuf &sb, const TEMP<n> &obj,		\
	   int recdepth = RPC_INFINITY,				\
	   const char *name = NULL, const char *prefix = NULL)	\
{								\
  return rpc_print_array_vec (sb, obj, recdepth, name, prefix);	\
}

RPC_ARRAYVEC_DECL (rpc_opaque)
RPC_ARRAYVEC_DECL (rpc_bytes)

#undef RPC_ARRAYVEC_DECL

template<class T> RPC_PRINT_DECL (T);
template<class T> RPC_PRINT_GEN (T, sb << "???");

#endif /* MAINTAINER */

#endif /* !_RPCTYPES_H_ */

