unpack_base.h
Go to the documentation of this file.
1 /*
2  @copyright Russell Standish 2000-2013
3  @author Russell Standish
4  This file is part of Classdesc
5 
6  Open source licensed under the MIT license. See LICENSE for details.
7 */
8 
13 #ifndef PACK_BASE_H
14 #define PACK_BASE_H
15 #include "classdesc.h"
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <errno.h>
20 #include <assert.h>
21 #include <stdarg.h>
22 #include <exception>
23 #include <vector>
24 #include <algorithm>
25 #include <typeinfo>
26 
27 #include <Realloc.h>
28 
29 #ifdef _MSC_VER
30 // stupid warning about unassignable objects
31 #pragma warning(disable:4512)
32 #endif
33 
34 namespace classdesc
35 {
36  struct XDR;
37 
38  class pack_error : public exception
39  {
40  string msg;
41  public:
42  pack_error(const char *s): msg("pack:") {msg+=s;}
43  virtual ~pack_error() throw() {}
44  virtual const char* what() const throw() {return msg.c_str();}
45  };
46 
47 #ifdef XDR_PACK
48  typedef bool (*xdr_filter)(XDR*,...);
49 #endif
50 
51  struct basic_type
52  {
53  void *val;
54  size_t size;
55 #ifdef XDR_PACK
56  xdr_filter filter;
57 #endif
58  };
59 
60 #ifdef XDR_PACK
61  template <class T> xdr_filter XDR_filter(const T&);
62 #endif
63 
64  template <class T>
65  struct Basic_Type: public basic_type
66  {
67  Basic_Type(const T& x){
68  val=(void*)&x; size=sizeof(x);
69 #ifdef XDR_PACK
70  filter=XDR_filter(x);
71 #endif
72  }
73  };
74 
80  enum Ptr_flag {DEFAULT, GRAPH, TREE};
81 
87  struct PtrStoreBase
88  {
89  int cnt;
90  PtrStoreBase(): cnt(0) {}
91  virtual ~PtrStoreBase() {};
92  virtual void *data()=0;
93  };
94 
99  template <class T>
100  struct PtrStore: public PtrStoreBase
101  {
102  T d;
103  void* data() {return &d;}
104  };
105 
110  class PtrStoreRef
111  {
112  PtrStoreBase *d;
113  public:
114  void* data() {return d->data();}
115  PtrStoreRef(PtrStoreBase *d=NULL): d(d) {}
116  PtrStoreRef(const PtrStoreRef& x): d(x.d) {d && d->cnt++;}
117  PtrStoreRef& operator=(const PtrStoreRef& x) {d=x.d; d && d->cnt++; return *this;}
118  ~PtrStoreRef() {if (d && d->cnt-- == 0) delete d;}
119  };
120 
124  class pack_t
125  {
126 #if __cplusplus < 201103L
127  pack_t operator=(const pack_t&){return *this;}
128  pack_t(const pack_t&){}
129 #endif
130  protected:
131  FILE *f;
132  void swap_base (pack_t& other){
133  std::swap(f,other.f);
134  std::swap(mode,other.mode);
135  std::swap(m_data,other.m_data);
136  std::swap(m_size,other.m_size);
137  std::swap(m_pos,other.m_pos);
138  std::swap(ptr_flag,other.ptr_flag);
139  std::swap(alloced,other.alloced);
140  }
141  enum mode_t {buf, readf, writef};
142  mode_t mode;
143  char *m_data;
144  size_t m_size;
145  size_t m_pos;
146  public:
147  // class notimplemented{};
148  // int xdr;
149  Ptr_flag ptr_flag;
150  unsigned recur_max;
151  std::vector<PtrStoreRef> alloced; //allocated data used for cleaning up
152  const char* data() const {return m_data;}
153  char* data() {return m_data;}
154  size_t size() const {return m_size;}
155  size_t pos() const {return m_pos;}
156  char* realloc(char* d, size_t s) {
157 #ifdef Realloc
158  return (char*)Realloc(d,s);
159 #else
160  return (char*)classdesc::realloc(d,s);
161 #endif
162  }
163  void realloc(size_t s) {m_data=realloc(m_data,s);}
164 
165  virtual void append(const basic_type& x)
166  {
167  if (mode==buf)
168  {
169  realloc(m_size+x.size);
170  if (!m_data)
171  throw std::bad_alloc();
172  memcpy(m_data+m_size,x.val,x.size);
173  }
174  else
175  if (fwrite(x.val,x.size,1,f) != 1)
176  throw pack_error("file write fail");
177  m_size+=x.size;
178  }
179  virtual void popoff(basic_type& x)
180  {
181  if (mode==buf)
182  if (m_pos+x.size>m_size)
183  throw pack_error("unexpected end of data");
184  else
185  memcpy(x.val,m_data+m_pos,x.size);
186  else
187  if (fread(x.val,x.size,1,f) != 1)
188  throw pack_error("unexpected end of file");
189  m_pos+=x.size;
190  }
191 
192  pack_t(size_t sz=0): f(0), mode(buf), m_data(NULL), m_size(sz), m_pos(0), ptr_flag(DEFAULT), recur_max(500) {
193 #ifdef RECUR_MAX
194  recur_max=RECUR_MAX;
195 #endif
196  realloc(sz);}
197  pack_t(const char *fname, const char *rw):
198  f(fopen(fname,rw)), mode( (rw&&rw[0]=='w')? writef: readf),
199  m_data(0), m_size(0), m_pos(0), ptr_flag(DEFAULT), recur_max(500) {
200  // this is to support a deprecated interface
201 #ifdef RECUR_MAX
202  recur_max=RECUR_MAX;
203 #endif
204  if (!f) throw pack_error(strerror(errno));
205  }
206  virtual ~pack_t() {realloc(0); if (f) fclose(f);}
207 
208 #if __cplusplus >= 201103L
209  pack_t(pack_t&& x): f(nullptr), m_data(nullptr) {swap(x);}
210  pack_t& operator=(pack_t&& x) {swap(x); return *this;}
211 #endif
212 
213  virtual operator bool() {return m_pos<m_size;}
214  virtual pack_t& reseti() {m_size=0; if (f) fseek(f,0,SEEK_SET); return *this;}
215  virtual pack_t& reseto() {m_pos=0; if (f) fseek(f,0,SEEK_SET); return *this;}
216  virtual pack_t& seeki(long offs) {
217  assert(offs<=0); m_size+=offs;
218  if (f) fseek(f,offs,SEEK_CUR);
219  return *this;
220  }
221  virtual pack_t& seeko(long offs) {
222  m_pos+=offs;
223  if (f) fseek(f,offs,SEEK_CUR);
224  return *this;
225  }
226  void clear() {realloc(0); m_data=0; m_size=m_pos=0;}
227  virtual void packraw(const char *x, size_t s)
228  {
229  if (mode==buf)
230  {
231  realloc(m_size+s);
232  memcpy(m_data+m_size,x,s); m_size+=s;
233  }
234  else
235  if (fwrite(x,s,1,f)!=1)
236  throw pack_error("filed to write data to stream");
237 
238  }
239  virtual void unpackraw(char *x, size_t s)
240  {
241  if (mode==buf)
242  memcpy(x,m_data+m_pos,s);
243  else
244  if (fread(x,s,1,f)!=1)
245  throw pack_error("premature end of stream");
246  m_pos+=s;
247  }
248  virtual void swap(pack_t& other) {
249  if (typeid(*this)!=typeid(other))
250  throw pack_error("cannot swap differing types");
251  swap_base(other);
252  }
255  virtual int cmp(const pack_t& x) const {
256  if (m_size==x.m_size)
257  return memcmp(m_data,x.m_data,m_size);
258  else
259  return m_size<x.m_size? -1: 1;
260  }
261 
262  bool operator<(const pack_t& x) const {return cmp(x)==-1;}
263  bool operator>(const pack_t& x) const {return cmp(x)==1;}
264  bool operator==(const pack_t& x) const {return cmp(x)==0;}
265  bool operator!=(const pack_t& x) const {return cmp(x)!=0;}
266  };
267 
269  template <class T>
270  int deepCmp(const T& x, const T& y) {
271  pack_t xb, yb;
272  return (xb<<x).cmp(yb<<y);
273  }
275  template <class T>
276  bool deepEq(const T& x, const T& y) {
277  pack_t xb, yb;
278  return (xb<<x).cmp(yb<<y)==0;
279  }
280 
281  typedef pack_t unpack_t;
282 
283 #ifdef XDR_PACK
284  const int BUFCHUNK=1024;
285 
289  class xdr_pack: public pack_t
290  {
291  size_t asize;
292  XDR *input, *output;
293  public:
294  xdr_pack(size_t sz=BUFCHUNK);
295  xdr_pack(const char *, const char* rw);
296  ~xdr_pack();
297 #if __cplusplus >= 201103L
298  xdr_pack(xdr_pack&& x): input(nullptr), output(nullptr) {swap(x);}
299  xdr_pack& operator=(xdr_pack&& x) {swap(x); return *this;}
300 #endif
301 
302  virtual void append(const basic_type& x);
303  virtual void popoff(basic_type& x);
304  virtual xdr_pack& reseti();
305  virtual xdr_pack& reseto();
306  virtual xdr_pack& seeki(long offs);
307  virtual xdr_pack& seeko(long offs);
308  virtual void packraw(const char *x, size_t sz);
309  virtual void unpackraw(char *x, size_t sz);
310  virtual void swap(pack_t& other) {
311  xdr_pack* xdr_other=dynamic_cast<xdr_pack*>(&other);
312  if (!xdr_other) throw pack_error("cannot swap differing types");
313  swap_base(other);
314  std::swap(asize,xdr_other->asize);
315  std::swap(input,xdr_other->input);
316  std::swap(input,xdr_other->output);
317  }
318  };
319 #else
320  typedef pack_t xdr_pack;
321 #endif
322 
330  class BinStream
331  {
332  pack_t& packer;
333  public:
334  BinStream(pack_t& packer): packer(packer) {}
335  template <class T>
337  operator<<(const T& o)
338  {
339  packer.packraw(reinterpret_cast<const char*>(&o), sizeof(T));
340  return *this;
341  }
342  template <class T>
344  operator>>(T& o)
345  {
346  packer.unpackraw(reinterpret_cast<char*>(&o), sizeof(T));
347  return *this;
348  }
349 
350  template <class T>
351  typename enable_if<is_container<T>, BinStream&>::T
352  operator<<(const T& o)
353  {
354  (*this)<<o.size();
355  for (typename T::const_iterator i=o.begin(); i!=o.end(); ++i)
356  (*this)<<*i;
357  return *this;
358  }
359 
360  template <class T>
361  typename enable_if<is_sequence<T>, BinStream&>::T
362  operator>>(T& o)
363  {
364  size_t s;
365  (*this)>>s;
366  typename T::value_type v;
367  for (size_t i=0; i<s; ++i)
368  {
369  (*this)>>v;
370  o.push_back(v);
371  }
372  return *this;
373  }
374 
375  template <class T>
377  operator>>(T& o)
378  {
379  size_t s;
380  (*this)>>s;
381  typename T::value_type v;
382  for (size_t i=0; i<s; ++i)
383  {
384  (*this)>>v;
385  o.insert(v);
386  }
387  return *this;
388  }
389 
391  template <class T, class A>
392  inline BinStream& operator<<(const std::vector<T,A>& o);
393 
394  template <class T, class A>
395  inline BinStream& operator>>(std::vector<T,A>& o);
396 
397  };
398 
404  template <class Pack> struct BinStreamT: public BinStream
405  {
406  Pack thePack;
407  BinStreamT(): BinStream(thePack) {}
408  template <class A1> BinStreamT(A1 a1):
409  BinStream(thePack), thePack(a1) {}
410  template <class A1, class A2> BinStreamT(A1 a1, A2 a2):
411  BinStream(thePack), thePack(a1, a2) {}
412  };
413 
414 
424  template <class T>
425  struct unserialisable: public T
426  {
427  unserialisable() {}
428  unserialisable(const T& x): T(x) {}
429  unserialisable operator=(const T& x) {T::operator=(x); return *this;}
430  };
431 
432 
433  template <class T> pack_t& operator<<(pack_t& y,const T&x);
434  template <class T> pack_t& operator>>(pack_t& y,T&x);
435 
438  template <class T> struct pack_supported:
439  public is_fundamental<T> {};
440 
441 #ifndef THROW_PTR_EXCEPTION
442  template <class T>
443  inline void pack(pack_t& targ, const string& desc, is_treenode dum, const T* const& arg);
444 
445  template <class T>
446  inline void unpack(unpack_t& targ, const string& desc,
447  is_treenode dum, T*& arg);
448 
449  template <class T>
450  inline void pack(pack_t& targ, const string& desc,
451  is_graphnode dum,const T& arg);
452 
454 
455  template <class T>
456  inline void unpack(pack_t& targ, const string& desc,
457  is_graphnode dum,T& arg);
458 
459 #endif
460 
461  template <class T>
462  void pack_onbase(pack_t& x,const string& d,T& a)
463  {pack(x,d,a);}
464 
465  template <class T>
466  void unpack_onbase(unpack_t& x,const string& d,T& a)
467  {unpack(x,d,a);}
468 }
469 
470 using classdesc::pack_onbase;
471 using classdesc::unpack_onbase;
472 
478 namespace classdesc_access
479 {
481  template <class T> struct access_pack;
483  template <class T> struct access_unpack;
484 
485  /* default action for pointers is to throw an error message */
486  template <class T>
487  struct access_pack<T*>
488  {
489  template <class C>
490  void operator()(classdesc::pack_t& targ, const classdesc::string& desc, C& arg)
491  {
492  switch (targ.ptr_flag)
493  {
494  // You may wish to define this macro if you have messy types containing pointers
495 #ifndef THROW_PTR_EXCEPTION
496  case classdesc::GRAPH:
497  pack(targ,desc,classdesc::is_graphnode(),arg);
498  break;
499  case classdesc::TREE:
500  pack(targ,desc,classdesc::is_treenode(),arg);
501  break;
502 #endif
503  default:
504  throw classdesc::pack_error("Packing arbitrary pointer data not implemented");
505  }
506  }
507  };
508 
509  template <class T>
510  struct access_unpack<T*>
511  {
512  template <class C>
513  void operator()(classdesc::unpack_t& targ, const classdesc::string& desc, C& arg)
514  {
515  switch (targ.ptr_flag)
516  {
517  // You may wish to define this macro if you have messy types containing pointers
518 #ifndef THROW_PTR_EXCEPTION
519  case classdesc::GRAPH:
520  unpack(targ,desc,classdesc::is_graphnode(),arg);
521  break;
522  case classdesc::TREE:
523  unpack(targ,desc,classdesc::is_treenode(),arg);
524  break;
525 #endif
526  default:
527  throw classdesc::pack_error("Unpacking arbitrary pointer data not implemented");
528  }
529  }
530  };
531 
532  template <class T>
533  struct access_pack<classdesc::unserialisable<T> >:
534  public classdesc::NullDescriptor<classdesc::pack_t> {};
535 
536  template <class T>
537  struct access_unpack<classdesc::unserialisable<T> >:
538  public classdesc::NullDescriptor<classdesc::pack_t> {};
539 
540  template <class T>
541  struct access_pack<classdesc::Exclude<T> >:
542  public classdesc::NullDescriptor<classdesc::pack_t> {};
543 
544  template <class T>
545  struct access_unpack<classdesc::Exclude<T> >:
546  public classdesc::NullDescriptor<classdesc::pack_t> {};
547 
548 }
549 
550 namespace classdesc
551 {
552  //generic pack, unpack - defined in pack_stream
553  template <class T> typename
555  pack(pack_t& buf, const string& desc, T& arg)
556  {classdesc_access::access_pack<T>()(buf,desc,arg);}
557 
558 
559  template <class T> typename
561  unpack(unpack_t& buf, const string& desc, T& arg)
562  {classdesc_access::access_unpack<T>()(buf,desc,arg);}
563 
564  template <class T>
565  typename enable_if<is_fundamental<T>, void>::T
566  pack(pack_t& targ, const string&, T& arg)
567  {
568  Basic_Type<T> b(arg);
569  targ.append(b);
570  }
571 
572  template <class T>
573  typename enable_if<is_fundamental<T>, void>::T
574  pack(pack_t& targ, const string&, const T& arg)
575  {
576  Basic_Type<T> b(arg);
577  targ.append(b);
578  }
579 
580  template <class T>
581  typename enable_if<is_fundamental<T>, void>::T
582  unpack(unpack_t& targ, const string&, T& arg)
583  {
584  Basic_Type<T> b(arg);
585  targ.popoff(b);
586  }
587 
588  template <class T>
589  typename enable_if<is_fundamental<T>, void>::T
590  unpack(unpack_t& targ, const string&, const T&)
591  { /* const vars cannot be unpacked */
592  T dum; Basic_Type<T> b(dum);
593  targ.popoff(b);
594  }
595 
596 
597  /* The problem is that function pointers this template also, so we
598  need a separate pack to packup pointers to single objects:
599 
600  pack a flag indicating validity, and then the object pointer points
601  to. Cannot handle arrays, but useful for graphs - assumes pointer is
602  valid unless NULL.
603 
604  */
605 
606 
607  template <class T>
608  void pack(pack_t& targ, const string& desc, is_array,
609  T &arg,int dims,size_t ncopies,...)
610  {
611  va_list ap;
612  va_start(ap,ncopies);
613  for (int i=1; i<dims; i++) ncopies*=va_arg(ap,int); //assume that 2 and higher D arrays dimensions are int
614  va_end(ap);
615  for (size_t i=0; i<ncopies; i++) pack(targ,desc,(&arg)[i]);
616  }
617 
618  template <class T>
619  void unpack(unpack_t& targ, const string& desc, is_array, T &arg,
620  int dims,size_t ncopies,...)
621  {
622  va_list ap;
623  va_start(ap,ncopies);
624  for (int i=1; i<dims; i++) ncopies*=va_arg(ap,int);
625  va_end(ap);
626  for (size_t i=0; i<ncopies; i++) unpack(targ,desc,(&arg)[i]);
627  }
628 
629  /* specialise for char* */
630  inline void pack(pack_t& targ, const string&, is_array,
631  char &arg,int dims,size_t ncopies,...)
632  {
633  int i;
634  va_list ap;
635  va_start(ap,ncopies);
636  for (i=1; i<dims; i++) ncopies*=va_arg(ap,int);
637  va_end(ap);
638  targ.packraw(&arg,ncopies);
639  }
640 
641  inline void unpack(unpack_t& targ, const string&, is_array,
642  char &arg,int dims,size_t ncopies,...)
643  {
644  int i;
645  va_list ap;
646  va_start(ap,ncopies);
647  for (i=1; i<dims; i++) ncopies*=va_arg(ap,int);
648  va_end(ap);
649  targ.unpackraw(&arg,ncopies);
650  }
651 
652  /* what to do about member functions */
653 
654  template<class C, class T>
655  void pack(pack_t&, const string&, C&, T) {}
656 
657  template<class C, class T>
658  void unpack(unpack_t&, const string&, C&, T) {}
659 
660  /*
661  Method pointer serialisation
662  */
663  template <class C, class R, class A1>
664  void pack(pack_t& targ, const string& desc, R (C::*&arg)(A1))
665  {targ.packraw((char*)&arg,sizeof(arg));}
666 
667  template <class C, class R, class A1>
668  void unpack(pack_t& targ, const string& desc, R (C::*&arg)(A1))
669  {targ.unpackraw((char*)&arg,sizeof(arg));}
670 
672  template <class T>
673  void pack(pack_t& targ, const string& desc, is_const_static i, T t)
674  {}
675 
676  template <class T>
677  void unpack(pack_t& targ, const string& desc,is_const_static i, T t)
678  {}
679 
680  // static methods
681  template <class T, class U>
682  void pack(pack_t&, const string&,is_const_static, const T&, U) {}
683 
684  template <class T, class U>
685  void unpack(pack_t& targ, const string& desc,is_const_static i, const T&, U) {}
686 
687  // to handle pack/unpacking of enums when -typeName is in effect
688  template <class E>
689  void pack(pack_t& targ, const string& desc,Enum_handle<E> a)
690  {
691  int x=a;
692  pack(targ,desc,x);
693  }
694 
695  template <class E>
696  void unpack(pack_t& targ, const string& desc, Enum_handle<E> a)
697  {
698  int x;
699  unpack(targ,desc,x);
700  a=x;
701  }
702 
703 }
704 
705 using classdesc::pack;
706 using classdesc::unpack;
707 using classdesc::pack_onbase;
708 using classdesc::unpack_onbase;
709 
710 #include "pack_stl.h"
711 
712 #ifdef _CLASSDESC
713 #pragma omit pack classdesc::string
714 #pragma omit pack eco_strstream
715 #pragma omit pack xdr_pack
716 #pragma omit unpack classdesc::string
717 #pragma omit unpack eco_strstream
718 #pragma omit unpack xdr_pack
719 #endif
720 #endif
size_t size() const
size of buffer
Definition: unpack_base.h:154
virtual int cmp(const pack_t &x) const
Definition: unpack_base.h:255
Definition: pack_base.h:404
Definition: pack_base.h:100
Definition: pack_base.h:330
Definition: classdesc.h:631
Definition: classdesc.h:623
Definition: pack_base.h:38
int deepCmp(const T &x, const T &y)
deep comparison of two serialisable items
Definition: pack_base.h:270
class to allow access to private members
Definition: classdesc_access.h:21
Definition: classdesc.h:626
helper for constructing null descriptors
Definition: classdesc.h:784
char * data()
actual buffer
Definition: unpack_base.h:153
Ptr_flag
Definition: pack_base.h:80
Definition: pack_base.h:51
bool deepEq(const T &x, const T &y)
deep equality of two serialisable items
Definition: pack_base.h:276
size_t m_size
size of buffer
Definition: pack_base.h:144
size_t m_pos
position of read pointer
Definition: pack_base.h:145
class to allow access to private members
Definition: classdesc_access.h:22
void pack(pack_t &targ, const string &desc, is_treenode dum, const T *const &arg)
serialise a tree (or DAG)
Definition: pack_graph.h:28
Definition: pack_base.h:65
Definition: classdesc.h:588
Definition: pack_base.h:438
const char * data() const
actual buffer
Definition: unpack_base.h:152
Definition: classdesc.h:630
serialisation for standard containers
Definition: pack_base.h:425
Definition: pack_base.h:110
Contains definitions related to classdesc functionality.
Definition: arrays.h:2514
Definition: pack_base.h:124
Definition: pack_base.h:87
controlled template specialisation: stolen from boost::enable_if.
Definition: classdesc.h:249
char * m_data
actual buffer
Definition: pack_base.h:143
size_t pos() const
position of read pointer
Definition: unpack_base.h:155
Contains access_* structs, and nothing else. These structs are used to gain access to private members...
Definition: accessor.h:55
Definition: classdesc.h:704
void unpack(unpack_t &targ, const string &desc, is_treenode dum, T *&arg)
unserialise a tree.
Definition: pack_graph.h:44