// $Id$ #include #include #include #include #include #include #include #include using namespace std; #include #ifndef _JILSTREAM_H_ #define _JILSTREAM_H_ // First pass compilation will not have this // defined so we can provide a dummy function const char* JILMyDictionary(void); #ifndef _JIL_SERIALIZERS_H_ inline const char* JILMyDictionary(void){return "\n\tempty\n\n";} #endif /// The JILStream class is the base class used to derive classes /// capable of reading/writing objects in specialized file formats. The /// JILStream class itself is fully functional, but the file format /// it uses is uncompressed ASCII with lots of redundant information /// which leads to very large files. /// /// When writing a subclass of JILStream, one typically needs to /// override the default virtual methods for the following methods /// in addition to the methods for converting atomic types. See the /// descriptions below for more details. /// /// For serializing to a file: ///
    ///
  • JILStream& operator<<(const std::type_info *t) ///
  • JILStream& operator<<(JILStreamManipulator_t m) ///
  • void StartNamedWrite(const char *name) ///
  • void StartVectorWrite(const type_info &t, unsigned int size, unsigned int bytes_per_item) ///
  • void StartListWrite(const type_info &t, unsigned int size, unsigned int bytes_per_item) ///
  • void StartArrayWrite(const type_info &t, unsigned int size, unsigned int bytes_per_item) ///
  • bool StartPointerWrite(const type_info* &t, void* ptr) ///
/// /// For deserializing objects from a file: ///
    ///
  • bool GetNamed(const char *name) ///
  • void FreeNamed(void) ///
  • bool GetPointerFromStream(const type_info* type, void* &ptr) ///
  • unsigned int StartVectorRead(const type_info &t) ///
  • unsigned int StartListRead(const type_info &t) ///
  • unsigned int StartArrayRead(const type_info &t, unsigned int size) ///
  • void GetObjectsFromSource(const type_info *t, string tag, vector *objects_ptr) ///
///
class JILStream{ public: enum JILStreamIOType_t{ STREAM_INPUT, STREAM_OUTPUT }; enum JILStreamPointerTracking_t{ PTR_AUTOMATIC, PTR_NONE }; /// These "manipulators" are sent into the stream to flag the /// end of the various types of items. enum JILStreamManipulator_t{ END_NAMED, END_OBJECT, END_VECTOR, END_LIST, END_ARRAY, END_POINTER }; /// Constructor that opens basic JILStream file. This should NOT /// be called when instantiating a subclass object. See JILStreamInit /// below. JILStream(string filename="", string mode="w"){ if(mode == "w")iotype = STREAM_OUTPUT; else if(mode == "r")iotype = STREAM_INPUT; else {std::cerr<<"Unknown mode \""<filename = filename; pointer_depth = 0; type_depth = 0; named_depth = 0; vector_depth = 0; list_depth = 0; pointer_tracking = PTR_AUTOMATIC; keep_object_stats = false; } /// Destructor of basic JILStream object. virtual ~JILStream(){ FreeNamed(); } /// Read in dictionary information from the stream. This will /// read in the XML formatted dictionary information from the /// stream. It is assumed that most subclasses will use this /// routine, but it is made virtual in case some need to implement /// dictionary info in something other than the default XML. virtual void GetDictionary(istream *instream){ // For now, we just read in until we find the end tag char line[1024]; string endtag=""; do{ line[0] = 0; instream->getline(line,1024); if(endtag == line)break; }while(instream->gcount()!=0); } //-------------------- Serialization ------------------------- /// Set the current tag to be written out with subsequent objects inline void SetTag(string tag){ this->tag = tag; } /// Set the pointer tracking mode virtual void SetPointerTracking(JILStreamPointerTracking_t ptmode){ pointer_tracking = ptmode; } /// Set the object statistics tracking mode virtual void SetPointerTracking(bool keep_object_stats){ this->keep_object_stats = keep_object_stats; } // Return a value that can be used to refer to the current // stream position. What this position is relative to is // determined by the subclass virtual std::streamoff GetStreamPosition(void)=0; /// Atomic types. All atomic types (plus strings) are explicitly defined. virtual JILStream& operator<<(short i)=0; virtual JILStream& operator<<(int i)=0; virtual JILStream& operator<<(long i)=0; virtual JILStream& operator<<(unsigned short i)=0; virtual JILStream& operator<<(unsigned int i)=0; virtual JILStream& operator<<(unsigned long i)=0; virtual JILStream& operator<<(float f)=0; virtual JILStream& operator<<(double f)=0; virtual JILStream& operator<<(std::string s)=0; /// Handle stream manipulators virtual JILStream& operator<<(JILStreamManipulator_t m)=0; /// The first pass compilation will use this "catch-all" template /// since no specific operator<< methods are defined for the custom /// classes. Executables using this template shouldn't be run and are /// just used to provide the introspection information. template JILStream& operator<<(T t){ return UnknownOut(&typeid(t)); } /// Keep and check a cache of pointers for the current named section. /// This routine is used when serializing the object. inline bool CacheObjectPointerWrite(std::streamoff &pos, const std::type_info *t, void *ptr) { // If the pointer_tracking model is not PTR_AUTOMATIC, then // just tell the subclass to write out the object if(pointer_tracking != PTR_AUTOMATIC)return true; if(ptr == NULL){ pos = 0; return false; } // Look for pointer in cache vector::iterator iter = pointer_cache.begin(); for(;iter != pointer_cache.end(); iter++){ if((*iter).ptr == ptr && (*iter).type == t){ pos = (*iter).pos; return false; } } AddObjectToCache(pos,t,ptr); return true; } /// Keep and check a cache of pointers for the current named section. /// This routine is used when deserializing the object. bool CacheFindObjectPointer(std::streamoff pos, void* &ptr) { // pointer tracking model only affects how objects are written // so we ignore it here. // Look for pointer in cache vector::iterator iter = pointer_cache.begin(); for(;iter != pointer_cache.end(); iter++){ if((*iter).pos == pos){ ptr = (*iter).ptr; return true; // pointer found in cache. } } return false; // pointer not in cache. } /// Add a pointer with corresponding stream position to the cache inline void AddObjectToCache(std::streamoff pos, const std::type_info *t, void *ptr){ // Pointer wasn't found in cache. Add it and write out object pointer_cache_t p; p.ptr = ptr; p.type = t; p.pos = pos; pointer_cache.push_back(p); } /// This is called when an object is about to be written to the stream. /// If it returns "true", then the object data is sent, otherwise, it /// is not. template inline bool WriteObject(T *ptr){ return StartObjectWrite(&typeid(T), (void*)ptr); } // The StartObjectWrite() method is called just before the // data members are streamed. If it returns "true", then the /// object data is sent, otherwise, it is not. virtual bool StartObjectWrite(const std::type_info *t, void *ptr)=0; /// Pointer types are caught by this template. If a top-level /// object(i.e. type_depth==0) is streamed as a pointer, then /// the contents of the pointer are streamed (but only if the /// pointer is non-NULL). Otherwise, StartPointerWrite() is /// called to determine if the object's contents should be streamed. template inline JILStream& operator<<(const T* tptr){ if(type_depth==0 && tptr==NULL)return *this; return (*this)<<*tptr; } /// Pass this over to the const version template inline JILStream& operator<<(T* tptr){ (*this)<<(const T*)tptr; return (*this); } /// STL container classes are handled by templates. They basically /// convert the call to the template method into a call to a /// non-templated virtual method. This means the template only /// has to be here and the subclass does not need to deal with /// templates at all. We first call the StartVectorWrite() method so we /// can tell it(the subclass) what type of data, how many items, and how big /// each item is before streaming the individual items. After all /// items are streamed, we send an END_VECTOR manipulator, which may /// be a little redundant, but is easily enough ignored if it is not /// needed by the subclass. template JILStream& operator<<(std::vector v){ StartVectorWrite(typeid(T), v.size(), sizeof(T)); for(unsigned int i=0; i JILStream& operator<<(std::list v){ StartListWrite(typeid(T), v.size(), sizeof(T)); //for(list::iterator iter=v.begin(); iter!=v.end(); iter++)(*this)<<(*iter); (*this)< JILStream& WriteArray(T* tpr, unsigned int size){ StartArrayWrite(typeid(T), size, sizeof(T)); for(unsigned int i=0; i>(short &i)=0; virtual JILStream& operator>>(int &i)=0; virtual JILStream& operator>>(long &i)=0; virtual JILStream& operator>>(unsigned short &i)=0; virtual JILStream& operator>>(unsigned int &i)=0; virtual JILStream& operator>>(unsigned long &i)=0; virtual JILStream& operator>>(float &f)=0; virtual JILStream& operator>>(double &f)=0; virtual JILStream& operator>>(std::string &s)=0; template bool ReadObject(T* &ptr){ const std::type_info *t = &typeid(T); std::streamoff pos = GetStreamPosition(); bool read_object = StartObjectRead(t, (void*&)ptr); if(read_object){ if(ptr==NULL)ptr = new T(); AddObjectToCache(pos, t, (void *)ptr); if(type_depth==0)current_object_is_new=true; } return read_object; } virtual bool StartObjectRead(const std::type_info *t, void* &ptr)=0; template JILStream& operator>>(T &t){ T* tt = &t; (*this)>>tt; // The above call may actually change the value // of tt. This should only occur for non-top-level objects. // This will be the case when an object that is a member // of one object is referred to as a member pointer of another // object. Furthermore, it will only be a problem if // the pointer member is read first from the file since that // will create a new object and we can't set the address of // the member object. To properly handle this would cause a // major increase in complexity of the whole system. We try // and get by here using the copy constructor. if(tt != &t){ std::cerr<<__FILE__<<":"<<__LINE__<<" Copying object contents for type "< JILStream& operator>>(T* &t){ return UnknownIn(&typeid(T)); } /// Read a vector in from the stream template JILStream& operator>>(std::vector &v){ unsigned int size=StartVectorRead(typeid(T)); for(unsigned int i=0;i>t; v.push_back(t); } // end_vector should already be removed. return *this; } /// Read a list in from the stream template JILStream& operator>>(std::list &v){ unsigned int size=StartListRead(typeid(T)); for(unsigned int i=0;i>t; v.push_back(t); } // end_list should already be removed. return *this; } /// returning false means we have handled this object and /// the deserializer routine should NOT try filling from the /// stream. virtual bool StartPointerRead(const std::type_info* type, void* &ptr)=0; /// Get vector info from stream virtual unsigned int StartVectorRead(const type_info &t)=0; /// Get list info from stream virtual unsigned int StartListRead(const type_info &t)=0; /// Template method to read in an array of items template JILStream& ReadArray(T* tpr, unsigned int size){ unsigned int mysize=StartArrayRead(typeid(T), size); if(mysize>*tpr; return *this; } /// Get array info from stream virtual unsigned int StartArrayRead(const type_info &t, unsigned int size)=0; virtual JILStream& UnknownIn(const type_info* type){ std::cerr<<"Attempting to convert unknown object!! (type="<name()<<")"< void GetObjects(vector &v, string tag = "", vector *objects_ptr=NULL){ // Look through all object records and copy the pointers of // type T into the vector v. if(objects_ptr==NULL)objects_ptr = &objects; const type_info *t = &typeid(T); GetObjectsFromSource(t, tag, objects_ptr); vector::iterator iter; for(iter=objects_ptr->begin(); iter!=objects_ptr->end(); iter++){ if((*iter)->type == t && (*iter)->tag == tag)v.push_back((T*)(*iter)->ptr); } } /// Read the objects of the specified type and tag in and add them to the /// JILObjectRecord list. This may not be needed by all subclasses and /// so a default empty routine is provided. virtual void GetObjectsFromSource(const type_info *t, string tag, vector *objects_ptr){} /// Delete all objects allocated on last call to GetNamed() void FreeNamedRecords(void){ DeleteObjectRecords(objects); object_stats.clear(); pointer_cache.clear(); } /// This should be called by the subclass at the begining of /// GetNamed() to free any memory from the last named section /// that was read in. If the subclass implements this method, /// it needs to call FreeNamedRecords(). virtual void FreeNamed(void){ FreeNamedRecords(); } /// Delete all objects in the given list. The list is passed in /// so lists that have been "adopted" can be easily freed. void DeleteObjectRecords(vector &objects){ vector::iterator iter; for(iter=objects.begin(); iter!=objects.end(); iter++)delete *iter; objects.clear(); } /// Allow object records (and the objects they point to) /// to be adopted so that the objects are not deleted /// by the JILStream and the stream forgets about them void AdoptObjectRecords(vector &objects) { objects = this->objects; this->objects.clear(); } /// Search the object records list for one with the given pointer. /// if found, return it. Otherwise, return NULL inline JILObjectRecord* FindObjectRecord(vector &objects, void* ptr){ vector::iterator iter; for(iter=objects.begin(); iter!=objects.end(); iter++){ if((*iter)->ptr == ptr)return *iter; } return NULL; } void PrintObjectHisto(vector *objects_ptr=NULL){ if(!objects_ptr)objects_ptr = &objects; map h; vector::iterator iter; for(iter=objects_ptr->begin(); iter!=objects_ptr->end(); iter++){ h[string((*iter)->type_name)]++; } cout<::iterator mi; for(mi=h.begin(); mi!=h.end(); mi++){ cout<second<<" "<first<::iterator iter; std::cout<<"Object Type Num. Total Bytes Bytes/object"<::iterator iter; std::cout<<"Object Type pos ptr"< objects; vector object_stats; vector pointer_cache; /// Used to keep track of objects found in file, even if the /// classes aren't known (i.e. compiled into) the current program void AddToObjectStats(string name, string tag, const std::type_info *type, unsigned int bytes){ if(!keep_object_stats)return; // First, see if an object_stat_t exists for this object vector::iterator iter; for(iter = object_stats.begin(); iter !=object_stats.end(); iter++){ if((*iter).name == name && (*iter).tag == tag && (*iter).type == type){ (*iter).num++; (*iter).total_size += bytes; return; } } object_stat_t object_stat; object_stat.name=name; object_stat.tag=tag; object_stat.type=type; object_stat.num=1; object_stat.total_size=bytes; object_stats.push_back(object_stat); } private: }; #endif // _JILSTREAM_H_