Browse Source

Refactor RegistryKeyList

This is in preparation for page patching. The old code kept dynamic
(programatically created) and serialized keys separate. This had the
potential to reorder the key list during the application of a patch.

This is separate from the prp-patches branch to facillitate review.
Adam Johnson 12 years ago
parent
commit
f664e8b9f5
  1. 4
      Sources/Plasma/NucleusLib/pnKeyedObject/hsKeyedObject.cpp
  2. 14
      Sources/Plasma/PubUtilLib/plResMgr/plKeyFinder.cpp
  3. 278
      Sources/Plasma/PubUtilLib/plResMgr/plRegistryKeyList.cpp
  4. 72
      Sources/Plasma/PubUtilLib/plResMgr/plRegistryKeyList.h
  5. 23
      Sources/Plasma/PubUtilLib/plResMgr/plRegistryNode.cpp
  6. 7
      Sources/Plasma/PubUtilLib/plResMgr/plRegistryNode.h

4
Sources/Plasma/NucleusLib/pnKeyedObject/hsKeyedObject.cpp

@ -107,6 +107,10 @@ plKey hsKeyedObject::RegisterAs(plFixedKeyId fixedKey)
if (key == nil)
{
key = hsgResMgr::ResMgr()->NewKey(meUoid, this);
// the key list "helpfully" assigns us an object id.
// we don't want one for fixed keys however (initialization order might bite us in the ass)
static_cast<plKeyImp*>(key)->SetObjectID(0);
}
else
{

14
Sources/Plasma/PubUtilLib/plResMgr/plKeyFinder.cpp

@ -438,15 +438,9 @@ plKey plKeyFinder::IFindSceneNodeKey(plRegistryPageNode* page) const
plRegistryKeyList* keyList = page->IGetKeyList(CLASS_INDEX_SCOPED(plSceneNode));
if (keyList)
{
if (keyList->fStaticKeys.size() == 1)
if (keyList->fKeys.size() == 1)
{
return plKey::Make((plKeyData*)keyList->fStaticKeys[0]);
}
else if (keyList->fDynamicKeys.size() == 1) // happens during export
{
plRegistryKeyList::DynSet::const_iterator it = keyList->fDynamicKeys.begin();
plKeyImp* keyImp = *it;
return plKey::Make(keyImp);
return plKey::Make((plKeyData*)keyList->fKeys[0]);
}
}
@ -459,9 +453,9 @@ plKey plKeyFinder::IFindSceneNodeKey(plRegistryPageNode* page) const
// Get the list of all sceneNodes
plKey retVal(nil);
keyList = page->IGetKeyList(CLASS_INDEX_SCOPED(plSceneNode));
if (keyList && keyList->fStaticKeys.size() == 1)
if (keyList && keyList->fKeys.size() == 1)
{
retVal = plKey::Make((plKeyData*)keyList->fStaticKeys[0]);
retVal = plKey::Make((plKeyData*)keyList->fKeys[0]);
}
// If we just loaded up all the keys for this page, then we
// may have a bunch of keys with a refcount of 0. For any of

278
Sources/Plasma/PubUtilLib/plResMgr/plRegistryKeyList.cpp

@ -39,83 +39,44 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
Mead, WA 99021
*==LICENSE==*/
#include "plRegistryKeyList.h"
#include "plRegistryHelpers.h"
#include "hsStream.h"
#include <algorithm>
plRegistryKeyList::plRegistryKeyList(uint16_t classType)
{
fClassType = classType;
fReffedStaticKeys = 0;
fLocked = 0;
fFlags = 0;
}
#include "HeadSpin.h"
#include "hsStream.h"
#include "pnKeyedObject/plKeyImp.h"
#include "plRegistryHelpers.h"
#include "plRegistryKeyList.h"
plRegistryKeyList::~plRegistryKeyList()
{
hsAssert(fLocked == 0, "Key list still locked on delete");
for (int i = 0; i < fStaticKeys.size(); i++)
{
plKeyImp* keyImp = fStaticKeys[i];
if (!keyImp->ObjectIsLoaded())
delete keyImp;
}
std::for_each(fKeys.begin(), fKeys.end(),
[] (plKeyImp* key) { if (!key->ObjectIsLoaded()) delete key; }
);
}
// Special dummy key that lets us set the return value of the GetName call.
// Makes it easier to do STL searches.
class plSearchKeyImp : public plKeyImp
plKeyImp* plRegistryKeyList::FindKey(const plString& keyName) const
{
public:
plString fSearchKeyName;
const plString& GetName() const { return fSearchKeyName; }
};
plKeyImp* plRegistryKeyList::FindKey(const plString& keyName)
{
static plSearchKeyImp searchKey;
searchKey.fSearchKeyName = keyName;
// Search the static key list
if (fFlags & kStaticUnsorted)
{
// We're unsorted, brute force it. May do a separate search table in the
// future if this is a bottlneck
for (int i = 0; i < fStaticKeys.size(); i++)
{
plKeyImp* curKey = fStaticKeys[i];
if (curKey && !keyName.Compare(curKey->GetName(), plString::kCaseInsensitive))
return curKey;
}
}
auto it = std::find_if(fKeys.begin(), fKeys.end(),
[&] (plKeyImp* key) { return key->GetName().CompareI(keyName) == 0; }
);
if (it != fKeys.end())
return *it;
else
{
// We're sorted, do a fast lookup
StaticVec::const_iterator it = std::lower_bound(fStaticKeys.begin(), fStaticKeys.end(), &searchKey, KeySorter());
if (it != fStaticKeys.end() && !keyName.Compare((*it)->GetName(), plString::kCaseInsensitive))
return *it;
}
// Search the dynamic key list
DynSet::const_iterator dynIt = fDynamicKeys.find(&searchKey);
if (dynIt != fDynamicKeys.end())
return *dynIt;
return nil;
return nullptr;
}
plKeyImp* plRegistryKeyList::FindKey(const plUoid& uoid)
plKeyImp* plRegistryKeyList::FindKey(const plUoid& uoid) const
{
uint32_t objectID = uoid.GetObjectID();
// Key is dynamic or doesn't know it's index. Do a find by name.
// Key is dynamic or doesn't know its index. Do a find by name.
if (objectID == 0)
return FindKey(uoid.GetObjectName());
// Direct lookup
if (objectID <= fStaticKeys.size())
if (objectID <= fKeys.size())
{
#ifdef PLASMA_EXTERNAL_RELEASE
return fStaticKeys[objectID-1];
@ -123,8 +84,8 @@ plKeyImp* plRegistryKeyList::FindKey(const plUoid& uoid)
// If this is an internal release, our objectIDs might not match
// because of local data. Verify that we have the right key by
// name, and if it's wrong, do the slower find-by-name.
plKeyImp *keyImp = fStaticKeys[objectID-1];
if (keyImp->GetName().Compare(uoid.GetObjectName(), plString::kCaseInsensitive) != 0)
plKeyImp *keyImp = fKeys[objectID-1];
if (keyImp->GetName().CompareI(uoid.GetObjectName()) != 0)
return FindKey(uoid.GetObjectName());
else
return keyImp;
@ -135,29 +96,17 @@ plKeyImp* plRegistryKeyList::FindKey(const plUoid& uoid)
// because no one was using them. No worries. The resManager will catch this and
// reload our keys, then try again.
return nil;
}
void plRegistryKeyList::ILock()
{
fLocked++;
}
void plRegistryKeyList::IUnlock()
{
fLocked--;
if (fLocked == 0)
IRepack();
return nullptr;
}
bool plRegistryKeyList::IterateKeys(plRegistryKeyIterator* iterator)
{
ILock();
for (int i = 0; i < fStaticKeys.size(); i++)
for (auto it = fKeys.begin(); it != fKeys.end(); ++it)
{
plKeyImp* keyImp = fStaticKeys[i];
if (keyImp != nil)
plKeyImp* keyImp = *it;
if (keyImp)
{
if (!iterator->EatKey(plKey::Make(keyImp)))
{
@ -167,18 +116,6 @@ bool plRegistryKeyList::IterateKeys(plRegistryKeyIterator* iterator)
}
}
DynSet::const_iterator it;
for (it = fDynamicKeys.begin(); it != fDynamicKeys.end(); it++)
{
plKeyImp* keyImp = *it;
hsAssert(keyImp, "Shouldn't ever have a nil dynamic key");
if (!iterator->EatKey(plKey::Make(keyImp)))
{
IUnlock();
return false;
}
}
IUnlock();
return true;
}
@ -188,23 +125,30 @@ void plRegistryKeyList::AddKey(plKeyImp* key, LoadStatus& loadStatusChange)
loadStatusChange = kNoChange;
hsAssert(fLocked == 0, "Don't currently support adding keys while locked");
if (fLocked == 0 && key != nil)
if (fLocked == 0 && key)
{
// If this is the first key added, we just became loaded
if (fDynamicKeys.empty())
loadStatusChange = kDynLoaded;
hsAssert(std::find(fKeys.begin(), fKeys.end(), key) == fKeys.end(), "Key already added");
hsAssert(fDynamicKeys.find(key) == fDynamicKeys.end(), "Key already added");
fDynamicKeys.insert(key);
}
}
// first key to be added?
if (fKeys.empty())
loadStatusChange = kTypeLoaded;
void plRegistryKeyList::SetKeyUsed(plKeyImp* key)
{
// If this is a static key, mark that we used it. Otherwise, just ignore it.
uint32_t id = key->GetUoid().GetObjectID();
if (id > 0)
fReffedStaticKeys++;
// Objects that already have an object ID will be respected.
// Totally new keys will not have one, but keys from other sources (patches) will.
if (key->GetUoid().GetObjectID() == 0)
{
fKeys.push_back(key);
key->SetObjectID(fKeys.size());
}
else
{
uint32_t id = key->GetUoid().GetObjectID();
if (fKeys.size() < id)
fKeys.resize(id);
fKeys[id - 1] = key;
}
++fReffedKeys;
}
}
bool plRegistryKeyList::SetKeyUnused(plKeyImp* key, LoadStatus& loadStatusChange)
@ -219,122 +163,50 @@ bool plRegistryKeyList::SetKeyUnused(plKeyImp* key, LoadStatus& loadStatusChange
return true;
}
// Check if it's a static key
uint32_t id = key->GetUoid().GetObjectID();
hsAssert(id <= fStaticKeys.size(), "Bad static key id");
if (id != 0 && id <= fStaticKeys.size())
{
fReffedStaticKeys--;
if (fLocked == 0)
IRepack();
// That was our last used static key, we're static unloaded
if (fReffedStaticKeys == 0)
loadStatusChange = kStaticUnloaded;
return true;
}
hsAssert(id <= fKeys.size(), "Bad static key id");
// Try to find it in the dynamic key list
DynSet::iterator dynIt = fDynamicKeys.find(key);
if (dynIt != fDynamicKeys.end())
// Fixed Keys will have id == 0. Let's just make sure we have it before we toss it.
if (id == 0)
{
hsAssert(fLocked == 0, "Don't currently support removing dynamic keys while locked");
if (fLocked == 0)
hsAssert(key->GetUoid().GetLocation() == plLocation::kGlobalFixedLoc, "key id == 0 but not fixed?");
if (!FindKey(key->GetName()))
{
fDynamicKeys.erase(dynIt);
delete key;
// That was our last dynamic key, notify of dynamic unloaded
if (fDynamicKeys.empty())
loadStatusChange = kDynUnloaded;
return true;
hsAssert(false, "Couldn't find fixed key!");
return false;
}
return false;
}
hsAssert(0, "Couldn't find this key, what is it?");
return false;
}
//// IRepack /////////////////////////////////////////////////////////////////
// Frees the memory for our static key array if none of them are loaded
void plRegistryKeyList::IRepack()
{
if (fReffedStaticKeys == 0 && !fStaticKeys.empty())
{
for (int i = 0; i < fStaticKeys.size(); i++)
delete fStaticKeys[i];
fStaticKeys.clear();
}
}
void plRegistryKeyList::PrepForWrite()
{
// If we have any static keys already, we were read in. To keep from
// invalidating old key indexes any new keys have to go on the end, hence we're
// unsorted now.
if (!fStaticKeys.empty())
fFlags |= kStaticUnsorted;
// If a dynamic keys doesn't have an object assigned to it, we're not writing
// it out. Figure out how many valid keys we have.
int numDynKeys = 0;
DynSet::const_iterator cIt;
for (cIt = fDynamicKeys.begin(); cIt != fDynamicKeys.end(); cIt++)
{
plKeyImp* key = *cIt;
// We're only going to write out keys that have objects
if (key->ObjectIsLoaded())
numDynKeys++;
}
else if (id > fKeys.size())
return false;
// Start our new object id's after any already created ones
uint32_t objectID = fStaticKeys.size()+1;
// Make room for our new keys
fStaticKeys.resize(fStaticKeys.size()+numDynKeys);
DynSet::iterator it = fDynamicKeys.begin();
while (it != fDynamicKeys.end())
{
plKeyImp* key = *it;
it++;
// If we're gonna use this key, tag it with it's object id and move it to the static array.
if (key->ObjectIsLoaded())
{
key->SetObjectID(objectID);
fStaticKeys[objectID-1] = key;
objectID++;
fReffedStaticKeys++;
fDynamicKeys.erase(key);
}
}
// Got that key, decrement the key counter
--fReffedKeys;
if (fReffedKeys == 0)
loadStatusChange = kTypeUnloaded;
return true;
}
void plRegistryKeyList::Read(hsStream* s)
{
uint32_t keyListLen = s->ReadLE32();
if (!fStaticKeys.empty())
if (!fKeys.empty())
{
s->Skip(keyListLen);
return;
}
fFlags = s->ReadByte();
// deprecated flags. used to indicate alphabetically sorted keys for some "optimization"
// that really appeared to do nothing. no loss.
s->ReadByte();
uint32_t numKeys = s->ReadLE32();
fStaticKeys.resize(numKeys);
fKeys.reserve(numKeys);
for (int i = 0; i < numKeys; i++)
for (uint32_t i = 0; i < numKeys; ++i)
{
plKeyImp* newKey = new plKeyImp;
newKey->Read(s);
fStaticKeys[i] = newKey;
fKeys.push_back(newKey);
}
}
@ -343,17 +215,13 @@ void plRegistryKeyList::Write(hsStream* s)
// Save space for the length of our data
uint32_t beginPos = s->GetPosition();
s->WriteLE32(0);
s->WriteByte(fFlags);
s->WriteByte(0); // Deprecated flags
int numKeys = fStaticKeys.size();
s->WriteLE32(numKeys);
s->WriteLE32(fKeys.size());
// Write out all our keys (anything in dynamic is unused, so just ignore those)
for (int i = 0; i < numKeys; i++)
{
plKeyImp* key = fStaticKeys[i];
key->Write(s);
}
// Write out all our keys
for (auto it = fKeys.begin(); it != fKeys.end(); ++it)
(*it)->Write(s);
// Go back to the start and write the length of our data
uint32_t endPos = s->GetPosition();

72
Sources/Plasma/PubUtilLib/plResMgr/plRegistryKeyList.h

@ -42,22 +42,13 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
#ifndef plRegistryKeyList_h_inc
#define plRegistryKeyList_h_inc
#include "HeadSpin.h"
#include "pnKeyedObject/plKeyImp.h"
#include <vector>
#include <set>
class plKeyImp;
class plRegistryKeyIterator;
class KeySorter
{
public:
bool operator() (plKeyImp* k1, plKeyImp* k2) const
{
hsAssert(k1 && k2, "Should have valid keys here");
return k1->GetName().Compare(k2->GetName(), plString::kCaseInsensitive) < 0;
}
};
class hsStream;
class plString;
class plUoid;
//
// List of keys for a single class type.
@ -68,60 +59,41 @@ protected:
friend class plKeyFinder;
uint16_t fClassType;
// Lock counter for iterating. If this is >0, don't do any ops that
// can change key positions in the array (instead, just leave holes)
uint16_t fLocked;
uint32_t fLocked;
uint32_t fReffedKeys;
enum Flags { kStaticUnsorted = 0x1 };
uint8_t fFlags;
// Static keys are one's we read off disk. These don't change and are
// assumed to be already sorted when they're read in.
typedef std::vector<plKeyImp*> StaticVec;
StaticVec fStaticKeys;
uint32_t fReffedStaticKeys; // Number of static keys that are loaded
// Dynamic keys are anything created at runtime. They are put in the
// correct sorted position when they are added
typedef std::set<plKeyImp*, KeySorter> DynSet;
DynSet fDynamicKeys;
std::vector<plKeyImp*> fKeys;
plRegistryKeyList() {}
void ILock();
void IUnlock();
void IRepack();
void ILock() { ++fLocked; }
void IUnlock() { --fLocked; }
public:
plRegistryKeyList(uint16_t classType);
enum LoadStatus
{
kNoChange,
kTypeLoaded,
kTypeUnloaded
};
plRegistryKeyList(uint16_t classType)
: fClassType(classType), fReffedKeys(0), fLocked(0)
{ }
~plRegistryKeyList();
uint16_t GetClassType() const { return fClassType; }
// Find a key by name (case-insensitive)
plKeyImp* FindKey(const plString& keyName);
// Find a key by uoid index.
plKeyImp* FindKey(const plUoid& uoid);
plKeyImp* FindKey(const plString& keyName) const;
plKeyImp* FindKey(const plUoid& uoid) const;
bool IterateKeys(plRegistryKeyIterator* iterator);
// Changes in our load status that can be caused by loading or unloading a key
enum LoadStatus
{
kNoChange,
kDynLoaded,
kDynUnloaded,
kStaticUnloaded,
};
void AddKey(plKeyImp* key, LoadStatus& loadStatusChange);
void SetKeyUsed(plKeyImp* key);
void SetKeyUsed(plKeyImp* key) { ++fReffedKeys; }
bool SetKeyUnused(plKeyImp* key, LoadStatus& loadStatusChange);
// Export time only. Before we write to disk, assign all the static keys
// object ID's that they can use to do fast lookups at load time.
void PrepForWrite();
void Read(hsStream* s);
void Write(hsStream* s);
};

23
Sources/Plasma/PubUtilLib/plResMgr/plRegistryNode.cpp

@ -55,8 +55,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
plRegistryPageNode::plRegistryPageNode(const plFileName& path)
: fValid(kPageCorrupt)
, fPath(path)
, fDynLoadedTypes(0)
, fStaticLoadedTypes(0)
, fLoadedTypes(0)
, fOpenRequests(0)
, fIsNewPage(false)
{
@ -73,8 +72,7 @@ plRegistryPageNode::plRegistryPageNode(const plLocation& location, const plStrin
const plString& page, const plFileName& dataPath)
: fValid(kPageOk)
, fPageInfo(location)
, fDynLoadedTypes(0)
, fStaticLoadedTypes(0)
, fLoadedTypes(0)
, fOpenRequests(0)
, fIsNewPage(true)
{
@ -182,7 +180,7 @@ void plRegistryPageNode::LoadKeys()
stream->SetPosition(oldPos);
CloseStream();
fStaticLoadedTypes = fKeyLists.size();
fLoadedTypes = fKeyLists.size();
}
void plRegistryPageNode::UnloadKeys()
@ -195,8 +193,7 @@ void plRegistryPageNode::UnloadKeys()
}
fKeyLists.clear();
fDynLoadedTypes = 0;
fStaticLoadedTypes = 0;
fLoadedTypes = 0;
}
//// plWriteIterator /////////////////////////////////////////////////////////
@ -235,8 +232,6 @@ void plRegistryPageNode::Write()
for (it = fKeyLists.begin(); it != fKeyLists.end(); it++)
{
plRegistryKeyList* keyList = it->second;
keyList->PrepForWrite();
int ver = plVersion::GetCreatableVersion(keyList->GetClassType());
fPageInfo.AddClassVersion(keyList->GetClassType(), ver);
}
@ -358,8 +353,8 @@ void plRegistryPageNode::AddKey(plKeyImp* key)
plRegistryKeyList::LoadStatus loadStatusChange;
keys->AddKey(key, loadStatusChange);
if (loadStatusChange == plRegistryKeyList::kDynLoaded)
fDynLoadedTypes++;
if (loadStatusChange == plRegistryKeyList::kTypeLoaded)
++fLoadedTypes;
}
void plRegistryPageNode::SetKeyUsed(plKeyImp* key)
@ -381,10 +376,8 @@ bool plRegistryPageNode::SetKeyUnused(plKeyImp* key)
bool removed = keys->SetKeyUnused(key, loadStatusChange);
// If the key type just changed load status, update our load counts
if (loadStatusChange == plRegistryKeyList::kDynUnloaded)
fDynLoadedTypes--;
else if (loadStatusChange == plRegistryKeyList::kStaticUnloaded)
fStaticLoadedTypes--;
if (loadStatusChange == plRegistryKeyList::kTypeUnloaded)
--fLoadedTypes;
return removed;
}

7
Sources/Plasma/PubUtilLib/plResMgr/plRegistryNode.h

@ -74,8 +74,7 @@ protected:
// Map from class type to a list of keys of that type
typedef std::map<uint16_t, plRegistryKeyList*> KeyMap;
KeyMap fKeyLists;
int fDynLoadedTypes; // The number of key types that have dynamic keys loaded
int fStaticLoadedTypes; // The number of key types that have all their keys loaded
uint32_t fLoadedTypes; // The number of key types that have dynamic keys loaded
PageCond fValid; // Condition of the page
plFileName fPath; // Path to the page file
@ -104,9 +103,9 @@ public:
PageCond GetPageCondition() { return fValid; }
// True if we have any static or dynamic keys loaded
bool IsLoaded() const { return fDynLoadedTypes > 0 || fStaticLoadedTypes > 0; }
bool IsLoaded() const { return fLoadedTypes > 0; }
// True if all of our static keys are loaded
bool IsFullyLoaded() const { return (fStaticLoadedTypes == fKeyLists.size() && !fKeyLists.empty()) || fIsNewPage; }
bool IsFullyLoaded() const { return (fLoadedTypes == fKeyLists.size() && !fKeyLists.empty()) || fIsNewPage; }
// Export time only. If we want to reuse a page, load the keys we want then
// call SetNewPage, so it will be considered a new page from now on. That

Loading…
Cancel
Save