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