Merge branch 'master' of git.dyninst.org:/pub/dyninst
[dyninst.git] / dynutil / h / Serialization.h
1
2 #if !defined (SERIALIZATION_PUBLIC_H)
3 #define SERIALIZATION_PUBLIC_H
4 //  Hopefully just a few definitions allowing for a public interface to 
5 //  serializing user-providede annotations
6
7 #include <stdio.h>
8 #include <assert.h>
9 #include <stdexcept>
10 #include <typeinfo>
11 #include <vector>
12 #include <assert.h>
13 #include "dyntypes.h"
14 #include "util.h"
15
16 //  SER_ERR("msg") -- an attempt at "graceful" failure.  If debug flag is set
17 //  it will assert, otherwise it throws...  leaving the "graceful" aspect
18 //  to the next (hopefully top-level) exception handler.
19
20
21 DLLEXPORT bool &serializer_debug_flag();
22
23 #define SER_ERR(cmsg) \
24    do { \
25       if (serializer_debug_flag()) { \
26          fprintf(stderr, "%s", cmsg); \
27          assert (0); \
28       } else { \
29          throw SerializerError(__FILE__, __LINE__, std::string(cmsg)); \
30       } \
31    } while (0)
32
33
34 class SerializerBase;
35 typedef enum {sd_serialize, sd_deserialize} iomode_t;
36
37 class DLLEXPORT Serializable {
38    protected:
39       Serializable() {}
40       virtual ~Serializable() {}
41
42    public:
43       virtual void serialize(SerializerBase *,  const char * = NULL) = 0;
44 };
45
46 class SerializerError : public std::runtime_error {
47    //  SerializerError:  a small class that is thrown by serialization/deserialization
48    //  routines.  This exists as an attempt to standardize and simplify error handling
49    //  for ser-des routines that are possibly deeply nested.
50    //  Here's the rub:  we don't want stray, unhandled exceptions finding their way into
51    //  the larger system...  thus all entry points to serialization/deserialization need
52    //  to catch this exception to render it transparent to the rest of the system.
53
54    public:
55
56    typedef enum {
57       ser_err_unspecified,
58       ser_err_no_err,
59       ser_err_disabled
60    } SerializerErrorType;
61
62    private:
63
64    std::string file__;
65    int line__;
66    SerializerErrorType err__;
67
68    public:
69
70
71    SerializerError(const std::string &__file__, 
72          const int &__line__, 
73          const std::string &msg, 
74          SerializerErrorType __err__ = ser_err_unspecified) :
75       runtime_error(msg),
76       file__(__file__),
77       line__(__line__),
78       err__(__err__)
79    {}
80
81    virtual ~SerializerError() throw() {}
82
83    std::string file() const {return file__;}
84    int line() const {return line__;}
85    SerializerErrorType code() const {return err__;}
86 };
87
88
89 DLLEXPORT void printSerErr(const SerializerError &err);
90
91 template <class S, class T>
92 void translate_vector(S *ser, std::vector<T> &vec,
93       const char *tag = NULL, const char *elem_tag = NULL)
94 {
95    unsigned int nelem = vec.size();
96    ser->vector_start(nelem, tag);
97
98    if (ser->iomode() == sd_deserialize) 
99    {
100       if (vec.size())
101          SER_ERR("nonempty vector used to create");
102
103       //  zero size vectors are allowed
104       //  what it T is a complex type (with inheritance info)??
105       //  does resize() call default ctors, or should we do that
106       //  manually here? look this up.
107       if (nelem)
108          vec.resize(nelem);
109    }
110
111    for (unsigned int i = 0; i < vec.size(); ++i) 
112    {
113       T &t = vec[i];
114       gtranslate(ser, t, elem_tag);
115    }
116
117    ser->vector_end();
118 }
119
120
121 template <class S, class T>
122 void translate_vector(S *ser, std::vector<T *> &vec, 
123       const char *tag = NULL, const char *elem_tag = NULL) 
124 {
125    unsigned int nelem = vec.size();
126    ser->vector_start(nelem, tag);
127
128    if (ser->iomode() == sd_deserialize) 
129    {
130       if (vec.size()) 
131          SER_ERR("nonempty vector used to create");
132
133       //  zero size vectors are allowed
134       if (nelem) 
135       {
136          //  block-allocate array of underlying type, then assign to our vector
137          //  What happens if an individual elem is later deleted??
138          T *chunk_alloc = new T[nelem];
139          vec.resize(nelem);
140          for (unsigned int i = 0; i < nelem; ++i)
141             vec[i] = &(chunk_alloc[i]);
142       }
143    }
144
145    for (unsigned int i = 0; i < vec.size(); ++i) 
146    {
147       T &t = *(vec[i]);
148       gtranslate(ser, t, elem_tag);
149    }
150
151    ser->vector_end();
152 }
153
154 template <class S, class T>
155 void translate_vector(S *ser, std::vector<std::vector<T> > &vec, 
156       const char *tag = NULL, const char *elem_tag = NULL) 
157 {
158    fprintf(stderr, "%s[%d]:  welcome to translate vector of vectors\n", 
159            __FILE__, __LINE__);
160
161    unsigned int nelem = vec.size();
162    ser->vector_start(nelem, tag);
163    if (ser->iomode() == sd_deserialize) 
164    {
165       if (vec.size())
166          SER_ERR("nonempty vector used to create");
167
168       //  zero size vectors are allowed
169       //  what it T is a complex type (with inheritance info)??
170       //  does resize() call default ctors, or should we do that
171       //  manually here? look this up.
172       if (nelem)
173          vec.resize(nelem);
174    }
175
176    for (unsigned int i = 0; i < vec.size(); ++i) 
177    {
178       std::vector<T> &tv = vec[i];
179       translate_vector(ser,tv, tag, elem_tag);
180    }
181
182    ser->vector_end();
183 }
184
185 template <class S, class K, class V>
186 void translate_hash_map(S *ser, dyn_hash_map<K, V> &hash, 
187       const char *tag = NULL, const char *key_tag = NULL, const char *value_tag = NULL)
188 {   
189    fprintf(stderr, "%s[%d]:  welcome to translate_hash_map<%s, %s>()\n", 
190            __FILE__, __LINE__,
191            typeid(K).name(), typeid(V).name()); 
192
193    unsigned int nelem = hash.size();
194    ser->hash_map_start(nelem, tag);
195    fprintf(stderr, "%s[%d]:  after hash_map start, mode = %sserialize\n", 
196            __FILE__, __LINE__, ser->iomode() == sd_serialize ? "" : "de"); 
197
198    if (ser->iomode() == sd_serialize) 
199    {
200       typename dyn_hash_map<K,V>::iterator iter = hash.begin();
201       fprintf(stderr, "%s[%d]:  about to serialize hash with %d elements\n", 
202               __FILE__, __LINE__, hash.size());
203
204       while (iter != hash.end()) 
205       {
206          K k = iter->first;
207          V v = iter->second;
208          ser->translate_base(k, key_tag);
209          ser->translate_base(v, value_tag);
210          iter++;           
211       }
212    }
213    else 
214    {
215       //  can we do some kind of preallocation here?
216       for (unsigned int i = 0; i < nelem; ++i) 
217       {
218          K k;
219          V v;
220          ser->translate_base(k, key_tag);
221          ser->translate_base(v, value_tag);
222          hash[k] = v;
223       }
224    }
225
226    ser->hash_map_end();
227 }
228
229 template <class S, class K, class V>
230 void translate_hash_map(S *ser, dyn_hash_map<K, V *> &hash,
231       const char *tag = NULL, const char *key_tag = NULL, const char *value_tag = NULL)
232 {
233    fprintf(stderr, "%s[%d]:  welcome to translate_hash_map<%s, %s*>()\n", 
234          __FILE__, __LINE__,
235          typeid(K).name(), typeid(V).name());
236
237    unsigned int nelem = hash.size();
238    ser->hash_map_start(nelem, tag);
239
240    fprintf(stderr, "%s[%d]:  after hash_map start, mode = %sserialize\n", 
241          __FILE__, __LINE__, ser->iomode() == sd_serialize ? "" : "de");
242
243    if (ser->iomode() == sd_serialize) 
244    {
245       typename dyn_hash_map<K,V *>::iterator iter = hash.begin();
246
247       while (iter != hash.end()) 
248       {
249          K k = iter->first;
250          V *v = iter->second;
251          ser->translate_base(k, key_tag);
252          ser->translate_base(*v, value_tag);
253          iter++;
254       }
255    }
256    else 
257    {
258       //  can we do some kind of preallocation here?
259       for (unsigned int i = 0; i < nelem; ++i) 
260       {
261          K k;
262          V *v = new V();
263          ser->translate_base(k, key_tag);
264          ser->translate_base(*v, value_tag);
265          hash[k] = v;
266       }
267    }
268    ser->hash_map_end();
269 }
270
271 template <class S, class K, class V>
272 void translate_hash_map(S *ser, dyn_hash_map<K, char *> &hash,
273       const char *tag = NULL, const char *key_tag = NULL, const char *value_tag = NULL)
274 {
275    //  THIS SPECIALIZATION DOES NOT WORK CORRECTLY (YET)
276    fprintf(stderr, "%s[%d]:  welcome to translate_hash_map<%s, %s*>()\n", 
277          __FILE__, __LINE__,
278          typeid(K).name(), typeid(V).name());
279
280    unsigned int nelem = hash.size();
281    ser->hash_map_start(nelem, tag);
282
283    fprintf(stderr, "%s[%d]:  after hash_map start, mode = %sserialize\n", 
284          __FILE__, __LINE__, ser->iomode() == sd_serialize ? "" : "de");
285
286    if (ser->iomode() == sd_serialize) 
287    {
288       typename dyn_hash_map<K,V *>::iterator iter = hash.begin();
289       
290       while (iter != hash.end()) 
291       {
292          K k = iter->first;
293          V v = iter->second;
294          ser->translate_base(k, key_tag);
295          ser->translate_base(v, value_tag);
296          iter++;
297       }
298    }
299    else 
300    {
301       //  can we do some kind of preallocation here?
302       for (unsigned int i = 0; i < nelem; ++i) 
303       {
304          K k;
305          V v;
306          ser->translate_base(k, key_tag);
307          ser->translate_base(*v, value_tag);
308          hash[k] = v;
309       }
310    }
311    ser->hash_map_end();
312 }
313
314
315 DLLEXPORT void trans_adapt(SerializerBase *ser, Serializable &it,  const char *tag);
316 DLLEXPORT void trans_adapt(SerializerBase *ser, Serializable *itp,  const char *tag);
317
318 DLLEXPORT void trans_adapt(SerializerBase *ser, bool &it,  const char *tag);
319 DLLEXPORT void trans_adapt(SerializerBase *ser, int &it,  const char *tag);
320 DLLEXPORT void trans_adapt(SerializerBase *ser, unsigned int &it,  const char *tag);
321 DLLEXPORT void trans_adapt(SerializerBase *ser, long &it,  const char *tag);
322 DLLEXPORT void trans_adapt(SerializerBase *ser, unsigned long &it,  const char *tag);
323 DLLEXPORT void trans_adapt(SerializerBase *ser, char &it,  const char *tag);
324 DLLEXPORT void trans_adapt(SerializerBase *ser, char *&it,  const char *tag);
325 DLLEXPORT void trans_adapt(SerializerBase *ser, std::string &it,  const char *tag);
326 DLLEXPORT void trans_adapt(SerializerBase *ser, float &it,  const char *tag);
327 DLLEXPORT void trans_adapt(SerializerBase *ser, double &it,  const char *tag);
328
329 typedef void NOTYPE_T;
330 template<class S, class T, class T2 = NOTYPE_T>
331 class trans_adaptor {
332    public:
333       trans_adaptor() 
334       {
335          fprintf(stderr, "%s[%d]:  trans_adaptor  -- general\n", __FILE__, __LINE__);
336       } 
337
338       T * operator()(S *ser, T & it, const char *tag = NULL, const char * /*tag2*/ = NULL)
339       {
340          trans_adapt(ser, it, tag);
341          return &it;
342       }
343 };
344
345 template<class S, class T2>
346 class trans_adaptor<S, Serializable, T2> {
347    public:
348       trans_adaptor() 
349       {
350          fprintf(stderr, "%s[%d]:  trans_adaptor  -- general\n", __FILE__, __LINE__);
351       } 
352
353       Serializable * operator()(S *ser, Serializable & it, const char *tag = NULL, 
354             const char * /*tag2*/ = NULL)
355       {
356          gtranslate(ser, it, tag);
357          return &it;
358       }
359 };
360
361 template<class S, class T, class TT2>
362 class trans_adaptor<S, std::vector<T>, TT2 > {
363    public:
364       trans_adaptor()
365       {
366          fprintf(stderr, "%s[%d]:  trans_adaptor  -- vectorl\n", __FILE__, __LINE__);
367       }
368
369       std::vector<T> * operator()(S *ser, std::vector<T> &v, const char *tag = NULL, 
370             const char *tag2 = NULL) 
371       {
372          translate_vector(ser, v, tag, tag2);         //  maybe catch errors here?
373          return &v;
374       }
375 };
376
377 template<class S, class T, class TT2>
378 class trans_adaptor<S, std::vector<T *>, TT2>  {
379    public: 
380       trans_adaptor() 
381       {
382          fprintf(stderr, "%s[%d]:  trans_adaptor  -- vector of ptrs\n", __FILE__, __LINE__);
383       }
384
385       std::vector<T*> * operator()(S *ser, std::vector<T *> &v, const char *tag = NULL, 
386             const char *tag2 = NULL) 
387       {
388          translate_vector(ser, v, tag, tag2);
389          //  maybe catch errors here?
390          return &v;
391       }
392 };
393
394 #if 0
395 template<class T, class ANNO_NAME, bool, annotation_implementation_t>
396 class Annotatable<T, ANNO_NAME,bool, annotation_implementation_t>;
397 #endif
398    
399 #if 0
400 I really really wish this worked, maybe it still can given some more pounding
401
402 template<class S, class T, class ANNO_NAME, bool SERIALIZABLE, annotation_implementation_t IMPL>
403 class trans_adaptor<S, 
404       Annotatable<T, 
405       ANNO_NAME, 
406       SERIALIZABLE,
407       IMPL> &>  {
408    public: 
409       trans_adaptor() 
410       {
411          fprintf(stderr, "%s[%d]:  trans_adaptor  -- annotatable<%s, %s, %s>\n", 
412                __FILE__, __LINE__, typeid(T).name(), typeid(ANNO_NAME).name(),
413                SERIALIZABLE ? "true" : "false");
414       }
415
416       Annotatable<T, ANNO_NAME, SERIALIZABLE, IMPL> * operator()(S *ser, 
417             Annotatable<T, ANNO_NAME, SERIALIZABLE, IMPL> &v, const char *tag = NULL, 
418             const char *tag2 = NULL) 
419       {
420           if (!SERIALIZABLE) 
421           {
422              fprintf(stderr, "%s[%d]:  Annotatable<%s, %s, %s>, not serializable\n", 
423                    __FILE__, __LINE__, typeid(T).name(), typeid(ANNO_NAME).name(),
424                    SERIALIZABLE ? "true" : "false");
425              return NULL;
426           }
427
428           int nelem = v.size();
429
430           if (0 == nelem) 
431           {
432              fprintf(stderr, "%s[%d]:  Annotatable<%s, %s, %s>, no annotations\n", 
433                    __FILE__, __LINE__, typeid(T).name(), typeid(ANNO_NAME).name(),
434                    SERIALIZABLE ? "true" : "false");
435              return NULL;
436           }
437
438           //  data structure must exist since size > 0
439
440           std::vector<T> &anno_vec = v.getDataStructure();
441
442           //  But is this OK??  (This goes around the usual annotations interface)
443           //  probably not, but let's see if it works anyways
444
445           fprintf(stderr, "%s[%d]:  WARNING:  This may not be kosher -- circumventing the anotations interface, think on this\n", __FILE__, __LINE__);
446           translate_vector(ser, anno_vec, tag, tag2);
447
448          //  maybe catch errors here?
449          return &v;
450       }
451 };
452 #endif
453
454 template <class S, class T>
455 void gtranslate(S *ser, T &it, const char *tag = NULL, const char *tag2 = NULL)
456 {
457    fprintf(stderr, "%s[%d]:  welcome to gtranslate<%s, %s>(%p)\n",
458          __FILE__, __LINE__,
459          "SerializerBase",
460          typeid(T).name(), &it);
461
462    //  Maybe just need to do try/catch here since the template mapping may 
463    //  change the type of return value thru template specialization
464
465    trans_adaptor<S, T> ta;
466    fprintf(stderr, "%s[%d]: gtranslate: before operation\n", __FILE__, __LINE__);
467
468    T *itp = ta(ser, it, tag, tag2);
469
470    if (!itp) 
471    {
472       fprintf(stderr, "%s[%d]: translate adaptor failed to de/serialize\n", 
473             __FILE__, __LINE__);
474    }
475 }
476
477 DLLEXPORT bool ifxml_start_element(SerializerBase *sb, const char *tag);
478 DLLEXPORT bool ifxml_end_element(SerializerBase *sb, const char * /*tag*/);
479
480 class SerializerBin;
481 class SerializerXML;
482
483 DLLEXPORT_COMMON bool sb_is_input(SerializerBase *sb);
484 DLLEXPORT_COMMON bool sb_is_output(SerializerBase *sb);
485
486 template <class T>
487 bool ifinput(bool (*f)(SerializerBase *, T*), SerializerBase *sb, T *itp)
488 {
489    if (!sb_is_input(sb))
490       return false;
491
492
493    return (*f)(sb, itp);
494 }
495
496 template <class T>
497 bool ifoutput(bool (*f)(SerializerBase *, T*), SerializerBase *sb, T *itp)
498 {
499    if (!sb_is_output(sb))
500       return false;
501
502    return (*f)(sb, itp);
503 }
504
505 template <class T>
506 bool ifbin(bool (*f)(SerializerBase *, T*), SerializerBase *sb, T *itp)
507 {
508    SerializerBin *sbin = dynamic_cast<SerializerBin *>(sb);
509
510    if (!sbin)
511       return false;
512
513    return (*f)(sbin, itp);
514 }
515
516 template <class T>
517 bool ifxml(bool (*f)(SerializerBase *, T*), SerializerBase *sb, T *itp)
518 {
519    SerializerXML *sxml = dynamic_cast<SerializerXML *>(sb);
520
521    if (!sxml)
522       return false;
523
524    return (*f)(sxml, itp);
525 }
526
527 #if 0
528 template <class S, class TT>
529 void gtranslate(S *ser, TT&it, void (*use_func)(SerializerBase *, TT &, void *), const char *tag = NULL, const char *tag2)
530 {
531    fprintf(stderr, "%s[%d]:  welcome to gtranslate<%s, %s>(%p)\n",
532          __FILE__, __LINE__,
533          "SerializerBase",
534          typeid(TT).name(), &it);
535
536    //  Maybe just need to do try/catch here since the template mapping may 
537    //  change the type of return value thru template specialization
538    assert(use_func);
539    (*use_func)(ser, it);
540 }
541
542 template<class S, class TT>
543 void trans_enum(S *ser, TT &it, std::vector<std::string> *enum_tags_ptr) 
544 {
545    assert(enum_tags_ptr);
546    std::vector<std::string> &enum_tags = *enum_tags_ptr;
547    assert(it < enum_tags.size());
548    unsigned int enum_int = (unsigned int) it;
549    gtranslate(ser, it);
550 }
551 #endif
552
553 DLLEXPORT bool isBinary(SerializerBase *ser);
554 DLLEXPORT bool isOutput(SerializerBase *ser);
555
556 template <class S, class T>
557 void gtranslate(S *ser, 
558       T &it, 
559       const char * (*to_str_func)(T), 
560       const char *tag = NULL, 
561       const char * /*tag2*/ = NULL)
562 {
563    assert(ser);
564    int enum_int = (int) it;
565
566    if (!isBinary(ser)) 
567    {
568       assert(isOutput(ser));
569
570       // use human-readable tag
571       const char *enum_tag = (*to_str_func)(it);
572       std::string enum_tag_str(enum_tag);
573       assert(enum_tag);
574
575       gtranslate(ser, enum_tag_str, tag, NULL);
576    }
577    else 
578    {
579       //  just in/output raw binary value 
580       gtranslate(ser, enum_int, tag, NULL);
581       it = (T) enum_int;
582    }
583 }
584
585 class SerializerError;
586
587 template <class S, class T>
588 bool gtranslate_w_err(S *ser, T&it, const char *tag = NULL, const char *tag2 = NULL)
589 {
590
591    try 
592    {
593       gtranslate(ser, it, tag, tag2);
594    }
595
596    catch (const SerializerError &err_) 
597    {
598       fprintf(stderr, "%s[%d]:  gtranslate failed\n", __FILE__, __LINE__);
599       printSerErr(err_);
600       return false;
601    }
602    return true;
603 }
604
605 #endif