Browse Source

Improve key deallocation support for `Collection`s

`RemoveItem()` method for `AssociativeArray()` is now able to deallocate
keys. `Empty()` method is now defined in a `Collectino` class and allows
users to recursively free nested collections, while deallocating their
keys.
pull/8/head
Anton Tarasenko 3 years ago
parent
commit
47c08b3d86
  1. 31
      sources/Data/Collections/AssociativeArray.uc
  2. 16
      sources/Data/Collections/Collection.uc
  3. 21
      sources/Data/Collections/DynamicArray.uc
  4. 29
      sources/Data/Collections/Tests/TEST_CollectionsMixed.uc

31
sources/Data/Collections/AssociativeArray.uc

@ -475,9 +475,12 @@ public final function AssociativeArray CreateItem(
* that, use `TakeItem()` or `TakeEntry()` methods. * that, use `TakeItem()` or `TakeEntry()` methods.
* *
* @param key Key for which to remove value. * @param key Key for which to remove value.
* @param deallocateKey Should key be also deallocated?
* @return Caller `AssociativeArray` to allow for method chaining. * @return Caller `AssociativeArray` to allow for method chaining.
*/ */
public final function AssociativeArray RemoveItem(AcediaObject key) public final function AssociativeArray RemoveItem(
AcediaObject key,
optional bool deallocateKey)
{ {
local Entry entryToRemove; local Entry entryToRemove;
local int bucketIndex, entryIndex; local int bucketIndex, entryIndex;
@ -493,23 +496,16 @@ public final function AssociativeArray RemoveItem(AcediaObject key)
if (entryToRemove.managed && entryToRemove.value != none) { if (entryToRemove.managed && entryToRemove.value != none) {
entryToRemove.value.FreeSelf(entryToRemove.valueLifeVersion); entryToRemove.value.FreeSelf(entryToRemove.valueLifeVersion);
} }
if (deallocateKey && entryToRemove.key != none) {
entryToRemove.key.FreeSelf(entryToRemove.keyLifeVersion);
}
return self; return self;
} }
/**
* Completely clears caller `AssociativeArray` of all stored entries,
* deallocating any stored managed values.
*
* @param deallocateKeys Setting this to `true` will force this method to
* also deallocate all keys from the caller `AssociativeArray`.
* Since we do not record whether `AssociativeArray` manages keys like it
* does values - all keys will be deallocated, so use this parameter with
* caution.
* @return Caller `AssociativeArray` to allow for method chaining.
*/
public function Empty(optional bool deallocateKeys) public function Empty(optional bool deallocateKeys)
{ {
local int i, j; local int i, j;
local Collection subCollection;
local array<Entry> nextEntries; local array<Entry> nextEntries;
for (i = 0; i < hashTable.length; i += 1) for (i = 0; i < hashTable.length; i += 1)
{ {
@ -518,6 +514,17 @@ public function Empty(optional bool deallocateKeys)
{ {
if (!nextEntries[j].managed) continue; if (!nextEntries[j].managed) continue;
if (nextEntries[j].value == none) continue; if (nextEntries[j].value == none) continue;
if ( nextEntries[j].value.GetLifeVersion()
!= nextEntries[j].valueLifeVersion) {
continue;
}
if (deallocateKeys)
{
subCollection = Collection(nextEntries[j].value);
if (subCollection != none) {
subCollection.Empty(true);
}
}
nextEntries[j].value.FreeSelf(nextEntries[j].valueLifeVersion); nextEntries[j].value.FreeSelf(nextEntries[j].valueLifeVersion);
} }
if (deallocateKeys) if (deallocateKeys)

16
sources/Data/Collections/Collection.uc

@ -60,6 +60,22 @@ public final function Iter Iterate()
return newIterator; return newIterator;
} }
/**
* Completely clears caller `Collections` of all stored entries,
* deallocating any stored managed values.
*
* @param deallocateKeys Setting this to `true` will force this method to
* also deallocate all keys from the caller `Collection`, if it uses them.
* If this parameter is set to `true`, then `Empty()` call will also be
* made recursively for all stored `Collection`, also causing them to
* deallocate their keys.
* For Acedia keys are only used by `AssociativeArray`.
* Since we do not record whether `Collection` manages keys like it
* does values - all keys will be deallocated, so use this parameter with
* caution.
*/
public function Empty(optional bool deallocateKeys) {}
/** /**
* Returns stored `AcediaObject` from the caller storage * Returns stored `AcediaObject` from the caller storage
* (or from it's sub-storages) via given `JSONPointer` path. * (or from it's sub-storages) via given `JSONPointer` path.

21
sources/Data/Collections/DynamicArray.uc

@ -114,16 +114,21 @@ protected final function DynamicArray FreeManagedItem(int index)
return self; return self;
} }
/** public function Empty(optional bool deallocateKeys)
* Empties caller `DynamicArray`, erasing it's contents. {
* All managed objects will be deallocated. local int i;
* local Collection subCollection;
* @return Reference to the caller `DynamicArray` to allow for method chaining. if (deallocateKeys)
*/
public final function DynamicArray Empty()
{ {
for (i = 0; i < storedObjects.length; i += 1)
{
subCollection = Collection(GetItem(i));
if (subCollection != none) {
subCollection.Empty(true);
}
}
}
SetLength(0); SetLength(0);
return self;
} }
/** /**

29
sources/Data/Collections/Tests/TEST_CollectionsMixed.uc

@ -27,6 +27,8 @@ protected static function TESTS()
Context("Testing accessing collections by JSON pointers."); Context("Testing accessing collections by JSON pointers.");
Test_GetBy(); Test_GetBy();
Test_GetTypeBy(); Test_GetTypeBy();
Context("Testing erasing collections with keys via `Empty()` method.");
Test_EmptyWithKeys();
} }
protected static function Test_GetBy() protected static function Test_GetBy()
@ -96,6 +98,33 @@ protected static function Test_GetTypeBy()
TEST_ExpectNone(obj.GetTextBy(P(""))); TEST_ExpectNone(obj.GetTextBy(P("")));
} }
protected static function Test_EmptyWithKeys()
{
local Text outerKey, innerKey1, innerKey2, innerKey3;
local DynamicArray middleArray;
local AssociativeArray outerObject, innerObject1, innerObject2;
outerKey = __().text.FromString("first");
innerKey1 = __().text.FromString("third?");
innerKey2 = __().text.FromString("Or not?");
innerKey3 = __().text.FromString("Like hell if I know!!!1111");
outerObject = __().collections.EmptyAssociativeArray();
innerObject1 = __().collections.EmptyAssociativeArray();
innerObject2 = __().collections.EmptyAssociativeArray();
middleArray = __().collections.EmptyDynamicArray();
innerObject1.SetItem(innerKey1, __().box.int(4));
innerObject1.SetItem(innerKey2, __().box.float(-4.6));
innerObject2.SetItem(innerKey2, __().ref.bool(true));
innerObject2.SetItem(innerKey3, none);
middleArray.AddItem(innerObject1).AddItem(innerObject2);
outerObject.SetItem(outerKey, middleArray);
outerObject.Empty(true);
Issue("Collection keys are not deallocated by `Empty(true)` method");
TEST_ExpectFalse(outerKey.IsAllocated());
TEST_ExpectFalse(innerKey1.IsAllocated());
TEST_ExpectFalse(innerKey2.IsAllocated());
TEST_ExpectFalse(innerKey3.IsAllocated());
}
defaultproperties defaultproperties
{ {
caseGroup = "Collections" caseGroup = "Collections"

Loading…
Cancel
Save