Adds initial API for providing function callbacks for serialization of annotations
[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 "dyntypes.h"
13 #include "util.h"
14
15 namespace Dyninst {
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 DLLEXPORT bool isBinary(Dyninst::SerializerBase *ser);
330 DLLEXPORT bool isOutput(Dyninst::SerializerBase *ser);
331
332 typedef void NOTYPE_T;
333 template<class S, class T, class T2 = NOTYPE_T>
334 class trans_adaptor {
335    public:
336       trans_adaptor() 
337       {
338          fprintf(stderr, "%s[%d]:  trans_adaptor  -- general\n", __FILE__, __LINE__);
339       } 
340
341       T * operator()(S *ser, T & it, const char *tag = NULL, const char * /*tag2*/ = NULL)
342       {
343          trans_adapt(ser, it, tag);
344          return &it;
345       }
346 };
347
348 template<class S, class T2>
349 class trans_adaptor<S, Serializable, T2> {
350    public:
351       trans_adaptor() 
352       {
353          fprintf(stderr, "%s[%d]:  trans_adaptor  -- general\n", __FILE__, __LINE__);
354       } 
355
356       Serializable * operator()(S *ser, Serializable & it, const char *tag = NULL, 
357             const char * /*tag2*/ = NULL)
358       {
359          gtranslate(ser, it, tag);
360          return &it;
361       }
362 };
363
364 template<class S, class T, class TT2>
365 class trans_adaptor<S, std::vector<T>, TT2 > {
366    public:
367       trans_adaptor()
368       {
369          fprintf(stderr, "%s[%d]:  trans_adaptor  -- vectorl\n", __FILE__, __LINE__);
370       }
371
372       std::vector<T> * operator()(S *ser, std::vector<T> &v, const char *tag = NULL, 
373             const char *tag2 = NULL) 
374       {
375          translate_vector(ser, v, tag, tag2);         //  maybe catch errors here?
376          return &v;
377       }
378 };
379
380 template<class S, class T, class TT2>
381 class trans_adaptor<S, std::vector<T *>, TT2>  {
382    public: 
383       trans_adaptor() 
384       {
385          fprintf(stderr, "%s[%d]:  trans_adaptor  -- vector of ptrs\n", __FILE__, __LINE__);
386       }
387
388       std::vector<T*> * operator()(S *ser, std::vector<T *> &v, const char *tag = NULL, 
389             const char *tag2 = NULL) 
390       {
391          translate_vector(ser, v, tag, tag2);
392          //  maybe catch errors here?
393          return &v;
394       }
395 };
396
397 #if 0
398 template<class T, class ANNO_NAME, bool, annotation_implementation_t>
399 class Annotatable<T, ANNO_NAME,bool, annotation_implementation_t>;
400 #endif
401    
402 #if 0
403 I really really wish this worked, maybe it still can given some more pounding
404
405 template<class S, class T, class ANNO_NAME, bool SERIALIZABLE, annotation_implementation_t IMPL>
406 class trans_adaptor<S, 
407       Annotatable<T, 
408       ANNO_NAME, 
409       SERIALIZABLE,
410       IMPL> &>  {
411    public: 
412       trans_adaptor() 
413       {
414          fprintf(stderr, "%s[%d]:  trans_adaptor  -- annotatable<%s, %s, %s>\n", 
415                __FILE__, __LINE__, typeid(T).name(), typeid(ANNO_NAME).name(),
416                SERIALIZABLE ? "true" : "false");
417       }
418
419       Annotatable<T, ANNO_NAME, SERIALIZABLE, IMPL> * operator()(S *ser, 
420             Annotatable<T, ANNO_NAME, SERIALIZABLE, IMPL> &v, const char *tag = NULL, 
421             const char *tag2 = NULL) 
422       {
423           if (!SERIALIZABLE) 
424           {
425              fprintf(stderr, "%s[%d]:  Annotatable<%s, %s, %s>, not serializable\n", 
426                    __FILE__, __LINE__, typeid(T).name(), typeid(ANNO_NAME).name(),
427                    SERIALIZABLE ? "true" : "false");
428              return NULL;
429           }
430
431           int nelem = v.size();
432
433           if (0 == nelem) 
434           {
435              fprintf(stderr, "%s[%d]:  Annotatable<%s, %s, %s>, no annotations\n", 
436                    __FILE__, __LINE__, typeid(T).name(), typeid(ANNO_NAME).name(),
437                    SERIALIZABLE ? "true" : "false");
438              return NULL;
439           }
440
441           //  data structure must exist since size > 0
442
443           std::vector<T> &anno_vec = v.getDataStructure();
444
445           //  But is this OK??  (This goes around the usual annotations interface)
446           //  probably not, but let's see if it works anyways
447
448           fprintf(stderr, "%s[%d]:  WARNING:  This may not be kosher -- circumventing the anotations interface, think on this\n", __FILE__, __LINE__);
449           translate_vector(ser, anno_vec, tag, tag2);
450
451          //  maybe catch errors here?
452          return &v;
453       }
454 };
455 #endif
456
457 template <class S, class T>
458 void gtranslate(S *ser, T &it, const char *tag = NULL, const char *tag2 = NULL)
459 {
460    fprintf(stderr, "%s[%d]:  welcome to gtranslate<%s, %s>(%p)\n",
461          __FILE__, __LINE__,
462          "SerializerBase",
463          typeid(T).name(), &it);
464
465    //  Maybe just need to do try/catch here since the template mapping may 
466    //  change the type of return value thru template specialization
467
468    trans_adaptor<S, T> ta;
469    fprintf(stderr, "%s[%d]: gtranslate: before operation\n", __FILE__, __LINE__);
470
471    T *itp = ta(ser, it, tag, tag2);
472
473    if (!itp) 
474    {
475       fprintf(stderr, "%s[%d]: translate adaptor failed to de/serialize\n", 
476             __FILE__, __LINE__);
477    }
478 }
479
480 DLLEXPORT bool ifxml_start_element(SerializerBase *sb, const char *tag);
481 DLLEXPORT bool ifxml_end_element(SerializerBase *sb, const char * /*tag*/);
482
483 class SerializerBin;
484 class SerializerXML;
485
486 DLLEXPORT_COMMON bool sb_is_input(SerializerBase *sb);
487 DLLEXPORT_COMMON bool sb_is_output(SerializerBase *sb);
488
489 template <class T>
490 bool ifinput(bool (*f)(SerializerBase *, T*), SerializerBase *sb, T *itp)
491 {
492    if (!sb_is_input(sb))
493       return false;
494
495
496    return (*f)(sb, itp);
497 }
498
499 template <class T>
500 bool ifoutput(bool (*f)(SerializerBase *, T*), SerializerBase *sb, T *itp)
501 {
502    if (!sb_is_output(sb))
503       return false;
504
505    return (*f)(sb, itp);
506 }
507
508 template <class T>
509 bool ifbin(bool (*f)(SerializerBase *, T*), SerializerBase *sb, T *itp)
510 {
511    SerializerBin *sbin = dynamic_cast<SerializerBin *>(sb);
512
513    if (!sbin)
514       return false;
515
516    return (*f)(sbin, itp);
517 }
518
519 template <class T>
520 bool ifxml(bool (*f)(SerializerBase *, T*), SerializerBase *sb, T *itp)
521 {
522    SerializerXML *sxml = dynamic_cast<SerializerXML *>(sb);
523
524    if (!sxml)
525       return false;
526
527    return (*f)(sxml, itp);
528 }
529
530 #if 0
531 template <class S, class TT>
532 void gtranslate(S *ser, TT&it, void (*use_func)(SerializerBase *, TT &, void *), const char *tag = NULL, const char *tag2)
533 {
534    fprintf(stderr, "%s[%d]:  welcome to gtranslate<%s, %s>(%p)\n",
535          __FILE__, __LINE__,
536          "SerializerBase",
537          typeid(TT).name(), &it);
538
539    //  Maybe just need to do try/catch here since the template mapping may 
540    //  change the type of return value thru template specialization
541    assert(use_func);
542    (*use_func)(ser, it);
543 }
544
545 template<class S, class TT>
546 void trans_enum(S *ser, TT &it, std::vector<std::string> *enum_tags_ptr) 
547 {
548    assert(enum_tags_ptr);
549    std::vector<std::string> &enum_tags = *enum_tags_ptr;
550    assert(it < enum_tags.size());
551    unsigned int enum_int = (unsigned int) it;
552    gtranslate(ser, it);
553 }
554 #endif
555
556
557 template <class S, class T>
558 void gtranslate(S *ser, 
559       T &it, 
560       const char * (*to_str_func)(T), 
561       const char *tag = NULL, 
562       const char * /*tag2*/ = NULL)
563 {
564    assert(ser);
565    int enum_int = (int) it;
566
567    if (!isBinary(ser)) 
568    {
569       assert(isOutput(ser));
570
571       // use human-readable tag
572       const char *enum_tag = (*to_str_func)(it);
573       std::string enum_tag_str(enum_tag);
574       assert(enum_tag);
575
576       gtranslate(ser, enum_tag_str, tag, NULL);
577    }
578    else 
579    {
580       //  just in/output raw binary value 
581       gtranslate(ser, enum_int, tag, NULL);
582       it = (T) enum_int;
583    }
584 }
585
586 class SerializerError;
587
588 template <class S, class T>
589 bool gtranslate_w_err(S *ser, T&it, const char *tag = NULL, const char *tag2 = NULL)
590 {
591
592    try 
593    {
594       gtranslate(ser, it, tag, tag2);
595    }
596
597    catch (const SerializerError &err_) 
598    {
599       fprintf(stderr, "%s[%d]:  gtranslate failed\n", __FILE__, __LINE__);
600       printSerErr(err_);
601       return false;
602    }
603    return true;
604 }
605
606 } /* namespace Dyninst */
607 #endif