// 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 NL_PDS_TABLE_BUFFER_H
#define NL_PDS_TABLE_BUFFER_H
#include
#include
#include "../pd_lib/pd_utils.h"
#include "../pd_lib/pd_server_utils.h"
#include "../pd_lib/db_reference_file.h"
#include "../pd_lib/db_delta_file.h"
class IRowProcessor;
/**
* A memory cache for table
* \author Benjamin Legros
* \author Nevrax France
* \date 2003
*/
class CTableBuffer : public CPDSLogger
{
public:
/// Constructor
CTableBuffer();
/// Destructor
~CTableBuffer();
/// Clear all
void clear();
/// Init
void init(uint32 tableId, uint32 rowSize, bool mapped);
/// Setup Ref
void setupRef(CRefIndex& ref);
/// Init, only for reference builder
void init(uint32 tableId, const std::string& refRootPath, const std::string& refPath); //CRefIndex& ref);
/// Row Data Type
typedef uint8* TRowData;
/// Map of Rows
typedef CHashMap TRowMap;
/**
* Row Accessor, embeds any row access
*/
class CAccessor
{
public:
/// Get row index
RY_PDS::TRowIndex row() const { return (*_MapIt).first; }
/// Get row data
TRowData data() { return (*_MapIt).second + (_Mapped ? sizeof(CMappedHeader) : sizeof(CHeader)); }
/// Get row data
TRowData data() const { return (*_MapIt).second + (_Mapped ? sizeof(CMappedHeader) : sizeof(CHeader)); }
/// Default Constructor, should never be used
explicit CAccessor() { }
/// Is Row Allocated
bool allocated() const { return ((CHeader*)fullRow())->allocated(); }
/// Is Row Mapped
bool mapped() const { return _Mapped && ((CMappedHeader*)fullRow())->mapped(); }
/// Is Row Dirty
bool dirty() const { return ((CHeader*)fullRow())->dirty(); }
/// Get Dirt stamp
uint32 dirtyStamp() const { return ((CHeader*)fullRow())->getDirtStamp(); }
/// Get Map Key
uint64 key() const { return mapped() ? ((CMappedHeader*)fullRow())->getKey() : 0; }
/// Get Full Row Data
TRowData fullRow() const { return (*_MapIt).second; }
/// Equals
bool operator == (const CAccessor& access) const { return _Mapped == access._Mapped && _MapIt == access._MapIt; }
/**
* Copy header info from another accessor
* Warning, this is only a local copy of header, data won't been updated in parent CTableBuffer
*/
void copyHeader(const CAccessor& access);
private:
friend class CTableBuffer;
/// Accessor in table map
typedef TRowMap::iterator TMapIt;
TMapIt _MapIt;
bool _Mapped;
/// Constructor for CTableBuffer
CAccessor(TMapIt it, bool mapped) : _MapIt(it), _Mapped(mapped) { }
/// Get Full Row Data
TRowData fullRow() { return (*_MapIt).second; }
};
/**
* Return a pointer to straight row data
* If row is not loaded, data are loaded from table file
*/
CAccessor getRow(RY_PDS::TRowIndex row);
/**
* Return a pointer to straight new row data
* Row is filled with blank, not loaded from file
*/
CAccessor getEmptyRow(RY_PDS::TRowIndex row);
/**
* Acquire a Row
* Row is marked as non releasable until it is released enough time
*/
bool acquireRow(CAccessor accessor);
/**
* Release a Row
* Row is marked as purgeable, and will be purged as soon as possible,
* unless it is reactivated before purge
*/
bool releaseRow(CAccessor accessor);
/**
* Release a Row
* Row is marked as purgeable, and will be purged as soon as possible,
* unless it is reactivated before purge
*/
bool releaseRow(RY_PDS::TRowIndex row);
/**
* Release all rows
* To be called when client disconnects, so all loaded rows may be released without
* client releasing them manually
*/
bool releaseAll();
/**
* Mark a Row as being dirty for later delta save
*/
bool dirtyRow(CAccessor accessor);
/**
* Build Delta
*/
bool buildDelta(const CTimestamp& starttimestamp, const CTimestamp& endtimestamp);
/**
* Apply delta changes from a file
*/
bool applyDeltaChanges(const std::string& filename);
/**
* Flush Released Rows from memory
*/
void flushReleased();
/**
* Reset dirty tags
*/
void resetDirty();
/**
* Flush Reference files
*/
void flushRefFiles();
/**
* Purge all references
* Flush loaded reference files, and close them all.
*/
bool purgeReferences();
/**
* Open all reference files in reference directory to update
*/
bool openAllRefFilesWrite();
/**
* Open all reference files in reference directory to read
*/
bool openAllRefFilesRead();
/**
* Update delta ids for all references
*/
bool updateDeltaIds(uint32 start, uint32 end);
/**
* Allocate a row
* \param row is the row index to allocate
* Return true if succeded
*/
bool allocate(RY_PDS::TRowIndex row, CAccessor& accessor);
/**
* Deallocate a row
* \param row is the row index to deallocate
* Return true if succeded
*/
bool deallocate(RY_PDS::TRowIndex row);
/**
* Tells if a row is allocated
* \param row is the row to check
*/
bool isAllocated(RY_PDS::TRowIndex row) const { return _RowMapper.allocated(row); }
/**
* Get number of actually allocated rows
*/
uint32 numAllocated() const { return _RowMapper.numAllocated(); }
/**
* Get Max row index used
*/
RY_PDS::TRowIndex maxRowIndex() const { return _RowMapper.maxRowIndex(); }
/**
* Get Next Unalloocated row
*/
RY_PDS::TRowIndex nextUnallocatedRow() const { return _RowMapper.nextUnallocatedRow(); }
/**
* Map a row
* \param index is the index to map
* \param key is the 64 bits row key
* Return true if succeded
*/
bool mapRow(const RY_PDS::CObjectIndex &index, uint64 key);
/**
* Unmap a row in a table
* \param key is the 64 bits row key
* Return true if succeded
*/
bool unmapRow(const RY_PDS::CObjectIndex &index, uint64 key);
/**
* Get a mapped row
* \param key is the 64 bits row key
* Return a valid CObjectIndex if success
*/
RY_PDS::CObjectIndex getMappedRow(uint64 key) const;
/**
* Get number of actually allocated rows
*/
uint32 numMapped() const { return _RowMapper.numMapped(); }
/**
* Get the number of actually loaded rows
*/
uint32 getLoadedRows() const { return (uint32)_RowMap.size(); }
/**
* Get memory load
*/
uint32 getMemoryLoad() const { return (uint32)_RowMap.size()*_InternalRowSize; }
/**
* Build RowMapper
*/
bool buildRowMapper();
/**
* Process Rows, apply external row processing after rows are loaded
*/
bool processRows(IRowProcessor* processor);
/**
* Link RowMapper
*/
void linkRowMapper(CTableBuffer* link) { _RowMapper.link(&(link->_RowMapper)); }
/**
* Get Size of Row Header (depends on whether table is mapped or not)
*/
uint getHeaderSize() const { return _Mapped ? sizeof(CMappedHeader) : sizeof(CHeader); }
/**
* Get Internal Row Size
*/
uint getInternalRowSize() const { return _InternalRowSize; }
/**
* Get User Row Size
*/
uint getRowSize() const { return _InternalRowSize; }
/**
* Update a Row
* Update in reference file the whole row
*/
bool updateRow(CAccessor accessor);
/**
* Get current Timestamp
*/
static uint32 getCommonStamp() { return _CommonStamp; }
/**
* Update common Timestamp
*/
static void updateCommonStamp();
/// \name Debug
// @{
/**
* Setup debug delta file
*/
bool setupDebugDeltaFile(const std::string& filename, CDBDeltaFile& delta) const;
/**
* Get Delta file Row
*/
uint8* getDeltaRow(uint32& row, CDBDeltaFile& delta) const;
// @}
protected:
virtual std::string getLoggerIdentifier() const { return "buffer"; }
private:
/// Initialised
bool _Init;
/// Table Id
uint32 _TableId;
/// Row Size
uint32 _RowSize;
/// Internal Row Size
uint32 _InternalRowSize;
/// Ref Root Path
std::string _RefRootPath;
/// Ref Path
std::string _RefPath;
/// Rows per ref file
uint32 _RowsPerFile;
/// Maximum ref file size
static uint32 _MaxRefFileSize;
/// Row Map
TRowMap _RowMap;
/// Map of purgeable rows
typedef std::set TReleaseSet;
/// Release Map
TReleaseSet _ReleaseSet;
/// List of dirty rows
typedef std::vector TDirtyList;
/// Dirty List
TDirtyList _DirtyList;
/// List of ref files -- a vector or pointer, to avoid realloc issues
typedef std::vector TRefFileMap;
/// Reference files
TRefFileMap _RefFileMap;
/// Index and Allocation Map
CRowMapper _RowMapper;
/// Rows are mapped?
bool _Mapped;
/// Current Delta Id
uint32 _CurrentDeltaId;
/// Current reference stamp
uint32 _ReferenceStamp;
/// Common stamp
static uint32 _CommonStamp;
/// Load a row from the appropriate reference file
bool loadRow(RY_PDS::TRowIndex row, TRowData rowData);
/// Init Number of Rows per File
bool initRowsPerFile();
/// Check reference file is ready
bool checkRef(uint32 refFile);
/// Acquire a row, internal version
bool acquireRow(TRowMap::iterator it);
/// Release a row, internal version
bool releaseRow(TRowMap::iterator it, bool forceNotAcquired = false);
/// Update a Row -- rowdata is _InternalByteSize long, this is a full buffer row with header
bool updateRow(RY_PDS::TRowIndex row, const TRowData rowData, bool forceWriteToDisk);
/// Get Reference Files list
void getReferenceFilesList(std::vector &result);
/// Process row for RowMapper
bool processRow(CAccessor& accessor);
/// Header of a row
class CHeader
{
public:
enum
{
Dirty = 1,
Allocated = 2,
};
/// Clear
void clear() { _Flags = 0; _Acquire = 0; _DirtStamp = 0; }
/// GetFlags
uint8 getFlags() const { return _Flags; }
/// Is Row Dirty
bool dirty() const { return checkFlags(Dirty); }
/// Is Row Allocated
bool allocated() const { return checkFlags(Allocated); }
/// Set flags
void setFlags(uint8 flags) { _Flags |= flags; }
/// Set flags
void clearFlags(uint8 flags) { _Flags &= (~flags); }
/// Check Flags
bool checkFlags(uint8 testflags) const { return (_Flags & testflags) != 0; }
/// Set Row As Dirty
void setDirty();
/// Clear DirtStamp (set initial dirt stamp as 'no dirt stamp' value)
void clearDirtStamp() { _DirtStamp = 0; clearFlags(Dirty); }
/// Get DirtStamp
uint32 getDirtStamp() const { return _DirtStamp; }
/// Acquire, return true the first time it is acquired
bool acquire()
{
++_Acquire;
return _Acquire == 1;
}
/// Release, return true if row can be release effectively
bool unacquire()
{
--_Acquire;
if (_Acquire < 0)
_Acquire = 0;
return !isAcquired();
}
/// Clear Acquire count
void clearAcquireCount() { _Acquire = 0; }
/// Is acquired
bool isAcquired() const { return _Acquire > 0; }
protected:
/// Row flags
uint8 _Flags;
/// Acquire counter
sint16 _Acquire;
/// Row Dirty Timestamp
uint32 _DirtStamp;
};
/// Header of a mapped row
class CMappedHeader : public CHeader
{
public:
/// Clear up header
void clear() { CHeader::clear(); _Key = 0; }
/// Set Row Key
void setKey(uint64 key) { _Key = key; }
/// Get Row Key
uint64 getKey() const { return _Key; }
/// Is Row Mapped?
bool mapped() const { return _Key != 0; }
protected:
/**
* Row map key
* The key of the row in table map, 0 means this row is not mapped
*/
uint64 _Key;
};
friend class CAccessor;
friend class CDatabaseAdapter;
};
/**
* A callback interface for row processing at loading
* \author Benjamin Legros
* \author Nevrax France
* \date 2003
*/
class IRowProcessor
{
public:
virtual bool processRow(RY_PDS::TTableIndex table, CTableBuffer::CAccessor& accessor) = 0;
};
#include "pds_table_buffer_inline.h"
#endif // NL_PDS_TABLE_BUFFER_H
/* End of pds_table_buffer.h */