source-engine/datamodel/datamodel.h

528 lines
21 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#ifndef DATAMODEL_H
#define DATAMODEL_H
#ifdef _WIN32
#pragma once
#endif
#include "datamodel/dmattribute.h"
#include "datamodel/idatamodel.h"
#include "datamodel/dmelement.h"
#include "datamodel/dmehandle.h"
#include "tier1/uniqueid.h"
#include "tier1/utlsymbol.h"
#include "tier1/utllinkedlist.h"
#include "tier1/utldict.h"
#include "tier1/utlstring.h"
#include "tier1/utlhandletable.h"
#include "tier1/utlhash.h"
#include "tier2/tier2.h"
#include "clipboardmanager.h"
#include "undomanager.h"
#include "tier1/convar.h"
#include "tier0/vprof.h"
//-----------------------------------------------------------------------------
// forward declarations
//-----------------------------------------------------------------------------
class IDmElementFramework;
class IUndoElement;
class CDmElement;
enum DmHandleReleasePolicy
{
HR_ALWAYS,
HR_NEVER,
HR_IF_NOT_REFERENCED,
};
//-----------------------------------------------------------------------------
// memory categories
//-----------------------------------------------------------------------------
enum
{
MEMORY_CATEGORY_OUTER,
MEMORY_CATEGORY_ELEMENT_INTERNAL,
MEMORY_CATEGORY_DATAMODEL,
MEMORY_CATEGORY_REFERENCES,
MEMORY_CATEGORY_ATTRIBUTE_TREE,
MEMORY_CATEGORY_ATTRIBUTE_OVERHEAD,
MEMORY_CATEGORY_ATTRIBUTE_DATA,
MEMORY_CATEGORY_ATTRIBUTE_COUNT,
MEMORY_CATEGORY_COUNT,
};
//-----------------------------------------------------------------------------
// hash map of id->element, with the id storage optimized out
//-----------------------------------------------------------------------------
class CElementIdHash : public CUtlHash< DmElementHandle_t >
{
public:
CElementIdHash( int nBucketCount = 0, int nGrowCount = 0, int nInitCount = 0 )
: CUtlHash< DmElementHandle_t >( nBucketCount, nGrowCount, nInitCount, CompareFunc, KeyFunc )
{
}
protected:
typedef CUtlHash< DmElementHandle_t > BaseClass;
static bool CompareFunc( DmElementHandle_t const& a, DmElementHandle_t const& b ) { return a == b; }
static bool IdCompareFunc( DmElementHandle_t const& hElement, DmObjectId_t const& id )
{
CDmElement *pElement = g_pDataModel->GetElement( hElement );
Assert( pElement );
if ( !pElement )
return false;
return IsUniqueIdEqual( id, pElement->GetId() );
}
static unsigned int KeyFunc( DmElementHandle_t const& hElement )
{
CDmElement *pElement = g_pDataModel->GetElement( hElement );
Assert( pElement );
if ( !pElement )
return 0;
return *( unsigned int* )&pElement->GetId();
}
static unsigned int IdKeyFunc( DmObjectId_t const &src )
{
return *(unsigned int*)&src;
}
protected:
bool DoFind( DmObjectId_t const &src, unsigned int *pBucket, int *pIndex )
{
// generate the data "key"
unsigned int key = IdKeyFunc( src );
// hash the "key" - get the correct hash table "bucket"
unsigned int ndxBucket;
if( m_bPowerOfTwo )
{
*pBucket = ndxBucket = ( key & m_ModMask );
}
else
{
int bucketCount = m_Buckets.Count();
*pBucket = ndxBucket = key % bucketCount;
}
int ndxKeyData;
CUtlVector< DmElementHandle_t > &bucket = m_Buckets[ndxBucket];
int keyDataCount = bucket.Count();
for( ndxKeyData = 0; ndxKeyData < keyDataCount; ndxKeyData++ )
{
if( IdCompareFunc( bucket.Element( ndxKeyData ), src ) )
break;
}
if( ndxKeyData == keyDataCount )
return false;
*pIndex = ndxKeyData;
return true;
}
public:
UtlHashHandle_t Find( DmElementHandle_t const &src ) { return BaseClass::Find( src ); }
UtlHashHandle_t Find( DmObjectId_t const &src )
{
unsigned int ndxBucket;
int ndxKeyData;
if ( DoFind( src, &ndxBucket, &ndxKeyData ) )
return BuildHandle( ndxBucket, ndxKeyData );
return InvalidHandle();
}
};
//-----------------------------------------------------------------------------
// struct to hold the set of elements in any given file
//-----------------------------------------------------------------------------
struct FileElementSet_t
{
FileElementSet_t( UtlSymId_t filename = UTL_INVAL_SYMBOL, UtlSymId_t format = UTL_INVAL_SYMBOL ) :
m_filename( filename ), m_format( format ),
m_hRoot( DMELEMENT_HANDLE_INVALID ),
m_bLoaded( true ),
m_nElements( 0 )
{
}
FileElementSet_t( const FileElementSet_t& that ) : m_filename( that.m_filename ), m_format( that.m_format ), m_hRoot( DMELEMENT_HANDLE_INVALID ), m_bLoaded( that.m_bLoaded ), m_nElements( that.m_nElements )
{
// the only time this should be copy constructed is when passing in an empty set to the parent array
// otherwise it could get prohibitively expensive time and memory wise
Assert( that.m_nElements == 0 );
}
UtlSymId_t m_filename;
UtlSymId_t m_format;
CDmeCountedHandle m_hRoot;
bool m_bLoaded;
int m_nElements;
};
//-----------------------------------------------------------------------------
// Purpose: Versionable factor for element types
//-----------------------------------------------------------------------------
class CDataModel : public CBaseAppSystem< IDataModel >
{
typedef CBaseAppSystem< IDataModel > BaseClass;
public:
CDataModel();
virtual ~CDataModel();
// External interface
public:
// Methods of IAppSystem
virtual bool Connect( CreateInterfaceFn factory );
virtual void *QueryInterface( const char *pInterfaceName );
virtual InitReturnVal_t Init();
virtual void Shutdown();
// Methods of IDataModel
virtual void AddElementFactory( const char *pClassName, IDmElementFactory *pFactory );
virtual bool HasElementFactory( const char *pElementType ) const;
virtual void SetDefaultElementFactory( IDmElementFactory *pFactory );
virtual int GetFirstFactory() const;
virtual int GetNextFactory( int index ) const;
virtual bool IsValidFactory( int index ) const;
virtual char const *GetFactoryName( int index ) const;
virtual DmElementHandle_t CreateElement( UtlSymId_t typeSymbol, const char *pElementName, DmFileId_t fileid, const DmObjectId_t *pObjectID = NULL );
virtual DmElementHandle_t CreateElement( const char *pTypeName, const char *pElementName, DmFileId_t fileid, const DmObjectId_t *pObjectID = NULL );
virtual void DestroyElement( DmElementHandle_t hElement );
virtual CDmElement* GetElement( DmElementHandle_t hElement ) const;
virtual UtlSymId_t GetElementType( DmElementHandle_t hElement ) const;
virtual const char* GetElementName( DmElementHandle_t hElement ) const;
virtual const DmObjectId_t& GetElementId( DmElementHandle_t hElement ) const;
virtual const char *GetAttributeNameForType( DmAttributeType_t attType ) const;
virtual DmAttributeType_t GetAttributeTypeForName( const char *name ) const;
virtual void AddSerializer( IDmSerializer *pSerializer );
virtual void AddLegacyUpdater( IDmLegacyUpdater *pUpdater );
virtual void AddFormatUpdater( IDmFormatUpdater *pUpdater );
virtual const char* GetFormatExtension( const char *pFormatName );
virtual const char* GetFormatDescription( const char *pFormatName );
virtual int GetFormatCount() const;
virtual const char * GetFormatName( int i ) const;
virtual const char * GetDefaultEncoding( const char *pFormatName );
virtual int GetEncodingCount() const;
virtual const char * GetEncodingName( int i ) const;
virtual bool IsEncodingBinary( const char *pEncodingName ) const;
virtual bool DoesEncodingStoreVersionInFile( const char *pEncodingName ) const;
virtual void SetSerializationDelimiter( CUtlCharConversion *pConv );
virtual void SetSerializationArrayDelimiter( const char *pDelimiter );
virtual bool IsUnserializing();
virtual bool Serialize( CUtlBuffer &outBuf, const char *pEncodingName, const char *pFormatName, DmElementHandle_t hRoot );
virtual bool Unserialize( CUtlBuffer &buf, const char *pEncodingName, const char *pSourceFormatName, const char *pFormatHint,
const char *pFileName, DmConflictResolution_t idConflictResolution, DmElementHandle_t &hRoot );
virtual bool UpdateUnserializedElements( const char *pSourceFormatName, int nSourceFormatVersion,
DmFileId_t fileid, DmConflictResolution_t idConflictResolution, CDmElement **ppRoot );
virtual IDmSerializer* FindSerializer( const char *pEncodingName ) const;
virtual IDmLegacyUpdater* FindLegacyUpdater( const char *pLegacyFormatName ) const;
virtual IDmFormatUpdater* FindFormatUpdater( const char *pFormatName ) const;
virtual bool SaveToFile( char const *pFileName, char const *pPathID, const char *pEncodingName, const char *pFormatName, CDmElement *pRoot );
virtual DmFileId_t RestoreFromFile( char const *pFileName, char const *pPathID, const char *pFormatHint, CDmElement **ppRoot, DmConflictResolution_t idConflictResolution = CR_DELETE_NEW, DmxHeader_t *pHeaderOut = NULL );
virtual void SetKeyValuesElementCallback( IElementForKeyValueCallback *pCallbackInterface );
virtual const char * GetKeyValuesElementName( const char *pszKeyName, int iNestingLevel );
virtual UtlSymId_t GetSymbol( const char *pString );
virtual const char * GetString( UtlSymId_t sym ) const;
virtual int GetElementsAllocatedSoFar();
virtual int GetMaxNumberOfElements();
virtual int GetAllocatedAttributeCount();
virtual int GetAllocatedElementCount();
virtual DmElementHandle_t FirstAllocatedElement();
virtual DmElementHandle_t NextAllocatedElement( DmElementHandle_t hElement );
virtual int EstimateMemoryUsage( DmElementHandle_t hElement, TraversalDepth_t depth = TD_DEEP );
virtual void SetUndoEnabled( bool enable );
virtual bool IsUndoEnabled() const;
virtual bool UndoEnabledForElement( const CDmElement *pElement ) const;
virtual bool IsDirty() const;
virtual bool CanUndo() const;
virtual bool CanRedo() const;
virtual void StartUndo( const char *undodesc, const char *redodesc, int nChainingID = 0 );
virtual void FinishUndo();
virtual void AbortUndoableOperation();
virtual void ClearRedo();
virtual const char *GetUndoDesc();
virtual const char *GetRedoDesc();
virtual void Undo();
virtual void Redo();
virtual void TraceUndo( bool state ); // if true, undo records spew as they are added
virtual void ClearUndo();
virtual void GetUndoInfo( CUtlVector< UndoInfo_t >& list );
virtual const char * GetUndoString( UtlSymId_t sym );
virtual void AddUndoElement( IUndoElement *pElement );
virtual UtlSymId_t GetUndoDescInternal( const char *context );
virtual UtlSymId_t GetRedoDescInternal( const char *context );
virtual void EmptyClipboard();
virtual void SetClipboardData( CUtlVector< KeyValues * >& data, IClipboardCleanup *pfnOptionalCleanuFunction = 0 );
virtual void AddToClipboardData( KeyValues *add );
virtual void GetClipboardData( CUtlVector< KeyValues * >& data );
virtual bool HasClipboardData() const;
virtual CDmAttribute * GetAttribute( DmAttributeHandle_t h );
virtual bool IsAttributeHandleValid( DmAttributeHandle_t h ) const;
virtual void OnlyCreateUntypedElements( bool bEnable );
virtual int NumFileIds();
virtual DmFileId_t GetFileId( int i );
virtual DmFileId_t FindOrCreateFileId( const char *pFilename );
virtual void RemoveFileId( DmFileId_t fileid );
virtual DmFileId_t GetFileId( const char *pFilename );
virtual const char * GetFileName( DmFileId_t fileid );
virtual void SetFileName( DmFileId_t fileid, const char *pFileName );
virtual const char * GetFileFormat( DmFileId_t fileid );
virtual void SetFileFormat( DmFileId_t fileid, const char *pFormat );
virtual DmElementHandle_t GetFileRoot( DmFileId_t fileid );
virtual void SetFileRoot( DmFileId_t fileid, DmElementHandle_t hRoot );
virtual bool IsFileLoaded( DmFileId_t fileid );
virtual void MarkFileLoaded( DmFileId_t fileid );
virtual void UnloadFile( DmFileId_t fileid );
virtual int NumElementsInFile( DmFileId_t fileid );
virtual void DontAutoDelete( DmElementHandle_t hElement );
virtual void MarkHandleInvalid( DmElementHandle_t hElement );
virtual void MarkHandleValid( DmElementHandle_t hElement );
virtual DmElementHandle_t FindElement( const DmObjectId_t &id );
virtual DmAttributeReferenceIterator_t FirstAttributeReferencingElement( DmElementHandle_t hElement );
virtual DmAttributeReferenceIterator_t NextAttributeReferencingElement( DmAttributeReferenceIterator_t hAttrIter );
virtual CDmAttribute * GetAttribute( DmAttributeReferenceIterator_t hAttrIter );
virtual bool InstallNotificationCallback( IDmNotify *pNotify );
virtual void RemoveNotificationCallback( IDmNotify *pNotify );
virtual bool IsSuppressingNotify( ) const;
virtual void SetSuppressingNotify( bool bSuppress );
virtual void PushNotificationScope( const char *pReason, int nNotifySource, int nNotifyFlags );
virtual void PopNotificationScope( bool bAbort );
virtual void SetUndoDepth( int nSize );
virtual void DisplayMemoryStats();
public:
// Internal public methods
int GetCurrentFormatVersion( const char *pFormatName );
// CreateElement references the attribute list passed in via ref, so don't edit or purge ref's attribute list afterwards
CDmElement* CreateElement( const DmElementReference_t &ref, const char *pElementType, const char *pElementName, DmFileId_t fileid, const DmObjectId_t *pObjectID );
void DeleteElement( DmElementHandle_t hElement, DmHandleReleasePolicy hrp = HR_ALWAYS );
// element handle related methods
DmElementHandle_t AcquireElementHandle();
void ReleaseElementHandle( DmElementHandle_t hElement );
// Handles to attributes
DmAttributeHandle_t AcquireAttributeHandle( CDmAttribute *pAttribute );
void ReleaseAttributeHandle( DmAttributeHandle_t hAttribute );
// remove orphaned element subtrees
void FindAndDeleteOrphanedElements();
// Event "mailing list"
DmMailingList_t CreateMailingList();
void DestroyMailingList( DmMailingList_t list );
void AddElementToMailingList( DmMailingList_t list, DmElementHandle_t h );
// Returns false if the mailing list is empty now
bool RemoveElementFromMailingList( DmMailingList_t list, DmElementHandle_t h );
// Returns false if the mailing list is empty now (can happen owing to stale attributes)
bool PostAttributeChanged( DmMailingList_t list, CDmAttribute *pAttribute );
void GetInvalidHandles( CUtlVector< DmElementHandle_t > &handles );
void MarkHandlesValid( CUtlVector< DmElementHandle_t > &handles );
void MarkHandlesInvalid( CUtlVector< DmElementHandle_t > &handles );
// search id->handle table (both loaded and unloaded) for id, and if not found, create a new handle, map it to the id and return it
DmElementHandle_t FindOrCreateElementHandle( const DmObjectId_t &id );
// changes an element's id and associated mappings - generally during unserialization
DmElementHandle_t ChangeElementId( DmElementHandle_t hElement, const DmObjectId_t &oldId, const DmObjectId_t &newId );
DmElementReference_t *FindElementReference( DmElementHandle_t hElement, DmObjectId_t **ppId = NULL );
void RemoveUnreferencedElements();
void RemoveElementFromFile( DmElementHandle_t hElement, DmFileId_t fileid );
void AddElementToFile( DmElementHandle_t hElement, DmFileId_t fileid );
void NotifyState( int nNotifyFlags );
int EstimateMemoryOverhead() const;
bool IsCreatingUntypedElements() const { return m_bOnlyCreateUntypedElements; }
unsigned short GetSymbolCount() const;
private:
struct MailingList_t
{
CUtlVector<DmElementHandle_t> m_Elements;
};
struct ElementIdHandlePair_t
{
DmObjectId_t m_id;
DmElementReference_t m_ref;
ElementIdHandlePair_t() = default;
explicit ElementIdHandlePair_t( const DmObjectId_t &id ) : m_ref()
{
CopyUniqueId( id, &m_id );
}
ElementIdHandlePair_t( const DmObjectId_t &id, const DmElementReference_t &ref ) : m_ref( ref )
{
CopyUniqueId( id, &m_id );
}
ElementIdHandlePair_t( const ElementIdHandlePair_t& that ) : m_ref( that.m_ref )
{
CopyUniqueId( that.m_id, &m_id );
}
ElementIdHandlePair_t &operator=( const ElementIdHandlePair_t &that )
{
CopyUniqueId( that.m_id, &m_id );
m_ref = that.m_ref;
return *this;
}
static unsigned int HashKey( const ElementIdHandlePair_t& that )
{
return *( unsigned int* )&that.m_id.m_Value;
}
static bool Compare( const ElementIdHandlePair_t& a, const ElementIdHandlePair_t& b )
{
return IsUniqueIdEqual( a.m_id, b.m_id );
}
};
private:
CDmElement *Unserialize( CUtlBuffer& buf );
void Serialize( CDmElement *element, CUtlBuffer& buf );
// Read the header, return the version (or false if it's not a DMX file)
bool ReadDMXHeader( CUtlBuffer &inBuf, DmxHeader_t *pHeader ) const;
const char *GetEncodingFromLegacyFormat( const char *pLegacyFormatName ) const;
bool IsValidNonDMXFormat( const char *pFormatName ) const;
bool IsLegacyFormat( const char *pFormatName ) const;
// Returns the current undo manager
CUndoManager* GetUndoMgr();
const CUndoManager* GetUndoMgr() const;
CClipboardManager *GetClipboardMgr();
const CClipboardManager *GetClipboardMgr() const;
void UnloadFile( DmFileId_t fileid, bool bDeleteElements );
friend class CDmeElementRefHelper;
friend class CDmAttribute;
template< class T > friend class CDmArrayAttributeOp;
void OnElementReferenceAdded ( DmElementHandle_t hElement, CDmAttribute *pAttribute );
void OnElementReferenceRemoved( DmElementHandle_t hElement, CDmAttribute *pAttribute );
void OnElementReferenceAdded ( DmElementHandle_t hElement, bool bRefCount );
void OnElementReferenceRemoved( DmElementHandle_t hElement, bool bRefCount );
private:
CUtlVector< IDmSerializer* > m_Serializers;
CUtlVector< IDmLegacyUpdater* > m_LegacyUpdaters;
CUtlVector< IDmFormatUpdater* > m_FormatUpdaters;
IDmElementFactory *m_pDefaultFactory;
CUtlDict< IDmElementFactory*, int > m_Factories;
CUtlSymbolTable m_SymbolTable;
CUtlHandleTable< CDmElement, 20 > m_Handles;
CUtlHandleTable< CDmAttribute, 20 > m_AttributeHandles;
CUndoManager m_UndoMgr;
CUtlLinkedList< MailingList_t, DmMailingList_t > m_MailingLists;
bool m_bIsUnserializing : 1;
bool m_bUnableToSetDefaultFactory : 1;
bool m_bOnlyCreateUntypedElements : 1;
bool m_bUnableToCreateOnlyUntypedElements : 1;
bool m_bDeleteOrphanedElements : 1;
CUtlHandleTable< FileElementSet_t, 20 > m_openFiles;
CElementIdHash m_elementIds;
CUtlHash< ElementIdHandlePair_t > m_unloadedIdElementMap;
CUtlVector< DmObjectId_t > m_unreferencedElementIds;
CUtlVector< DmElementHandle_t > m_unreferencedElementHandles;
CClipboardManager m_ClipboardMgr;
IElementForKeyValueCallback *m_pKeyvaluesCallbackInterface;
int m_nElementsAllocatedSoFar;
int m_nMaxNumberOfElements;
};
//-----------------------------------------------------------------------------
// Singleton
//-----------------------------------------------------------------------------
extern CDataModel *g_pDataModelImp;
//-----------------------------------------------------------------------------
// Inline methods
//-----------------------------------------------------------------------------
inline CUndoManager* CDataModel::GetUndoMgr()
{
return &m_UndoMgr;
}
inline const CUndoManager* CDataModel::GetUndoMgr() const
{
return &m_UndoMgr;
}
inline void CDataModel::NotifyState( int nNotifyFlags )
{
GetUndoMgr()->NotifyState( nNotifyFlags );
}
inline CClipboardManager *CDataModel::GetClipboardMgr()
{
return &m_ClipboardMgr;
}
inline const CClipboardManager *CDataModel::GetClipboardMgr() const
{
return &m_ClipboardMgr;
}
//-----------------------------------------------------------------------------
// Methods of DmElement which are public to datamodel
//-----------------------------------------------------------------------------
class CDmeElementAccessor
{
public:
static void Purge( CDmElement *pElement ) { pElement->Purge(); }
static void SetId( CDmElement *pElement, const DmObjectId_t &id ) { pElement->SetId( id ); }
static bool IsDirty( const CDmElement *pElement ) { return pElement->IsDirty(); }
static void MarkDirty( CDmElement *pElement, bool dirty = true ) { pElement->MarkDirty( dirty ); }
static void MarkAttributesClean( CDmElement *pElement ) { pElement->MarkAttributesClean(); }
static void MarkBeingUnserialized( CDmElement *pElement, bool beingUnserialized = true ) { pElement->MarkBeingUnserialized( beingUnserialized ); }
static bool IsBeingUnserialized( const CDmElement *pElement ) { return pElement->IsBeingUnserialized(); }
static void AddAttributeByPtr( CDmElement *pElement, CDmAttribute *ptr ) { pElement->AddAttributeByPtr( ptr ); }
static void RemoveAttributeByPtrNoDelete( CDmElement *pElement, CDmAttribute *ptr ) { pElement->RemoveAttributeByPtrNoDelete( ptr); }
static void ChangeHandle( CDmElement *pElement, DmElementHandle_t handle ) { pElement->ChangeHandle( handle ); }
static DmElementReference_t *GetReference( CDmElement *pElement ) { return pElement->GetReference(); }
static void SetReference( CDmElement *pElement, const DmElementReference_t &ref ) { pElement->SetReference( ref ); }
static int EstimateMemoryUsage( CDmElement *pElement, CUtlHash< DmElementHandle_t > &visited, TraversalDepth_t depth, int *pCategories ) { return pElement->EstimateMemoryUsage( visited, depth, pCategories ); }
static void PerformConstruction( CDmElement *pElement ) { pElement->PerformConstruction(); }
static void PerformDestruction( CDmElement *pElement ) { pElement->PerformDestruction(); }
};
#endif // DATAMODEL_H