summaryrefslogtreecommitdiff
path: root/datamodel/dmelementdictionary.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /datamodel/dmelementdictionary.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'datamodel/dmelementdictionary.cpp')
-rw-r--r--datamodel/dmelementdictionary.cpp468
1 files changed, 468 insertions, 0 deletions
diff --git a/datamodel/dmelementdictionary.cpp b/datamodel/dmelementdictionary.cpp
new file mode 100644
index 0000000..5f11370
--- /dev/null
+++ b/datamodel/dmelementdictionary.cpp
@@ -0,0 +1,468 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "dmelementdictionary.h"
+#include "datamodel/dmelement.h"
+#include "datamodel/dmattribute.h"
+#include "datamodel/dmattributevar.h"
+#include "datamodel/idatamodel.h"
+#include "datamodel.h"
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CDmElementDictionary::CDmElementDictionary()
+ : m_idmap( 1024, 0, 0, DmIdPair_t::Compare, DmIdPair_t::HashKey )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Clears the dictionary
+//-----------------------------------------------------------------------------
+void CDmElementDictionary::Clear()
+{
+ m_Dict.Purge();
+ m_Attributes.Purge();
+ m_ArrayAttributes.Purge();
+ m_elementsToDelete.Purge();
+}
+
+
+//-----------------------------------------------------------------------------
+// Inserts an element into the table
+//-----------------------------------------------------------------------------
+DmElementDictHandle_t CDmElementDictionary::InsertElement( CDmElement *pElement )
+{
+ // Insert it into the reconnection table
+ return m_Dict.AddToTail( pElement ? pElement->GetHandle() : DMELEMENT_HANDLE_INVALID );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns a particular element
+//-----------------------------------------------------------------------------
+CDmElement *CDmElementDictionary::GetElement( DmElementDictHandle_t handle )
+{
+ if ( handle == ELEMENT_DICT_HANDLE_INVALID )
+ return NULL;
+
+ return g_pDataModel->GetElement( m_Dict[ handle ] );
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds an attribute to the fixup list
+//-----------------------------------------------------------------------------
+void CDmElementDictionary::AddAttribute( CDmAttribute *pAttribute, const DmObjectId_t &objectId )
+{
+ if ( m_elementsToDelete.Find( pAttribute->GetOwner()->GetHandle() ) != m_elementsToDelete.InvalidIndex() )
+ return; // don't add attributes if their element is being deleted
+
+ int i = m_Attributes.AddToTail();
+ m_Attributes[i].m_nType = AT_OBJECTID;
+ m_Attributes[i].m_pAttribute = pAttribute;
+ CopyUniqueId( objectId, &m_Attributes[i].m_ObjectId );
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds an element of an attribute array to the fixup list
+//-----------------------------------------------------------------------------
+void CDmElementDictionary::AddArrayAttribute( CDmAttribute *pAttribute, DmElementDictHandle_t hElement )
+{
+ if ( m_elementsToDelete.Find( pAttribute->GetOwner()->GetHandle() ) != m_elementsToDelete.InvalidIndex() )
+ return; // don't add attributes if their element is being deleted
+
+ int i = m_ArrayAttributes.AddToTail();
+ m_ArrayAttributes[i].m_nType = AT_ELEMENT;
+ m_ArrayAttributes[i].m_pAttribute = pAttribute;
+ m_ArrayAttributes[i].m_hElement = hElement;
+}
+
+void CDmElementDictionary::AddArrayAttribute( CDmAttribute *pAttribute, const DmObjectId_t &objectId )
+{
+ if ( m_elementsToDelete.Find( pAttribute->GetOwner()->GetHandle() ) != m_elementsToDelete.InvalidIndex() )
+ return; // don't add attributes if their element is being deleted
+
+ int i = m_ArrayAttributes.AddToTail();
+ m_ArrayAttributes[i].m_nType = AT_OBJECTID;
+ m_ArrayAttributes[i].m_pAttribute = pAttribute;
+ CopyUniqueId( objectId, &m_ArrayAttributes[i].m_ObjectId );
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmElementDictionary::RemoveAttributeInfosOfElement( AttributeList_t &attributes, DmElementHandle_t hElement )
+{
+ while ( attributes.Count() > 0 && attributes.Tail().m_pAttribute->GetOwner()->GetHandle() == hElement )
+ {
+ attributes.Remove( attributes.Count() - 1 );
+ }
+}
+
+DmElementHandle_t CDmElementDictionary::SetElementId( DmElementDictHandle_t hDictHandle, const DmObjectId_t &newId, DmConflictResolution_t idConflictResolution )
+{
+ DmElementHandle_t hElement = m_Dict[ hDictHandle ];
+ CDmElement *pElement = g_pDataModel->GetElement( hElement );
+ Assert( pElement );
+ if ( !pElement )
+ return DMELEMENT_HANDLE_INVALID;
+
+ const DmObjectId_t &oldId = pElement->GetId();
+
+ if ( idConflictResolution == CR_FORCE_COPY )
+ {
+ m_idmap.Insert( DmIdPair_t( newId, oldId ) ); // map the newId back to the old id, and keep the old id
+ return hElement;
+ }
+
+ DmElementHandle_t newHandle = g_pDataModelImp->ChangeElementId( hElement, oldId, newId );
+ if ( newHandle != DMELEMENT_HANDLE_INVALID )
+ {
+ // if ChangeElementId returns a handle, the id has been changed
+ if ( newHandle != hElement )
+ {
+ int i = m_Dict.Find( hElement );
+ if ( i != m_Dict.InvalidIndex() )
+ {
+ m_Dict[ i ] = newHandle;
+ }
+ }
+ return newHandle; // either keeping the old handle, with the new id, or found a new handle associated with that new id
+ }
+
+ // id not changed because that id is already in use
+ if ( idConflictResolution == CR_DELETE_NEW )
+ {
+ DmElementHandle_t hExistingElement = g_pDataModel->FindElement( newId );
+
+ int i = m_elementsToDelete.AddToTail( );
+ m_elementsToDelete[i].m_hDictHandle = hDictHandle;
+ m_elementsToDelete[i].m_hElementToDelete = hElement;
+ m_elementsToDelete[i].m_hReplacementElement = hExistingElement;
+
+ // remove all element ref attributes read in before the id (typically none)
+ RemoveAttributeInfosOfElement( m_Attributes, hElement );
+ RemoveAttributeInfosOfElement( m_ArrayAttributes, hElement );
+
+ return DMELEMENT_HANDLE_INVALID;
+ }
+
+ if ( idConflictResolution == CR_DELETE_OLD )
+ {
+ DmElementHandle_t hExistingElement = g_pDataModel->FindElement( newId );
+ Assert( hExistingElement != DMELEMENT_HANDLE_INVALID );
+ if ( hExistingElement == DMELEMENT_HANDLE_INVALID )
+ return DMELEMENT_HANDLE_INVALID; // unexpected error in ChangeElementId (failed due to something other than a conflict)
+
+ g_pDataModelImp->DeleteElement( hExistingElement, HR_NEVER ); // need to keep the handle around until ChangeElemendId
+ newHandle = g_pDataModelImp->ChangeElementId( hElement, oldId, newId );
+ Assert( newHandle == hExistingElement );
+
+ int i = m_Dict.Find( hElement );
+ if ( i != m_Dict.InvalidIndex() )
+ {
+ m_Dict[ i ] = newHandle;
+ }
+
+ return newHandle;
+ }
+
+ if ( idConflictResolution == CR_COPY_NEW )
+ {
+ m_idmap.Insert( DmIdPair_t( newId, oldId ) ); // map the newId back to the old id, and keep the old id
+ return hElement;
+ }
+
+ Assert( 0 );
+ return DMELEMENT_HANDLE_INVALID;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds an element into the table
+//-----------------------------------------------------------------------------
+DmElementDictHandle_t CDmElementDictionary::FindElement( CDmElement *pElement )
+{
+ return m_Dict.Find( pElement ? pElement->GetHandle() : DMELEMENT_HANDLE_INVALID );
+}
+
+
+//-----------------------------------------------------------------------------
+// Hook up all element references (which were unserialized as object ids)
+//-----------------------------------------------------------------------------
+void CDmElementDictionary::HookUpElementAttributes()
+{
+ int n = m_Attributes.Count();
+ for ( int i = 0; i < n; ++i )
+ {
+ Assert( m_Attributes[i].m_pAttribute->GetType() == AT_ELEMENT );
+ Assert( m_Attributes[i].m_nType == AT_OBJECTID );
+
+ UtlHashHandle_t h = m_idmap.Find( DmIdPair_t( m_Attributes[i].m_ObjectId ) );
+ DmObjectId_t &id = h == m_idmap.InvalidHandle() ? m_Attributes[i].m_ObjectId : m_idmap[ h ].m_newId;
+
+ // 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 hElement = g_pDataModelImp->FindOrCreateElementHandle( id );
+ m_Attributes[i].m_pAttribute->SetValue<DmElementHandle_t>( hElement );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Hook up all element array references
+//-----------------------------------------------------------------------------
+void CDmElementDictionary::HookUpElementArrayAttributes()
+{
+ // Find unique array attributes; we need to clear them all before adding stuff.
+ // This clears them of stuff added during their construction phase.
+ int n = m_ArrayAttributes.Count();
+ CUtlRBTree< CDmAttribute*, unsigned short > lookup( 0, n, DefLessFunc(CDmAttribute*) );
+ for ( int i = 0; i < n; ++i )
+ {
+ Assert( m_ArrayAttributes[i].m_pAttribute->GetType() == AT_ELEMENT_ARRAY );
+ CDmAttribute *pElementArray = m_ArrayAttributes[i].m_pAttribute;
+ CDmrElementArray<> array( pElementArray );
+ if ( lookup.Find( pElementArray ) == lookup.InvalidIndex() )
+ {
+ array.RemoveAll();
+ lookup.Insert( pElementArray );
+ }
+ }
+
+ for ( int i = 0; i < n; ++i )
+ {
+ Assert( m_ArrayAttributes[i].m_pAttribute->GetType() == AT_ELEMENT_ARRAY );
+
+ CDmrElementArray<> array( m_ArrayAttributes[i].m_pAttribute );
+
+ if ( m_ArrayAttributes[i].m_nType == AT_ELEMENT )
+ {
+ CDmElement *pElement = GetElement( m_ArrayAttributes[i].m_hElement );
+ array.AddToTail( pElement );
+ }
+ else
+ {
+ UtlHashHandle_t h = m_idmap.Find( DmIdPair_t( m_ArrayAttributes[i].m_ObjectId ) );
+ DmObjectId_t &id = ( h == m_idmap.InvalidHandle() ) ? m_ArrayAttributes[i].m_ObjectId : m_idmap[ h ].m_newId;
+
+ // 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 hElement = g_pDataModelImp->FindOrCreateElementHandle( id );
+ int nIndex = array.AddToTail();
+ array.SetHandle( nIndex, hElement );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Hook up all element references (which were unserialized as object ids)
+//-----------------------------------------------------------------------------
+void CDmElementDictionary::HookUpElementReferences()
+{
+ int nElementsToDelete = m_elementsToDelete.Count();
+ for ( int i = 0; i < nElementsToDelete; ++i )
+ {
+ DmElementDictHandle_t hDictIndex = m_elementsToDelete[i].m_hDictHandle;
+ DmElementHandle_t hElement = m_Dict[ hDictIndex ];
+ g_pDataModelImp->DeleteElement( hElement );
+ m_Dict[ hDictIndex ] = m_elementsToDelete[i].m_hReplacementElement;
+ }
+
+ HookUpElementArrayAttributes();
+ HookUpElementAttributes();
+}
+
+
+
+//-----------------------------------------------------------------------------
+//
+// Element dictionary used in serialization
+//
+//-----------------------------------------------------------------------------
+CDmElementSerializationDictionary::CDmElementSerializationDictionary() :
+ m_Dict( 1024, 0, CDmElementSerializationDictionary::LessFunc )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Used to sort the list of elements
+//-----------------------------------------------------------------------------
+bool CDmElementSerializationDictionary::LessFunc( const ElementInfo_t &lhs, const ElementInfo_t &rhs )
+{
+ return lhs.m_pElement < rhs.m_pElement;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds the handle of the element
+//-----------------------------------------------------------------------------
+DmElementDictHandle_t CDmElementSerializationDictionary::Find( CDmElement *pElement )
+{
+ ElementInfo_t find;
+ find.m_pElement = pElement;
+ return m_Dict.Find( find );
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates the list of all things to serialize
+//-----------------------------------------------------------------------------
+void CDmElementSerializationDictionary::BuildElementList_R( CDmElement *pElement, bool bFlatMode, bool bIsRoot )
+{
+ if ( !pElement )
+ return;
+
+ // FIXME: Right here we should ask the element if it's an external
+ // file reference and exit immediately if so.
+
+ // This means we've already encountered this guy.
+ // Therefore, he can never be a root element
+ DmElementDictHandle_t h = Find( pElement );
+ if ( h != m_Dict.InvalidIndex() )
+ {
+ m_Dict[h].m_bRoot = true;
+ return;
+ }
+
+ ElementInfo_t info;
+ info.m_bRoot = bFlatMode || bIsRoot;
+ info.m_pElement = pElement;
+ m_Dict.Insert( info );
+
+ for ( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ if ( pAttribute->IsFlagSet( FATTRIB_DONTSAVE ) )
+ continue;
+
+ switch( pAttribute->GetType() )
+ {
+ case AT_ELEMENT:
+ {
+ CDmElement *pChild = pAttribute->GetValueElement<CDmElement>();
+ if ( !pChild || pChild->GetFileId() != pElement->GetFileId() )
+ break;
+
+ BuildElementList_R( pChild, bFlatMode, false );
+ }
+ break;
+
+ case AT_ELEMENT_ARRAY:
+ {
+ CDmrElementArray<> array( pAttribute );
+ int nCount = array.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pChild = array[i];
+ if ( !pChild || pChild->GetFileId() != pElement->GetFileId() )
+ break;
+
+ BuildElementList_R( pChild, bFlatMode, false );
+ }
+ }
+ break;
+ }
+ }
+}
+
+void CDmElementSerializationDictionary::BuildElementList( CDmElement *pElement, bool bFlatMode )
+{
+ BuildElementList_R( pElement, bFlatMode, true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Should I inline the serialization of this element?
+//-----------------------------------------------------------------------------
+bool CDmElementSerializationDictionary::ShouldInlineElement( CDmElement *pElement )
+{
+ // This means we've already encountered this guy.
+ // Therefore, he can never be a root element
+ DmElementDictHandle_t h = Find( pElement );
+ if ( h != m_Dict.InvalidIndex() )
+ return !m_Dict[h].m_bRoot;
+
+ // If we didn't find the element, it means it's a reference to an external
+ // element (or it's NULL), so don't inline ie.
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Clears the dictionary
+//-----------------------------------------------------------------------------
+void CDmElementSerializationDictionary::Clear()
+{
+ m_Dict.RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+// How many root elements do we have?
+//-----------------------------------------------------------------------------
+int CDmElementSerializationDictionary::RootElementCount() const
+{
+ int nCount = 0;
+ DmElementDictHandle_t h = m_Dict.FirstInorder();
+ while( h != m_Dict.InvalidIndex() )
+ {
+ if ( m_Dict[h].m_bRoot )
+ {
+ ++nCount;
+ }
+ h = m_Dict.NextInorder( h );
+ }
+ return nCount;
+}
+
+
+//-----------------------------------------------------------------------------
+// Iterates over all root elements to serialize
+//-----------------------------------------------------------------------------
+DmElementDictHandle_t CDmElementSerializationDictionary::FirstRootElement() const
+{
+ // NOTE - this code only works with BlockMemory or Memory (NOT FixedMemory)
+
+ // NOTE: I don't have to use First/NextInorder here because there
+ // are guaranteed to be no removals from the dictionary.
+ // Also, using inorder traversal won't get my actual root element to be first in the file
+ int nCount = m_Dict.Count();
+ for ( DmElementDictHandle_t h = 0; h < nCount; ++h )
+ {
+ if ( m_Dict[h].m_bRoot )
+ return h;
+ }
+ return ELEMENT_DICT_HANDLE_INVALID;
+}
+
+DmElementDictHandle_t CDmElementSerializationDictionary::NextRootElement( DmElementDictHandle_t h ) const
+{
+ // NOTE - this code only works with BlockMemory or Memory (NOT FixedMemory)
+
+ // NOTE: I don't have to use First/NextInorder here because there
+ // are guaranteed to be no removals from the dictionary.
+ // Also, using inorder traversal won't get my actual root element to be first in the file
+ ++h;
+ int nCount = m_Dict.Count();
+ for ( ; h < nCount; ++h )
+ {
+ if ( m_Dict[h].m_bRoot )
+ return h;
+ }
+ return ELEMENT_DICT_HANDLE_INVALID;
+}
+
+CDmElement *CDmElementSerializationDictionary::GetRootElement( DmElementDictHandle_t h )
+{
+ Assert( m_Dict[h].m_bRoot );
+ return m_Dict[h].m_pElement;
+}
+
+