// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #ifndef RY_PD_SERVER_UTILS_H #define RY_PD_SERVER_UTILS_H /* * Includes */ #include #include #include #include #include #include #include #include "pd_utils.h" #include "timestamp.h" /** * CRefIndex, permanent info about the database * This class is not intended to be modified, only create when a valid * reference is ready * To store volatile data, see CDatabaseState. */ class CRefIndex { public: /// Database Id uint32 DatabaseId; /// Numeral index of the reference uint32 Index; /// Directory path of the reference, this MUST local path from root database path std::string Path; /// Timestamp, in the form 'YYYY.MM.DD.hh.mm.ss' //std::string Timestamp; CTimestamp Timestamp; void serial(NLMISC::IStream& s) { s.xmlPush("reference"); s.serialCheck((uint32)'RIDX'); uint version = s.serialVersion(0); s.xmlPush("database"); s.serial(DatabaseId); s.xmlPop(); s.xmlPush("index"); s.serial(Index); s.xmlPop(); s.xmlPush("path"); s.serial(Path); s.xmlPop(); s.xmlPush("timestamp"); if (s.isReading()) { std::string ts; s.serial(ts); Timestamp.fromString(ts.c_str()); } else { std::string ts = Timestamp.toString(); s.serial(ts); } //s.serial(Timestamp); s.xmlPop(); s.xmlPop(); } /// Constructor CRefIndex(uint32 databaseId) : DatabaseId(databaseId), Index(0) { setup(); } /// Setup void setup() { Path = "ref."+NLMISC::toString("%08X", Index); setTimestamp(); } /** * Set Time stamp */ void setTimestamp(); /** * Load a Reference index file */ bool load(const std::string& filename); /** * Load a Reference index file */ bool load(); /** * Save a Reference index file */ bool save(); /** * Save a Reference index file */ bool save(const std::string& filename); /** * Build next Reference index file */ bool buildNext(); /** * Get next Reference index file */ void getNext(); /** * Get (and setup if needed) database root path */ std::string getRootPath(); /** * Get reference path */ std::string getPath(); /** * Set As Valid Reference */ bool setAsValidRef(); /** * Get Nominal Root Path */ std::string getNominalRootPath(); /** * Get Seconds update path */ std::string getSecondsUpdatePath(); /** * Get Minutes update path */ std::string getMinutesUpdatePath(); /** * Get Hours update path */ std::string getHoursUpdatePath(); /** * Get Log path */ std::string getLogPath(); private: /** * Setup reference directory */ bool setupDirectory(); /** * Check directory */ bool checkDirectory(const std::string& path); }; /** * CDatabaseState * This class contains 'volatile' state of the database * This class is update at each update, and written on HD at the end of update. * File on HD is used when database is checked at startup (and if it fails, * database should not be initialised unless maintenance fixes the problem) */ class CDatabaseState { public: /// Constructor CDatabaseState(); /// Database Name std::string Name; /// Database Id uint32 Id; /// Last Update Id uint32 LastUpdateId; /// Current Valid Index uint32 CurrentIndex; /// End Database Update Timestamp CTimestamp EndTimestamp; /// Serial method void serial(NLMISC::IStream& s); /// Save State bool save(CRefIndex& ref); /// Load State bool load(CRefIndex& ref, bool usePrevious = false); /// Load State bool load(const std::string& rootpath, bool usePrevious = false); /// State exists in path static bool exists(const std::string& rootpath); /// Get state filename static std::string fileName() { return "state"; } /// Get state filename static std::string fileName(CRefIndex& ref) { return ref.getRootPath()+"state"; } private: }; class CMixedStreamFile : public NLMISC::IStream { public: /// Constructor CMixedStreamFile() : NLMISC::IStream(false), _File(NULL) { } /// Destructor virtual ~CMixedStreamFile() { close(); } bool open(const char* filename, const char* mode = "rb") { if (_File != NULL) return false; _File = fopen(filename, mode); if (_File == NULL) return false; return true; } virtual void close() { if (_File != NULL) fclose(_File); _File = NULL; } void flush() { if (_File != NULL) fflush(_File); } protected: /// standard FILE handler FILE* _File; bool readBuffer(void* buf, uint len) { if (_File == NULL) return false; uint32 rb = (uint32)fread(buf, 1, len, _File); _ReadBytes += rb; return rb == len; } bool writeBuffer(const void* buf, uint len) { if (_File == NULL) return false; uint32 wb = (uint32)fwrite(buf, 1, len, _File); _WrittenBytes += wb; return wb == len; } bool readBuffer(void* buf, uint len, uint& readlen) { if (_File == NULL) return false; readlen = (uint)fread(buf, 1, len, _File); return readlen == len; } static uint64 _ReadBytes; static uint64 _WrittenBytes; template bool readValue(T& v) { return readBuffer(&v, sizeof(v)); } template bool writeValue(const T& v) { return writeBuffer(&v, sizeof(v)); } public: virtual void serialBuffer(uint8 *buf, uint len) { if (_File == NULL) throw NLMISC::EStream("CMixedStreamFile not opened"); if (isReading()) { if (!readBuffer(buf, len)) throw NLMISC::EStream("file error "+NLMISC::toString(ferror(_File))); } else { if (!writeBuffer(buf, len)) throw NLMISC::EStream("file error "+NLMISC::toString(ferror(_File))); } } virtual void serialBit(bool &bit) { if (isReading()) { uint8 b; serial(b); bit = (b != 0); } else { uint8 b = bit; serial(b); } } }; class CRowMapper { public: /// Constructor CRowMapper(); /// Clear up mapper void clear(); /// Set link void link(CRowMapper* link) { _Link = link; } /// Key typedef uint64 TKey; /** * Declares a row as allocated */ bool allocate(RY_PDS::TRowIndex row); /** * Declares a row as deallocated */ bool deallocate(RY_PDS::TRowIndex row); /** * Checks if a row is allocated */ bool allocated(RY_PDS::TRowIndex row) const; /** * Get Number of Allocated rows */ uint32 numAllocated() const { return _AllocatedRows; } /** * Get Max row index used */ RY_PDS::TRowIndex maxRowIndex() const { return _RowState.size(); } /** * Get Next Unalloocated row */ RY_PDS::TRowIndex nextUnallocatedRow() const; /** * Map a key to an Row */ bool map(TKey key, const RY_PDS::CObjectIndex& index); /** * Is Row Mapped */ bool isMapped(TKey key); /** * Get Row from key */ RY_PDS::CObjectIndex get(TKey key) const; /** * Unmap a key of an Row */ bool unmap(TKey key); /** * Get Number of Mapped rows */ uint32 numMapped() const { return (uint32)_KeyMap.size(); } private: /** * Bitset of allocated rows, each bit indicates if corresponding row is allocated */ typedef NLMISC::CBitSet TRowState; TRowState _RowState; /// Total number of allocated rows uint32 _AllocatedRows; /// Key Hash class CKeyHash { public: size_t operator() (const TKey& key) const { return ((uint32)key) ^ ((uint32)(key >> 32)); } }; typedef CHashMap TKeyMap; TKeyMap _KeyMap; /// Row Mapper link, in case of mapped table that inherit from another CRowMapper* _Link; }; /** * Debug/Log Interface */ class CPDSLogger { public: CPDSLogger() : _ParentLogger(NULL) { } void setParentLogger(const CPDSLogger* parent) { _ParentLogger = parent; } /** * Get Contextual Identifier String */ std::string getContextualIndentifier() const; protected: /** * Get Internal Logger Identifier * An identifier should ressemble like 'type:id', where type is the 'class' * of the logger, and id is a unique identifier of the instanciated object of 'class' */ virtual std::string getLoggerIdentifier() const = 0; private: /// Parent Logger const CPDSLogger* _ParentLogger; }; #define PDS_FULL_DEBUG_LEVEL 3 #define PDS_NORMAL_DEBUG_LEVEL 2 #define PDS_MINIMAL_DEBUG_LEVEL 1 #define PDS_NO_DEBUG_LEVEL 0 #define PDS_DEBUG_LEVEL PDS_FULL_DEBUG_LEVEL #define PDS_DEBUG_IN_L(obj, level) if (level <= 0 || !RY_PDS::PDVerbose || level > RY_PDS::PDVerboseLevel) {} else NLMISC::createDebug (), NLMISC::DebugLog->setPosition( __LINE__, __FILE__ ), NLMISC::DebugLog->display("%s:", obj->getContextualIndentifier().c_str()), NLMISC::DebugLog->displayNL #define PDS_DEBUG_IN(obj) PDS_DEBUG_IN_L(obj, PDS_DEBUG_LEVEL) #define PDS_INFO_IN(obj) NLMISC::createDebug (), NLMISC::InfoLog->setPosition( __LINE__, __FILE__ ), NLMISC::InfoLog->display("%s:", obj->getContextualIndentifier().c_str()), NLMISC::InfoLog->displayNL #define PDS_WARNING_IN(obj) NLMISC::createDebug (), NLMISC::WarningLog->setPosition( __LINE__, __FILE__ ), NLMISC::WarningLog->display("%s:", obj->getContextualIndentifier().c_str()), NLMISC::WarningLog->displayNL #define PDS_DEBUG_L(level) PDS_DEBUG_IN_L(this, level) #define PDS_FULL_DEBUG PDS_DEBUG_L(PDS_FULL_DEBUG_LEVEL) #define PDS_DEBUG PDS_DEBUG_L(PDS_DEBUG_LEVEL) #define PDS_INFO PDS_INFO_IN(this) #define PDS_WARNING PDS_WARNING_IN(this) #define PDS_LOG_DEBUG(level) if (level <= 0 || !RY_PDS::PDVerbose || level > RY_PDS::PDVerboseLevel) {} else nldebug /* * */ /* * Inlines */ /* * Get Contextual Identifier String */ inline std::string CPDSLogger::getContextualIndentifier() const { if (_ParentLogger != NULL) return _ParentLogger->getContextualIndentifier()+"|"+getLoggerIdentifier(); else return getLoggerIdentifier(); } /* * Constructor */ inline CRowMapper::CRowMapper() { clear(); } /* * Clear up mapper */ inline void CRowMapper::clear() { _Link = NULL; _AllocatedRows = 0; _RowState.clearAll(); _KeyMap.clear(); } /* * Declares a row as allocated */ inline bool CRowMapper::allocate(RY_PDS::TRowIndex row) { // check for room if (row >= _RowState.size()) _RowState.resizeNoReset(row+1); // row already allocated? if (_RowState.get(row)) return false; _RowState.set(row); ++_AllocatedRows; return true; } /* * Declares a row as deallocated */ inline bool CRowMapper::deallocate(RY_PDS::TRowIndex row) { // check bit not outside bitset if (row >= _RowState.size()) return false; // row allocated? if (!_RowState.get(row)) return false; _RowState.clear(row); --_AllocatedRows; return true; } /* * Checks if a row is allocated */ inline bool CRowMapper::allocated(RY_PDS::TRowIndex row) const { return row < _RowState.size() && _RowState.get(row); } /* * Get Next Unalloocated row */ inline RY_PDS::TRowIndex CRowMapper::nextUnallocatedRow() const { RY_PDS::TRowIndex row; // go through all rows till found a free row for (row=0; allocated(row); ++row) ; return row; } /* * Map a key to an Row */ inline bool CRowMapper::map(TKey key, const RY_PDS::CObjectIndex& index) { if (_Link != NULL) { return _Link->map(key, index); } if (_KeyMap.find(key) != _KeyMap.end()) { nlwarning("CRowMapper::map(): cannot map '%016"NL_I64"X' to '%d', already mapped", key, index.toString().c_str()); return false; } _KeyMap[key] = index; return true; } /* * Is Row Mapped */ inline bool CRowMapper::isMapped(TKey key) { if (_Link != NULL) { return _Link->isMapped(key); } return (_KeyMap.find(key) != _KeyMap.end()); } /* * Get Row from key */ inline RY_PDS::CObjectIndex CRowMapper::get(TKey key) const { if (_Link != NULL) { return _Link->get(key); } TKeyMap::const_iterator it = _KeyMap.find(key); if (it == _KeyMap.end()) { PDS_LOG_DEBUG(1)("CRowMapper::get(): key '%016"NL_I64"X' not mapped", key); return RY_PDS::CObjectIndex::null(); } return (*it).second; } /* * Unmap a key of an Row */ inline bool CRowMapper::unmap(TKey key) { if (_Link != NULL) { return _Link->unmap(key); } TKeyMap::iterator it = _KeyMap.find(key); if (it == _KeyMap.end()) { nlwarning("CRowMapper::unmap(): key '%016"NL_I64"X' not mapped", key); return false; } _KeyMap.erase(it); return true; } #endif //RY_PDS_LIB_H