From 66d02e3c8c039385d794587a08bd7cf3d3cb79fc Mon Sep 17 00:00:00 2001 From: Anton Tarasenko Date: Fri, 15 Jul 2022 20:36:27 +0700 Subject: [PATCH] Finish `Collection`s refactoring --- sources/Aliases/AliasSource.uc | 25 +- sources/Avarice/AvariceLink.uc | 5 +- sources/Avarice/AvariceMessage.uc | 10 +- sources/Avarice/AvariceTcpStream.uc | 10 +- sources/BaseRealm/Iter.uc | 7 +- .../Commands/BuiltInCommands/ACommandTest.uc | 6 +- sources/Data/Collections/ArrayList.uc | 50 +- sources/Data/Collections/ArrayListIterator.uc | 2 +- sources/Data/Collections/AssociativeArray.uc | 1015 ----------------- .../Collections/AssociativeArrayIterator.uc | 83 -- sources/Data/Collections/Collection.uc | 397 ++++--- sources/Data/Collections/CollectionsAPI.uc | 74 -- sources/Data/Collections/DynamicArray.uc | 817 ------------- .../Data/Collections/DynamicArrayIterator.uc | 80 -- sources/Data/Collections/HashTable.uc | 50 +- sources/Data/Collections/HashTableIterator.uc | 2 +- .../Tests/TEST_AssociativeArray.uc | 488 -------- .../Collections/Tests/TEST_DynamicArray.uc | 322 ------ .../Collections/Tests/TEST_IteratorOld.uc | 143 --- sources/Data/Database/DBAPI.uc | 30 +- .../Data/Database/Tests/TEST_LocalDatabase.uc | 9 +- .../BaseImplementation/EKFInventory.uc | 27 +- .../KF1Frontend/World/KF1_EntityIterator.uc | 2 +- .../KF1Frontend/World/KF1_TracingIterator.uc | 2 +- sources/Logger/Logger.uc | 4 +- sources/Text/JSON/JSONAPI.uc | 510 +-------- sources/Text/Tests/TEST_JSON.uc | 18 +- 27 files changed, 328 insertions(+), 3860 deletions(-) delete mode 100644 sources/Data/Collections/AssociativeArray.uc delete mode 100644 sources/Data/Collections/AssociativeArrayIterator.uc delete mode 100644 sources/Data/Collections/DynamicArray.uc delete mode 100644 sources/Data/Collections/DynamicArrayIterator.uc delete mode 100644 sources/Data/Collections/Tests/TEST_AssociativeArray.uc delete mode 100644 sources/Data/Collections/Tests/TEST_DynamicArray.uc delete mode 100644 sources/Data/Collections/Tests/TEST_IteratorOld.uc diff --git a/sources/Aliases/AliasSource.uc b/sources/Aliases/AliasSource.uc index 5a1e8a5..39f6aad 100644 --- a/sources/Aliases/AliasSource.uc +++ b/sources/Aliases/AliasSource.uc @@ -23,7 +23,7 @@ * along with Acedia. If not, see . */ class AliasSource extends Singleton - dependson(AssociativeArray) + dependson(HashTable) config(AcediaAliases); // (Sub-)class of `Aliases` objects that this `AliasSource` uses to store @@ -50,7 +50,7 @@ var private config array record; // It contains same records as `record` array + aliases from // `loadedAliasObjects` objects when there are no duplicate aliases. // Otherwise only stores first loaded alias. -var private AssociativeArray aliasHash; +var private HashTable aliasHash; var private LoggerAPI.Definition errIncorrectAliasPair, warnDuplicateAlias; @@ -63,7 +63,7 @@ protected function OnCreated() } // Load and hash loadedAliasObjects = aliasesClass.static.LoadAllObjects(); - aliasHash = _.collections.EmptyAssociativeArray(); + aliasHash = _.collections.EmptyHashTable(); HashValidAliasesFromRecord(); HashValidAliasesFromPerObjectConfig(); } @@ -125,8 +125,8 @@ private final function HashValidAliasesFromPerObjectConfig() // in a case-insensitive way. private final function InsertAlias(BaseText alias, BaseText value) { - local Text aliasLowerCaseCopy; - local AssociativeArray.Entry hashEntry; + local Text aliasLowerCaseCopy; + local HashTable.Entry hashEntry; if (alias == none) return; if (value == none) return; aliasLowerCaseCopy = alias.LowerCopy(); @@ -136,7 +136,8 @@ private final function InsertAlias(BaseText alias, BaseText value) } _.memory.Free(hashEntry.key); _.memory.Free(hashEntry.value); - aliasHash.SetItem(aliasLowerCaseCopy, value.Copy(), true); + aliasHash.SetItem(aliasLowerCaseCopy, value); + aliasLowerCaseCopy.FreeSelf(); } /** @@ -191,7 +192,7 @@ public function Text Resolve(BaseText alias, optional bool copyOnFailure) result = Text(aliasHash.GetItem(lowerCaseAlias)); lowerCaseAlias.FreeSelf(); if (result != none) { - return result.Copy(); + return result; } if (copyOnFailure) { return alias.Copy(); @@ -254,6 +255,8 @@ public final function bool AddAlias( record[record.length] = newPair; } aliasHash.SetItem(lowerCaseAlias, aliasValue); + _.memory.Free(lowerCaseAlias); + _.memory.Free(aliasValue); AliasService(class'AliasService'.static.Require()).PendingSaveSource(self); return true; } @@ -275,10 +278,10 @@ public final function bool AddAlias( */ public final function RemoveAlias(BaseText aliasToRemove) { - local int i; - local bool isMatchingRecord; - local bool removedAliasFromRecord; - local AssociativeArray.Entry hashEntry; + local int i; + local bool isMatchingRecord; + local bool removedAliasFromRecord; + local HashTable.Entry hashEntry; if (aliasToRemove == none) { return; } diff --git a/sources/Avarice/AvariceLink.uc b/sources/Avarice/AvariceLink.uc index 23f7c57..4098a6e 100644 --- a/sources/Avarice/AvariceLink.uc +++ b/sources/Avarice/AvariceLink.uc @@ -68,7 +68,7 @@ var private SimpleSignal onDeathSignal; // We want to have a separate signal for each message "service", since most // users of `AvariceLink` would only care about one particular service. // To achieve that we use this array as a "service name" <-> "signal" map. -var private AssociativeArray serviceSignalMap; +var private HashTable serviceSignalMap; var private const int TSERVICE_PREFIX, TTYPE_PREFIX; var private const int TPARAMS_PREFIX, TMESSAGE_SUFFIX; @@ -80,7 +80,7 @@ protected function Constructor() onConnectedSignal = SimpleSignal(_.memory.Allocate(class'SimpleSignal')); onDisconnectedSignal = SimpleSignal(_.memory.Allocate(class'SimpleSignal')); onDeathSignal = SimpleSignal(_.memory.Allocate(class'SimpleSignal')); - serviceSignalMap = _.collections.EmptyAssociativeArray(); + serviceSignalMap = _.collections.EmptyHashTable(); } protected function Finalizer() @@ -219,6 +219,7 @@ private final function Avarice_OnMessage_Signal GetServiceSignal( result = Avarice_OnMessage_Signal( _.memory.Allocate(class'Avarice_OnMessage_Signal')); serviceSignalMap.SetItem(service, result); + _.memory.Free(result); } else { service.FreeSelf(); diff --git a/sources/Avarice/AvariceMessage.uc b/sources/Avarice/AvariceMessage.uc index 2655819..3d823d4 100644 --- a/sources/Avarice/AvariceMessage.uc +++ b/sources/Avarice/AvariceMessage.uc @@ -35,7 +35,7 @@ var public Text type; // Value of the "p" field var public AcediaObject parameters; -var private AssociativeArray messageTemplate; +var private HashTable messageTemplate; var private const int TS, TT, TP; @@ -44,7 +44,7 @@ public static function StaticConstructor() if (StaticConstructorGuard()) return; super.StaticConstructor(); - default.messageTemplate = __().collections.EmptyAssociativeArray(); + default.messageTemplate = __().collections.EmptyHashTable(); ResetTemplate(default.messageTemplate); } @@ -58,7 +58,7 @@ protected function Finalizer() parameters = none; } -private static final function ResetTemplate(AssociativeArray template) +private static final function ResetTemplate(HashTable template) { if (template == none) { return; @@ -70,8 +70,8 @@ private static final function ResetTemplate(AssociativeArray template) public final function MutableText ToText() { - local MutableText result; - local AssociativeArray template; + local MutableText result; + local HashTable template; if (type == none) return none; if (service == none) return none; diff --git a/sources/Avarice/AvariceTcpStream.uc b/sources/Avarice/AvariceTcpStream.uc index b2bf80a..623c287 100644 --- a/sources/Avarice/AvariceTcpStream.uc +++ b/sources/Avarice/AvariceTcpStream.uc @@ -335,19 +335,19 @@ private final function AvariceMessage MessageFromText(BaseText message) { local Parser parser; local AvariceMessage result; - local AssociativeArray parsedMessage; + local HashTable parsedMessage; local AcediaObject item; if (message == none) { return none; } parser = _.text.Parse(message); - parsedMessage = _.json.ParseObjectWith(parser); + parsedMessage = _.json.ParseHashTableWith(parser); parser.FreeSelf(); if (parsedMessage == none) { return none; } result = AvariceMessage(_.memory.Allocate(class'AvariceMessage')); - item = parsedMessage.TakeItem(keyS, true); + item = parsedMessage.TakeItem(keyS); if (item == none || item.class != class'Text') { _.memory.Free(item); @@ -356,7 +356,7 @@ private final function AvariceMessage MessageFromText(BaseText message) return none; } result.service = Text(item); - item = parsedMessage.TakeItem(keyT, true); + item = parsedMessage.TakeItem(keyT); if (item == none || item.class != class'Text') { _.memory.Free(item); @@ -365,7 +365,7 @@ private final function AvariceMessage MessageFromText(BaseText message) return none; } result.type = Text(item); - result.parameters = parsedMessage.TakeItem(keyP, true); + result.parameters = parsedMessage.TakeItem(keyP); _.memory.Free(parsedMessage); return result; } diff --git a/sources/BaseRealm/Iter.uc b/sources/BaseRealm/Iter.uc index 425b7c2..ef21bdb 100644 --- a/sources/BaseRealm/Iter.uc +++ b/sources/BaseRealm/Iter.uc @@ -25,14 +25,9 @@ class Iter extends AcediaObject * Makes iterator pick next item. * Use `HasFinished()` to check whether you have iterated all of them. * - * @param skipNone @deprecated - * Set this to `true` if you want to skip all stored - * values that are equal to `none`. By default does not skip them. - * Since order of iterating through items is not guaranteed, at each - * `Next()` call an arbitrary set of items can be skipped. * @return Reference to caller `Iterator` to allow for method chaining. */ -public function Iter Next(optional bool skipNone); +public function Iter Next(); /** * Returns current value pointed to by an iterator. diff --git a/sources/Commands/BuiltInCommands/ACommandTest.uc b/sources/Commands/BuiltInCommands/ACommandTest.uc index 299472a..7e29f87 100644 --- a/sources/Commands/BuiltInCommands/ACommandTest.uc +++ b/sources/Commands/BuiltInCommands/ACommandTest.uc @@ -28,8 +28,8 @@ protected function BuildData(CommandDataBuilder builder) protected function Executed(Command.CallData result, EPlayer callerPlayer) { - local Parser parser; - local AssociativeArray root; + local Parser parser; + local HashTable root; /*local int i; local WeaponLocker lol; local array aaa; @@ -63,7 +63,7 @@ protected function Executed(Command.CallData result, EPlayer callerPlayer) } }*/ parser = _.text.ParseString("{\"innerObject\":{\"my_bool\":true,\"array\":[\"Engine.Actor\",false,null,{\"something \\\"here\\\"\":\"yes\",\"maybe\":0.003},56.6],\"one more\":{\"nope\":324532,\"whatever\":false,\"o rly?\":\"ya rly\"},\"my_int\":-9823452},\"some_var\":-7.32,\"another_var\":\"aye!\"}"); - root = _.json.ParseObjectWith(parser); + root = _.json.ParseHashTableWith(parser); callerPlayer.BorrowConsole().WriteLine(_.json.PrettyPrint(root)); } diff --git a/sources/Data/Collections/ArrayList.uc b/sources/Data/Collections/ArrayList.uc index 5c42634..9b8943d 100644 --- a/sources/Data/Collections/ArrayList.uc +++ b/sources/Data/Collections/ArrayList.uc @@ -95,7 +95,7 @@ protected final function ArrayList FreeItem(int index) return self; } -public function Empty(optional bool deprecated) +public function Empty() { SetLength(0); } @@ -1103,54 +1103,6 @@ public final function MutableText GetMutableText(int index) return result; } -/** - * Returns `AssociativeArray` item at `index`. If index is invalid or - * stores a non-`AssociativeArray` value, returns `none`. - * - * Referred value must be stored as `AssociativeArray` - * (or one of it's sub-classes) for this method to work. - * - * @param index Index of an `AssociativeArray` item that `ArrayList` - * has to return. - * @return `AssociativeArray` value at `index` in the caller `ArrayList`. - * `none` if passed `index` is invalid or non-`AssociativeArray` value - * is stored there. - */ -public final function AssociativeArray GetAssociativeArray(int index) -{ - local AssociativeArray result; - - result = AssociativeArray(BorrowItem(index)); - if (result != none) { - result.NewRef(); - } - return result; -} - -/** - * Returns `DynamicArray` item at `index`. If index is invalid or - * stores a non-`DynamicArray` value, returns `none`. - * - * Referred value must be stored as `Text` (or one of it's sub-classes, - * such as `MutableText`) for this method to work. - * - * @param index Index of a `DynamicArray` item that caller `ArrayList` - * has to return. - * @return `DynamicArray` value at `index` in the caller `ArrayList`. - * `none` if passed `index` is invalid or non-`ArrayList` value - * is stored there. - */ -public final function DynamicArray GetDynamicArray(int index) -{ - local DynamicArray result; - - result = DynamicArray(BorrowItem(index)); - if (result != none) { - result.NewRef(); - } - return result; -} - /** * Returns `ArrayList` item at `index`. If index is invalid or * stores a non-`ArrayList` value, returns `none`. diff --git a/sources/Data/Collections/ArrayListIterator.uc b/sources/Data/Collections/ArrayListIterator.uc index 80cb1e0..233b9e0 100644 --- a/sources/Data/Collections/ArrayListIterator.uc +++ b/sources/Data/Collections/ArrayListIterator.uc @@ -47,7 +47,7 @@ public function Iter LeaveOnlyNotNone() return self; } -public function Iter Next(optional bool deprecated) +public function Iter Next() { local int collectionLength; diff --git a/sources/Data/Collections/AssociativeArray.uc b/sources/Data/Collections/AssociativeArray.uc deleted file mode 100644 index 5e6867a..0000000 --- a/sources/Data/Collections/AssociativeArray.uc +++ /dev/null @@ -1,1015 +0,0 @@ -/** - * This class implements an associative array for storing arbitrary types - * of data that provides a quick (near constant) access to *values* by - * associated *keys*. - * Since UnrealScript lacks any sort of templating, `AssociativeArray` - * stores generic `AcediaObject` keys and values. `Text` can be used instead of - * typical `string` keys and primitive values can be added in their boxed form - * (either as actual `Box` or as it's reference counterpart). - * Copyright 2020 - 2021 Anton Tarasenko - *------------------------------------------------------------------------------ - * This file is part of Acedia. - * - * Acedia is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License, or - * (at your option) any later version. - * - * Acedia 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Acedia. If not, see . - */ -class AssociativeArray extends Collection; - -// Defines key <-> value (with managed status) mapping. -// Stores lifetime information to ensure that values were not reallocated -// after being added to the collection. -struct Entry -{ - var public AcediaObject key; - var protected int keyLifeVersion; - var public AcediaObject value; - var protected int valueLifeVersion; - var public bool managed; -}; -// Bucket of entries. Used to store entries with the same index in hash table. -struct Bucket -{ - var array entries; -}; -var private array hashTable; -// Amount of elements currently stored in this `AssociativeArray`. -// If one of the keys was deallocated outside of `AssociativeArray`, -// this value may overestimate actual amount of elements. -var private int storedElementCount; -// Lower limit on hash table capacity, can be changed by the user. -var private int minimalCapacity; - -// hard lower and upper limits on hash table size, constant. -var private const int MINIMUM_SIZE; -var private const int MAXIMUM_SIZE; -// Minimum and maximum allowed density of elements -// (`storedElementCount / hashTable.length`). -// If density falls outside this range, - we have to resize hash table to -// get into (MINIMUM_DENSITY; MAXIMUM_DENSITY) bounds, as long as it does not -// violate hard size restrictions. -// Actual size changes in multipliers of 2, so -// `MINIMUM_DENSITY * 2 < MAXIMUM_DENSITY` must hold or we will constantly -// oscillate outside of (MINIMUM_DENSITY; MAXIMUM_DENSITY) bounds. -var private const float MINIMUM_DENSITY; -var private const float MAXIMUM_DENSITY; - -/** - * Auxiliary struct, necessary to implement iterator for `AssociativeArray`. - * Can be used for manual iteration, but should be avoided in favor of - * `Iterator`. - */ -struct Index -{ - var protected int bucketIndex; - var protected int entryIndex; -}; - -protected function Constructor() -{ - UpdateHashTableSize(); -} - -protected function Finalizer() -{ - Empty(); -} - -// Auxiliary method that is needed as a replacement for `%` module -// operator, since it is an operation on `float`s in UnrealScript and does not -// have appropriate value range to work with hashes. -// Assumes non-negative input. -private function int Remainder(int number, int divisor) -{ - local int quotient; - quotient = number / divisor; - return (number - quotient * divisor); -} - -// Calculates appropriate bucket index for the given key. -// Assumes that given key is not `none` and is allocated. -private final function int GetBucketIndex(AcediaObject key) -{ - local int bucketIndex; - bucketIndex = key.GetHashCode(); - if (bucketIndex < 0) { - // Minimum `int` value is greater than maximum one in absolute value, - // so shift it up to avoid overflow. - bucketIndex = -1 * (bucketIndex + 1); - } - bucketIndex = Remainder(bucketIndex, hashTable.length); - return bucketIndex; -} - -// Accessing value in `AssociativeArray` requires: -// 1. Two level lookup of both bucket and entry (inside that bucket) -// indices; -// 2. Lifetime checks to ensure no-one reallocated keys/values we -// are using; -// 3. Appropriate clean up o keys/values that were already deallocated. -// -// We spread the cost of the cleaning by pairing it with every bucket -// access. -// We only clean one (accessed) bucket per `FindEntryIndices()` and, -// given that there isn't many hash collisions, this operation should not be -// noticeably expensive. -// -// As a result returns bucket's and entry's indices in `bucketIndex` and -// `entryIndex` out variables. -// `bucketIndex` is guaranteed to be found for non-`none` keys, -// `entryIndex` is valid iff method returns `true`, otherwise it's equal to -// the index at which new property can get inserted. -private final function bool FindEntryIndices( - AcediaObject key, - out int bucketIndex, - out int entryIndex) -{ - local int i; - local array bucketEntries; - if (key == none) return false; - if (!key.IsAllocated()) return false; - - bucketIndex = GetBucketIndex(key); - CleanBucket(hashTable[bucketIndex]); - // Check if bucket actually has given key. - bucketEntries = hashTable[bucketIndex].entries; - for (i = 0; i < bucketEntries.length; i += 1) - { - if (key.IsEqual(bucketEntries[i].key)) - { - entryIndex = i; - return true; - } - } - entryIndex = bucketEntries.length; - return false; -} - -// Cleans given bucket from entries with deallocated/reallocated -// keys or values. -private final function CleanBucket(out Bucket bucketToClean) -{ - local int i; - local Entry nextEntry; - local array bucketEntries; - bucketEntries = bucketToClean.entries; - i = 0; - while (i < bucketEntries.length) - { - nextEntry = bucketEntries[i]; - // If value was already reallocated - set it to `none`. - if ( nextEntry.value != none - && nextEntry.value.GetLifeVersion() != nextEntry.valueLifeVersion) - { - bucketEntries[i].value = none; - } - // If key was reallocated - it's value becomes essentially - // inaccessible, so we deallocate it. - // All keys, recorded in hash table, guaranteed to be `!= none`. - if (nextEntry.key.GetLifeVersion() != nextEntry.keyLifeVersion) - { - if (bucketEntries[i].value != none && bucketEntries[i].managed) { - bucketEntries[i].value.FreeSelf(nextEntry.valueLifeVersion); - } - bucketEntries.Remove(i, 1); - // We'll update the count, but won't trigger hash table size update - // to avoid making value's indices lookup more expensive, since - // this method is used in `FindEntryIndices()`. - storedElementCount = Max(0, storedElementCount - 1); - continue; - } - i += 1; - } - bucketToClean.entries = bucketEntries; -} - -// Checks if we need to change our current hash table size -// and does so if needed -private final function UpdateHashTableSize() -{ - local int oldSize, newSize; - oldSize = hashTable.length; - // Calculate new size (and whether it is needed) based on amount of - // stored properties and current size - newSize = oldSize; - if (storedElementCount < newSize * MINIMUM_DENSITY) { - newSize /= 2; - } - else if (storedElementCount > newSize * MAXIMUM_DENSITY) { - newSize *= 2; - } - // `table_density = items_amount / table_size`, so to store at least - // `items_amount = minimalCapacity` without making table too dense we need - // `table_size = minimalCapacity / MAXIMUM_DENSITY`. - newSize = Max(newSize, Ceil(minimalCapacity / MAXIMUM_DENSITY)); - // But everything must fall into the set hard limits - newSize = Clamp(newSize, MINIMUM_SIZE, MAXIMUM_SIZE); - // Only resize if difference is huge enough or table does not exists yet - if (newSize != oldSize) { - ResizeHashTable(newSize); - } -} - -// Changes size of the hash table, does not check any limits, -// does not check if `newSize` is a valid size (`newSize > 0`). -private final function ResizeHashTable(int newSize) -{ - local int i, j; - local int newBucketIndex, newEntryIndex; - local array bucketEntries; - local array oldHashTable; - oldHashTable = hashTable; - // Clean current hash table - hashTable.length = 0; - hashTable.length = newSize; - for (i = 0; i < oldHashTable.length; i += 1) - { - CleanBucket(oldHashTable[i]); - bucketEntries = oldHashTable[i].entries; - for (j = 0; j < bucketEntries.length; j += 1) - { - newBucketIndex = GetBucketIndex(bucketEntries[j].key); - newEntryIndex = hashTable[newBucketIndex].entries.length; - hashTable[newBucketIndex].entries[newEntryIndex] = bucketEntries[j]; - } - } -} - -/** - * Returns minimal capacity of the caller associative array. - * - * See `SetMinimalCapacity()` for details. - * - * @return Minimal capacity of the caller associative array. Default is zero. - */ -public final function int GetMinimalCapacity() -{ - return minimalCapacity; -} - -/** - * Returns minimal capacity of the caller associative array. - * - * This associative array works like a hash table and needs to allocate - * sufficiently large dynamic array as a storage for its items. - * If you keep adding new items that storage will eventually become too small - * for hash table to work efficiently and we will have to reallocate and - * re-fill it. If you want to add a huge enough amount of items, this process - * can be repeated several times. - * This is not ideal, since it means doing a lot of iteration, each - * increasing infinite loop counter (game will crash if it gets high enough). - * Setting minimal capacity to the (higher) amount of items you expect to - * store in the caller array can remove the need for reallocating the storage. - * - * @param newMinimalCapacity New minimal capacity of this associative array. - * It's recommended to set it to the max amount of items you expect to - * store in this associative array - * (you will be still allowed to store more). - */ -public final function SetMinimalCapacity(int newMinimalCapacity) -{ - minimalCapacity = newMinimalCapacity; - UpdateHashTableSize(); -} - -/** - * Checks if caller `AssociativeArray` has value recorded with a given `key`. - * - * @return `true` if caller `AssociativeArray` has value recorded with - * a given `key` and `false` otherwise. - */ -public final function bool HasKey(AcediaObject key) -{ - local int bucketIndex, entryIndex; - return FindEntryIndices(key, bucketIndex, entryIndex); -} - -/** - * Checks if caller `AssociativeArray`'s value recorded with a given `key` - * is managed. - * - * Managed values will be automatically deallocated once they are removed - * (or overwritten) from the caller `AssociativeArray`. - * - * @return `true` if value recorded with a given `key` is managed - * and `false` otherwise; - * if value is missing (`none` or there is not entry for the `key`), - * returns `false`. - */ -public final function bool IsManaged(AcediaObject key) -{ - local int bucketIndex, entryIndex; - if (FindEntryIndices(key, bucketIndex, entryIndex)) { - return hashTable[bucketIndex].entries[entryIndex].managed; - } - return false; -} - -/** - * Returns value recorded by a given key `key` in the caller - * `AssociativeArray`. - * - * Can return `none` if either stored values is `none` or there's no value - * recorded with a `key`. To check whether there is a record, corresponding to - * the `key` use `HasKey()` method. - * - * @param key Key for which to return value. - * @return Value, stored with given key `key`. If there is no value with - * such a key method will return `none`. - */ -public final function AcediaObject GetItem(AcediaObject key) -{ - local int bucketIndex, entryIndex; - if (FindEntryIndices(key, bucketIndex, entryIndex)) { - return hashTable[bucketIndex].entries[entryIndex].value; - } - return none; -} - -/** - * Returns entry corresponding to a given key `key` in the caller - * `AssociativeArray`. - * - * @param key Key for which to return entry. - * @return Entry (key/value pair + indicator of whether values was managed - * by `AssociativeArray`) with the given key `key`. - */ -public final function Entry GetEntry(AcediaObject key) -{ - local Entry emptyEntry; - local int bucketIndex, entryIndex; - if (!FindEntryIndices(key, bucketIndex, entryIndex)) { - return emptyEntry; - } - return hashTable[bucketIndex].entries[entryIndex]; -} - -/** - * Returns entry corresponding to a given key `key` in the caller - * `AssociativeArray`, removing it from the caller `AssociativeArray`. - * - * Returned value is no longer managed by the `AssociativeArray` (if it was) - * and must be deallocated once you do not need them anymore. - * - * @param key Key for which to return entry. - * @return Entry (key/value pair + indicator of whether values was managed - * by `AssociativeArray`) with the given key `key`. - */ -public final function Entry TakeEntry(AcediaObject key) -{ - local Entry entryToTake; - local int bucketIndex, entryIndex; - if (!FindEntryIndices(key, bucketIndex, entryIndex)) { - return entryToTake; - } - entryToTake = hashTable[bucketIndex].entries[entryIndex]; - hashTable[bucketIndex].entries.Remove(entryIndex, 1); - storedElementCount = Max(0, storedElementCount - 1); - UpdateHashTableSize(); - return entryToTake; -} - -/** - * Returns value recorded with a given key `key` in the caller - * `AssociativeArray`, removing it from the collection. - * - * Returned value is no longer managed by the `AssociativeArray` (if it was) - * and must be deallocated once you do not need it anymore. - * - * @param key Key for which to return value. - * @param freeKey Setting this to `true` will also free the key item was - * stored with. Passed argument `key` will not be deallocated, unless it is - * the exact same object as item's key inside caller collection. - * @return Value, stored with given key `key`. If there is no value with - * such a key method will return `none`. - */ -public final function AcediaObject TakeItem( - AcediaObject key, - optional bool freeKey) -{ - local Entry entry; - entry = TakeEntry(key); - if (freeKey) { - _.memory.Free(entry.key); - } - return entry.value; -} - -/** - * Records new `value` under the key `key` into the caller `AssociativeArray`. - * - * If this will override already existing managed record - old value will - * be automatically deallocated (unless they are the same object as a new one). - * If you wish to avoid this behavior - retrieve them with either of - * `TakeItem()` or `TakeEntry()` methods first. - * - * @param key Key by which new value will be referred to. - * @param value Value to store in the caller `AssociativeArray`. - * @return Caller `AssociativeArray` to allow for method chaining. - */ -public final function AssociativeArray SetItem( - AcediaObject key, - AcediaObject value, - optional bool managed) -{ - local Entry oldEntry, newEntry; - local int bucketIndex, entryIndex; - if (key == none) { - return self; - } - if (FindEntryIndices(key, bucketIndex, entryIndex)) { - oldEntry = hashTable[bucketIndex].entries[entryIndex]; - } - else { - storedElementCount += 1; - } - newEntry.key = key; - newEntry.keyLifeVersion = key.GetLifeVersion(); - newEntry.managed = managed; - newEntry.value = value; - if (value != none) { - newEntry.valueLifeVersion = value.GetLifeVersion(); - } - if ( oldEntry.managed && oldEntry.value != none - && newEntry.value != oldEntry.value) - { - oldEntry.value.FreeSelf(oldEntry.valueLifeVersion); - } - hashTable[bucketIndex].entries[entryIndex] = newEntry; - return self; -} - -/** - * Creates a new instance of class `valueClass` and records it's value with - * key `key` in the caller `AssociativeArray`. Value is recorded as managed. - * - * @param key Key by which new value will be referred to. - * @param valueClass Class of object to create. Will only be created if - * passed `key` is valid. - * @return Caller `AssociativeArray` to allow for method chaining. - */ -public final function AssociativeArray CreateItem( - AcediaObject key, - class valueClass) -{ - if (key == none) return self; - if (valueClass == none) return self; - - return SetItem(key, AcediaObject(_.memory.Allocate(valueClass)), true); -} - -/** - * Removes a value recorded with a given key `key`. - * Does nothing if entry with a given key does not exist. - * - * Removed values are deallocated if they are managed. If you wish to avoid - * that, use `TakeItem()` or `TakeEntry()` methods. - * - * @param key Key for which to remove value. - * @param deallocateKey Should key be also deallocated? - * @return Caller `AssociativeArray` to allow for method chaining. - */ -public final function AssociativeArray RemoveItem( - AcediaObject key, - optional bool deallocateKey) -{ - local Entry entryToRemove; - local int bucketIndex, entryIndex; - if (key == none) return self; - - if (!FindEntryIndices(key, bucketIndex, entryIndex)) { - return self; - } - entryToRemove = hashTable[bucketIndex].entries[entryIndex]; - hashTable[bucketIndex].entries.Remove(entryIndex, 1); - storedElementCount = Max(0, storedElementCount - 1); - UpdateHashTableSize(); - if (entryToRemove.managed && entryToRemove.value != none) { - entryToRemove.value.FreeSelf(entryToRemove.valueLifeVersion); - } - if (deallocateKey && entryToRemove.key != none) { - entryToRemove.key.FreeSelf(entryToRemove.keyLifeVersion); - } - return self; -} - -public function Empty(optional bool deallocateKeys) -{ - local int i, j; - local Collection subCollection; - local array nextEntries; - for (i = 0; i < hashTable.length; i += 1) - { - nextEntries = hashTable[i].entries; - for (j = 0; j < nextEntries.length; j += 1) - { - if (!nextEntries[j].managed) continue; - if (nextEntries[j].value == none) continue; - nextEntries[j].value.FreeSelf(nextEntries[j].valueLifeVersion); - } - if (deallocateKeys) - { - for (j = 0; j < nextEntries.length; j += 1) - { - if (nextEntries[j].key == none) { - continue; - } - if ( nextEntries[j].key.GetLifeVersion() - != nextEntries[j].keyLifeVersion) { - continue; - } - subCollection = Collection(nextEntries[j].value); - if (subCollection != none) { - subCollection.Empty(true); - } - if (nextEntries[j].key != none) { - nextEntries[j].key.FreeSelf(nextEntries[j].keyLifeVersion); - } - } - } - } - hashTable.length = 0; - storedElementCount = 0; - UpdateHashTableSize(); -} - -/** - * Returns key of all properties inside caller `AssociativeArray`. - * - * Collecting all keys from the `AssociativeArray` is O(). - * - * See also `CopyTextKeys()` methods. - * - * @return Array of all the caller `AssociativeArray`'s keys. - * This method does not return copies of keys, but actual keys instead - - * deallocating them will remove their item from - * the caller `AssociativeArray`. - */ -public final function array GetKeys() -{ - local int i, j; - local array result; - local array nextEntry; - for (i = 0; i < hashTable.length; i += 1) - { - CleanBucket(hashTable[i]); - nextEntry = hashTable[i].entries; - for (j = 0; j < nextEntry.length; j += 1) { - result[result.length] = nextEntry[j].key; - } - } - return result; -} - -/** - * Returns copies of `Text` key of all properties inside caller - * `AssociativeArray`. Keys that have a different class (even if they are - * a child class for `Text`) are not returned. - * - * This method exists to provide alternative to `GetKeys()` method that would - * return copies of keys instead of actually used references: we cannot make - * a copy of an arbitrary `AcediaObject`, but we can of `Text`. - * Which is also a most common type for the keys. - * - * Collecting all keys from the `AssociativeArray` is O(). - * - * @return Array of all the caller `AssociativeArray`'s keys that have exactly - * `Text` class. - */ -public final function array CopyTextKeys() -{ - local int i, j; - local Text nextKeyAsText; - local array result; - local array nextEntry; - for (i = 0; i < hashTable.length; i += 1) - { - CleanBucket(hashTable[i]); - nextEntry = hashTable[i].entries; - for (j = 0; j < nextEntry.length; j += 1) - { - nextKeyAsText = Text(nextEntry[j].key); - if (nextKeyAsText != none && nextKeyAsText.class == class'Text') - { - result[result.length] = nextKeyAsText.Copy(); - } - } - } - return result; -} - -/** - * Returns amount of elements in the caller `AssociativeArray`. - * - * Note that this value might overestimate real amount of values inside - * `AssociativeArray` in case some of the keys used for storage were - * deallocated by code outside of `AssociativeArray`. - * Such values might be eventually found and removed, but - * `AssociativeArray` does not provide any guarantees on when it's done. - */ -public final function int GetLength() -{ - return storedElementCount; -} - -/** - * Auxiliary method for iterator that increments given `Index` structure. - * - * @param previousIndex Index to increment. - * @return `true` if incremented index is pointing at a valid item, - * `false` if collection has ended. - */ -public final function bool IncrementIndex(out Index previousIndex) -{ - previousIndex.entryIndex += 1; - // Go forward through buckets until we find non-empty one - while (previousIndex.bucketIndex < hashTable.length) - { - CleanBucket(hashTable[previousIndex.bucketIndex]); - if ( previousIndex.entryIndex - < hashTable[previousIndex.bucketIndex].entries.length) - { - return true; - } - previousIndex.entryIndex = 0; - previousIndex.bucketIndex += 1; - } - return false; -} - -/** - * Auxiliary method for iterator that returns value corresponding to - * a given `Index` structure. - * - * @param index Index of item to return. - * @return `Entry` corresponding to a given index. If index is invalid - * (not pointing at any value for caller `AssociativeArray`) returns - * `Entry` with key and value set to `none`. - * Note that `none` can be returned because that is simply the value - * being stored. - */ -public final function Entry GetEntryByIndex(Index index) -{ - local Entry emptyEntry; - if (index.bucketIndex < 0) return emptyEntry; - if (index.bucketIndex >= hashTable.length) return emptyEntry; - if ( index.entryIndex < 0 - || index.entryIndex >= hashTable[index.bucketIndex].entries.length) { - return emptyEntry; - } - return hashTable[index.bucketIndex].entries[index.entryIndex]; -} - -protected function AcediaObject GetByText(BaseText key) -{ - return GetItem(key); -} - -/** - * Returns `bool` item at key `key`. If key is invalid or - * stores a non-`bool` value, returns `defaultValue`. - * - * Referred value must be stored as `BoolBox` or `BoolRef` - * (or one of their sub-classes) for this method to work. - * - * @param key Key of a `bool` item that `AssociativeArray` - * has to return. - * @param defaultValue Value to return if there is either no item recorded - * at `key` or it has a wrong type. - * @return `bool` value at `key` in the caller `AssociativeArray`. - * `defaultValue` if passed `key` is invalid or non-`bool` value - * is stored with it. - */ -public final function bool GetBool(AcediaObject key, optional bool defaultValue) -{ - local AcediaObject result; - local BoolBox asBox; - local BoolRef asRef; - result = GetItem(key); - if (result == none) { - return defaultValue; - } - asBox = BoolBox(result); - if (asBox != none) { - return asBox.Get(); - } - asRef = BoolRef(result); - if (asRef != none) { - return asRef.Get(); - } - return defaultValue; -} - -/** - * Changes `AssociativeArray`'s value at key `key` to `value` that will be - * recorded as either `BoolBox` or `BoolRef`, depending of `asRef` - * optional parameter. - * - * Inserted value will always be recorded as a managed value, i.e. it will be - * automatically deallocated when overwritten, removed or - * caller `AssociativeArray` is deallocated. - * - * @param key Key, at which to change the value. If `DynamicArray` is - * not long enough to hold it, it will be automatically expanded. - * If passed key is negative - method will do nothing. - * @param value Value to be set at a given key. - * @param asRef Given `bool` value will be recorded as immutable `BoolBox` - * by default (`asRef == false`). Setting this parameter to `true` will - * make this method record it as a mutable `BoolRef`. - * @return Reference to the caller `AssociativeArray` to allow for - * method chaining. - */ -public final function AssociativeArray SetBool( - AcediaObject key, - bool value, - optional bool asRef) -{ - if (asRef) { - SetItem(key, _.ref.bool(value), true); - } - else { - SetItem(key, _.box.bool(value), true); - } - return self; -} - -/** - * Returns `byte` item at key `key`. If key is invalid or - * stores a non-`byte` value, returns `defaultValue`. - * - * Referred value must be stored as `ByteBox` or `ByteBox` - * (or one of their sub-classes) for this method to work. - * - * @param key Key of a `byte` item that `AssociativeArray` - * has to return. - * @param defaultValue Value to return if there is either no item recorded - * at `key` or it has a wrong type. - * @return `byte` value at `key` in the caller `AssociativeArray`. - * `defaultValue` if passed `key` is invalid or non-`byte` value - * is stored with it. - */ -public final function byte GetByte(AcediaObject key, optional byte defaultValue) -{ - local AcediaObject result; - local ByteBox asBox; - local ByteRef asRef; - result = GetItem(key); - if (result == none) { - return defaultValue; - } - asBox = ByteBox(result); - if (asBox != none) { - return asBox.Get(); - } - asRef = ByteRef(result); - if (asRef != none) { - return asRef.Get(); - } - return defaultValue; -} - -/** - * Changes `AssociativeArray`'s value at key `key` to `value` that will be - * recorded as either `ByteBox` or `ByteBox`, depending of `asRef` - * optional parameter. - * - * Inserted value will always be recorded as a managed value, i.e. it will be - * automatically deallocated when overwritten, removed or - * caller `AssociativeArray` is deallocated. - * - * @param key Key, at which to change the value. If `DynamicArray` is - * not long enough to hold it, it will be automatically expanded. - * If passed key is negative - method will do nothing. - * @param value Value to be set at a given key. - * @param asRef Given `byte` value will be recorded as immutable `ByteBox` - * by default (`asRef == false`). Setting this parameter to `true` will - * make this method record it as a mutable `ByteBox`. - * @return Reference to the caller `AssociativeArray` to allow for - * method chaining. - */ -public final function AssociativeArray SetByte( - AcediaObject key, - byte value, - optional bool asRef) -{ - if (asRef) { - SetItem(key, _.ref.byte(value), true); - } - else { - SetItem(key, _.box.byte(value), true); - } - return self; -} - -/** - * Returns `int` item at key `key`. If key is invalid or - * stores a non-`int` value, returns `defaultValue`. - * - * Referred value must be stored as `IntBox` or `IntRef` - * (or one of their sub-classes) for this method to work. - * - * @param key Key of a `int` item that `AssociativeArray` - * has to return. - * @param defaultValue Value to return if there is either no item recorded - * at `key` or it has a wrong type. - * @return `int` value at `key` in the caller `AssociativeArray`. - * `defaultValue` if passed `key` is invalid or non-`int` value - * is stored with it. - */ -public final function int GetInt(AcediaObject key, optional int defaultValue) -{ - local AcediaObject result; - local IntBox asBox; - local IntRef asRef; - result = GetItem(key); - if (result == none) { - return defaultValue; - } - asBox = IntBox(result); - if (asBox != none) { - return asBox.Get(); - } - asRef = IntRef(result); - if (asRef != none) { - return asRef.Get(); - } - return defaultValue; -} - -/** - * Changes `AssociativeArray`'s value at key `key` to `value` that will be - * recorded as either `IntBox` or `IntRef`, depending of `asRef` - * optional parameter. - * - * Inserted value will always be recorded as a managed value, i.e. it will be - * automatically deallocated when overwritten, removed or - * caller `AssociativeArray` is deallocated. - * - * @param key Key, at which to change the value. If `DynamicArray` is - * not long enough to hold it, it will be automatically expanded. - * If passed key is negative - method will do nothing. - * @param value Value to be set at a given key. - * @param asRef Given `int` value will be recorded as immutable `IntBox` - * by default (`asRef == false`). Setting this parameter to `true` will - * make this method record it as a mutable `IntRef`. - * @return Reference to the caller `AssociativeArray` to allow for - * method chaining. - */ -public final function AssociativeArray SetInt( - AcediaObject key, - int value, - optional bool asRef) -{ - if (asRef) { - SetItem(key, _.ref.int(value), true); - } - else { - SetItem(key, _.box.int(value), true); - } - return self; -} - -/** - * Returns `float` item at key `key`. If key is invalid or - * stores a non-`float` value, returns `defaultValue`. - * - * Referred value must be stored as `FloatBox` or `FloatRef` - * (or one of their sub-classes) for this method to work. - * - * @param key Key of a `float` item that `AssociativeArray` - * has to return. - * @param defaultValue Value to return if there is either no item recorded - * at `key` or it has a wrong type. - * @return `float` value at `key` in the caller `AssociativeArray`. - * `defaultValue` if passed `key` is invalid or non-`float` value - * is stored with it. - */ -public final function float GetFloat( - AcediaObject key, - optional float defaultValue) -{ - local AcediaObject result; - local FloatBox asBox; - local FloatRef asRef; - result = GetItem(key); - if (result == none) { - return defaultValue; - } - asBox = FloatBox(result); - if (asBox != none) { - return asBox.Get(); - } - asRef = FloatRef(result); - if (asRef != none) { - return asRef.Get(); - } - return defaultValue; -} - -/** - * Changes `AssociativeArray`'s value at key `key` to `value` that will be - * recorded as either `FloatBox` or `FloatRef`, depending of `asRef` - * optional parameter. - * - * Inserted value will always be recorded as a managed value, i.e. it will be - * automatically deallocated when overwritten, removed or - * caller `AssociativeArray` is deallocated. - * - * @param key Key, at which to change the value. If `DynamicArray` is - * not long enough to hold it, it will be automatically expanded. - * If passed key is negative - method will do nothing. - * @param value Value to be set at a given key. - * @param asRef Given `float` value will be recorded as immutable `FloatBox` - * by default (`asRef == false`). Setting this parameter to `true` will - * make this method record it as a mutable `FloatRef`. - * @return Reference to the caller `AssociativeArray` to allow for - * method chaining. - */ -public final function AssociativeArray SetFloat( - AcediaObject key, - float value, - optional bool asRef) -{ - if (asRef) { - SetItem(key, _.ref.float(value), true); - } - else { - SetItem(key, _.box.float(value), true); - } - return self; -} - -/** - * Returns `Text` item stored at key `key`. If key is invalid or - * stores a non-`Text` value, returns `none`. - * - * Referred value must be stored as `Text` (or one of it's sub-classes, - * such as `MutableText`) for this method to work. - * - * @param key Key of a `Text` item that `AssociativeArray` - * has to return. - * @return `Text` value recorded with `key` in the caller `AssociativeArray`. - * `none` if passed `key` is invalid or non-`Text` value - * is stored with it. - */ -public final function Text GetText(AcediaObject key) -{ - return Text(GetItem(key)); -} - -/** - * Returns `AssociativeArray` item stored at key `key`. If key is invalid or - * stores a non-`AssociativeArray` value, returns `none`. - * - * Referred value must be stored as `AssociativeArray` - * (or one of it's sub-classes) for this method to work. - * - * @param key Key of an `AssociativeArray` item that caller `AssociativeArray` - * has to return. - * @return `AssociativeArray` value recorded with `key` in the caller - * `AssociativeArray`. `none` if passed `key` is invalid or - * non-`AssociativeArray` value is stored with it. - */ -public final function AssociativeArray GetAssociativeArray(AcediaObject key) -{ - return AssociativeArray(GetItem(key)); -} - -/** - * Returns `DynamicArray` item stored at key `key`. If key is invalid or - * stores a non-`DynamicArray` value, returns `none`. - * - * Referred value must be stored as `DynamicArray` - * (or one of it's sub-classes) for this method to work. - * - * @param key Key of a `DynamicArray` item that caller `AssociativeArray` - * has to return. - * @return `AssociativeArray` value recorded with `key` in the caller - * `AssociativeArray`. `none` if passed `key` is invalid or - * non-`AssociativeArray` value is stored with it. - */ -public final function DynamicArray GetDynamicArray(AcediaObject key) -{ - return DynamicArray(GetItem(key)); -} - -defaultproperties -{ - iteratorClass = class'AssociativeArrayIterator' - minimalCapacity = 0 - MINIMUM_SIZE = 50 - MAXIMUM_SIZE = 20000 - // `MINIMUM_DENSITY * 2 < MAXIMUM_DENSITY` must hold for `AssociativeArray` - // to work properly - MINIMUM_DENSITY = 0.25 - MAXIMUM_DENSITY = 0.75 -} \ No newline at end of file diff --git a/sources/Data/Collections/AssociativeArrayIterator.uc b/sources/Data/Collections/AssociativeArrayIterator.uc deleted file mode 100644 index 9e7ac77..0000000 --- a/sources/Data/Collections/AssociativeArrayIterator.uc +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Iterator for iterating over `AssociativeArray`'s items. - * Copyright 2020 Anton Tarasenko - *------------------------------------------------------------------------------ - * This file is part of Acedia. - * - * Acedia is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License, or - * (at your option) any later version. - * - * Acedia 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Acedia. If not, see . - */ -class AssociativeArrayIterator extends CollectionIterator - dependson(AssociativeArray); - -var private bool hasNotFinished; -var private AssociativeArray relevantCollection; -var private AssociativeArray.Index currentIndex; - -protected function Finalizer() -{ - relevantCollection = none; -} - -public function bool Initialize(Collection relevantArray) -{ - local AssociativeArray.Index emptyIndex; - currentIndex = emptyIndex; - relevantCollection = AssociativeArray(relevantArray); - if (relevantCollection == none) { - return false; - } - hasNotFinished = (relevantCollection.GetLength() > 0); - if (GetKey() == none) { - relevantCollection.IncrementIndex(currentIndex); - } - return true; -} - -public function Iter Next(optional bool skipNone) -{ - local int collectionLength; - if (!skipNone) - { - hasNotFinished = relevantCollection.IncrementIndex(currentIndex); - return self; - } - collectionLength = relevantCollection.GetLength(); - while (hasNotFinished) - { - hasNotFinished = relevantCollection.IncrementIndex(currentIndex); - if (relevantCollection.GetEntryByIndex(currentIndex).value != none) { - return self; - } - } - return self; -} - -public function AcediaObject Get() -{ - return relevantCollection.GetEntryByIndex(currentIndex).value; -} - -public function AcediaObject GetKey() -{ - return relevantCollection.GetEntryByIndex(currentIndex).key; -} - -public function bool HasFinished() -{ - return !hasNotFinished; -} - -defaultproperties -{ -} \ No newline at end of file diff --git a/sources/Data/Collections/Collection.uc b/sources/Data/Collections/Collection.uc index 8df12be..f5a98a0 100644 --- a/sources/Data/Collections/Collection.uc +++ b/sources/Data/Collections/Collection.uc @@ -29,11 +29,11 @@ var protected class iteratorClass; * * This method must return an item that `key` refers to with it's * textual content (not as an object itself). - * For example, `DynamicArray` parses it into unsigned number, while - * `AssociativeArray` uses it as a key directly. + * For example, `ArrayList` parses it into unsigned number, while + * `HashTable` uses it as a key directly. * * There is no requirement that all stored values must be reachable by - * this method (i.e. `AssociativeArray` only lets you access values with + * this method (i.e. `HashTable` only lets you access values with * `Text` keys). */ protected function AcediaObject GetByText(BaseText key); @@ -64,28 +64,18 @@ public final function CollectionIterator Iterate() /** * 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) {} +public function Empty() {} /** * Returns stored `AcediaObject` from the caller storage * (or from it's sub-storages) via given `JSONPointer` path. * * Acedia provides two collections: - * 1. `DynamicArray` is treated as a JSON array in the context of + * 1. `ArrayList` is treated as a JSON array in the context of * JSON pointers and passed variable names are treated as a `Text` * representation of it's integer indices; - * 2. `AssociativeArray` is treated as a JSON object in the context of + * 2. `HashTable` is treated as a JSON object in the context of * JSON pointers and passed variable names are treated as it's * `Text` keys (to refer to an element with an empty key, use "/", * since "" is treated as a JSON pointer and refers to @@ -95,8 +85,7 @@ public function Empty(optional bool deallocateKeys) {} * appropriately defining `GetByText()` protected method. * * There is no requirement that all stored values must be reachable by - * this method (i.e. `AssociativeArray` only lets you access values with - * `Text` keys). + * this method (i.e. `HashTable` only lets you access values with `Text` keys). * * @param jsonPointer Path, given by a JSON pointer. * @return An item `jsonPointerAsText` is referring to (according to the above @@ -106,16 +95,23 @@ public final function AcediaObject GetItemByJSON(JSONPointer jsonPointer) { local int segmentIndex; local Text nextSegment; - local AcediaObject result; - local Collection nextCollection; + local AcediaObject result, nextObject; + local Collection prevCollection, nextCollection; if (jsonPointer == none) return none; if (jsonPointer.GetLength() < 1) return self; nextCollection = self; + nextCollection.NewRef(); while (segmentIndex < jsonPointer.GetLength() - 1) { nextSegment = jsonPointer.GetComponent(segmentIndex); - nextCollection = Collection(nextCollection.GetByText(nextSegment)); + prevCollection = nextCollection; + nextObject = nextCollection.GetByText(nextSegment); + nextCollection = Collection(nextObject); + _.memory.Free(prevCollection); + if (nextCollection == none) { + _.memory.Free(nextObject); + } _.memory.Free(nextSegment); if (nextCollection == none) { break; @@ -147,10 +143,10 @@ public final function AcediaObject GetItemByJSON(JSONPointer jsonPointer) * collection itself). * * Acedia provides two collections: - * 1. `DynamicArray` is treated as a JSON array in the context of + * 1. `ArrayList` is treated as a JSON array in the context of * JSON pointers and passed variable names are treated as a `Text` * representation of it's integer indices; - * 2. `AssociativeArray` is treated as a JSON object in the context of + * 2. `HashTable` is treated as a JSON object in the context of * JSON pointers and passed variable names are treated as it's * `Text` keys (to refer to an element with an empty key, use "/", * since "" is treated as a JSON pointer and refers to @@ -160,7 +156,7 @@ public final function AcediaObject GetItemByJSON(JSONPointer jsonPointer) * appropriately defining `GetByText()` protected method. * * There is no requirement that all stored values must be reachable by - * this method (i.e. `AssociativeArray` only lets you access values with + * this method (i.e. `HashTable` only lets you access values with * `Text` keys). * * @param jsonPointer Path, given by a JSON pointer. @@ -200,22 +196,26 @@ public final function bool GetBoolBy( BaseText jsonPointerAsText, optional bool defaultValue) { - local AcediaObject result; + local bool result; + local AcediaObject resultObject; local BoolBox asBox; local BoolRef asRef; - result = GetItemBy(jsonPointerAsText); - if (result == none) { + + resultObject = GetItemBy(jsonPointerAsText); + if (resultObject == none) { return defaultValue; } - asBox = BoolBox(result); + result = defaultValue; + asBox = BoolBox(resultObject); if (asBox != none) { - return asBox.Get(); + result = asBox.Get(); } - asRef = BoolRef(result); + asRef = BoolRef(resultObject); if (asRef != none) { - return asRef.Get(); + result = asRef.Get(); } - return defaultValue; + _.memory.Free(resultObject); + return result; } /** @@ -238,22 +238,26 @@ public final function byte GetByteBy( BaseText jsonPointerAsText, optional byte defaultValue) { - local AcediaObject result; + local byte result; + local AcediaObject resultObject; local ByteBox asBox; local ByteRef asRef; - result = GetItemBy(jsonPointerAsText); - if (result == none) { + + resultObject = GetItemBy(jsonPointerAsText); + if (resultObject == none) { return defaultValue; } - asBox = ByteBox(result); + result = defaultValue; + asBox = ByteBox(resultObject); if (asBox != none) { - return asBox.Get(); + result = asBox.Get(); } - asRef = ByteRef(result); + asRef = ByteRef(resultObject); if (asRef != none) { - return asRef.Get(); + result = asRef.Get(); } - return defaultValue; + _.memory.Free(resultObject); + return result; } /** @@ -276,22 +280,26 @@ public final function int GetIntBy( BaseText jsonPointerAsText, optional int defaultValue) { - local AcediaObject result; + local int result; + local AcediaObject resultObject; local IntBox asBox; local IntRef asRef; - result = GetItemBy(jsonPointerAsText); - if (result == none) { + + resultObject = GetItemBy(jsonPointerAsText); + if (resultObject == none) { return defaultValue; } - asBox = IntBox(result); + result = defaultValue; + asBox = IntBox(resultObject); if (asBox != none) { - return asBox.Get(); + result = asBox.Get(); } - asRef = IntRef(result); + asRef = IntRef(resultObject); if (asRef != none) { - return asRef.Get(); + result = asRef.Get(); } - return defaultValue; + _.memory.Free(resultObject); + return result; } /** @@ -314,22 +322,26 @@ public final function float GetFloatBy( BaseText jsonPointerAsText, optional float defaultValue) { - local AcediaObject result; + local float result; + local AcediaObject resultObject; local FloatBox asBox; local FloatRef asRef; - result = GetItemBy(jsonPointerAsText); - if (result == none) { + + resultObject = GetItemBy(jsonPointerAsText); + if (resultObject == none) { return defaultValue; } - asBox = FloatBox(result); + result = defaultValue; + asBox = FloatBox(resultObject); if (asBox != none) { - return asBox.Get(); + result = asBox.Get(); } - asRef = FloatRef(result); + asRef = FloatRef(resultObject); if (asRef != none) { - return asRef.Get(); + result = asRef.Get(); } - return defaultValue; + _.memory.Free(resultObject); + return result; } /** @@ -352,22 +364,26 @@ public final function Vector GetVectorBy( BaseText jsonPointerAsText, optional Vector defaultValue) { - local AcediaObject result; + local Vector result; + local AcediaObject resultObject; local VectorBox asBox; local VectorRef asRef; - result = GetItemBy(jsonPointerAsText); - if (result == none) { + + resultObject = GetItemBy(jsonPointerAsText); + if (resultObject == none) { return defaultValue; } - asBox = VectorBox(result); + result = defaultValue; + asBox = VectorBox(resultObject); if (asBox != none) { - return asBox.Get(); + result = asBox.Get(); } - asRef = VectorRef(result); + asRef = VectorRef(resultObject); if (asRef != none) { - return asRef.Get(); + result = asRef.Get(); } - return defaultValue; + _.memory.Free(resultObject); + return result; } /** @@ -392,14 +408,16 @@ public final function string GetStringBy( { local AcediaObject result; local Basetext asText; + result = GetItemBy(jsonPointerAsText); if (result == none) { return defaultValue; } asText = BaseText(result); if (asText != none) { - return asText.ToString(); + return _.text.ToString(asText); } + _.memory.Free(result); return defaultValue; } @@ -425,14 +443,16 @@ public final function string GetFormattedStringBy( { local AcediaObject result; local Basetext asText; + result = GetItemBy(jsonPointerAsText); if (result == none) { return defaultValue; } asText = BaseText(result); if (asText != none) { - return asText.ToFormattedString(); + return _.text.ToFormattedString(asText); } + _.memory.Free(result); return defaultValue; } @@ -451,46 +471,16 @@ public final function string GetFormattedStringBy( */ public final function Text GetTextBy(BaseText jsonPointerAsText) { - return Text(GetItemBy(jsonPointerAsText)); -} - -/** - * Returns an `AssociativeArray` value stored (in the caller `Collection` or - * one of it's sub-collections) pointed by - * [JSON pointer](https://tools.ietf.org/html/rfc6901). - * See `GetItemBy()` for more information. - * - * Referred value must be stored as `AssociativeArray` - * (or one of it's sub-classes) for this method to work. - * - * @param jsonPointerAsText Description of a path to the - * `AssociativeArray` value. - * @return `AssociativeArray` value, stored at `jsonPointerAsText` or - * `none` if it is missing or has a different type. - */ -public final function AssociativeArray GetAssociativeArrayBy( - BaseText jsonPointerAsText) -{ - return AssociativeArray(GetItemBy(jsonPointerAsText)); -} + local Text asText; + local AcediaObject result; -/** - * Returns an `DynamicArray` value stored (in the caller `Collection` or - * one of it's sub-collections) pointed by - * [JSON pointer](https://tools.ietf.org/html/rfc6901). - * See `GetItemBy()` for more information. - * - * Referred value must be stored as `DynamicArray` - * (or one of it's sub-classes) for this method to work. - * - * @param jsonPointerAsText Description of a path to the - * `DynamicArray` value. - * @return `DynamicArray` value, stored at `jsonPointerAsText` or - * `none` if it is missing or has a different type. - */ -public final function DynamicArray GetDynamicArrayBy(BaseText jsonPointerAsText) -{ - return DynamicArray(GetItemBy(jsonPointerAsText)); + result = GetItemBy(jsonPointerAsText); + asText = Text(result); + if (asText != none) { + return asText; + } + _.memory.Free(result); + return none; } /** @@ -509,7 +499,16 @@ public final function DynamicArray GetDynamicArrayBy(BaseText jsonPointerAsText) public final function HashTable GetHashTableBy( BaseText jsonPointerAsText) { - return HashTable(GetItemBy(jsonPointerAsText)); + local HashTable asHashTable; + local AcediaObject result; + + result = GetItemBy(jsonPointerAsText); + asHashTable = HashTable(result); + if (asHashTable != none) { + return asHashTable; + } + _.memory.Free(result); + return none; } /** @@ -527,7 +526,16 @@ public final function HashTable GetHashTableBy( */ public final function ArrayList GetArrayListBy(BaseText jsonPointerAsText) { - return ArrayList(GetItemBy(jsonPointerAsText)); + local ArrayList asArrayList; + local AcediaObject result; + + result = GetItemBy(jsonPointerAsText); + asArrayList = ArrayList(result); + if (asArrayList != none) { + return asArrayList; + } + _.memory.Free(result); + return none; } /** @@ -549,22 +557,26 @@ public final function bool GetBoolByJSON( JSONPointer jsonPointer, optional bool defaultValue) { - local AcediaObject result; + local bool result; + local AcediaObject resultObject; local BoolBox asBox; local BoolRef asRef; - result = GetItemByJSON(jsonPointer); - if (result == none) { + + resultObject = GetItemByJSON(jsonPointer); + if (resultObject == none) { return defaultValue; } - asBox = BoolBox(result); + result = defaultValue; + asBox = BoolBox(resultObject); if (asBox != none) { - return asBox.Get(); + result = asBox.Get(); } - asRef = BoolRef(result); + asRef = BoolRef(resultObject); if (asRef != none) { - return asRef.Get(); + result = asRef.Get(); } - return defaultValue; + _.memory.Free(resultObject); + return result; } /** @@ -586,22 +598,26 @@ public final function byte GetByteByJSON( JSONPointer jsonPointer, optional byte defaultValue) { - local AcediaObject result; + local byte result; + local AcediaObject resultObject; local ByteBox asBox; local ByteRef asRef; - result = GetItemByJSON(jsonPointer); - if (result == none) { + + resultObject = GetItemByJSON(jsonPointer); + if (resultObject == none) { return defaultValue; } - asBox = ByteBox(result); + result = defaultValue; + asBox = ByteBox(resultObject); if (asBox != none) { - return asBox.Get(); + result = asBox.Get(); } - asRef = ByteRef(result); + asRef = ByteRef(resultObject); if (asRef != none) { - return asRef.Get(); + result = asRef.Get(); } - return defaultValue; + _.memory.Free(resultObject); + return result; } /** @@ -623,22 +639,26 @@ public final function int GetIntByJSON( JSONPointer jsonPointer, optional int defaultValue) { - local AcediaObject result; + local int result; + local AcediaObject resultObject; local IntBox asBox; local IntRef asRef; - result = GetItemByJSON(jsonPointer); - if (result == none) { + + resultObject = GetItemByJSON(jsonPointer); + if (resultObject == none) { return defaultValue; } - asBox = IntBox(result); + result = defaultValue; + asBox = IntBox(resultObject); if (asBox != none) { - return asBox.Get(); + result = asBox.Get(); } - asRef = IntRef(result); + asRef = IntRef(resultObject); if (asRef != none) { - return asRef.Get(); + result = asRef.Get(); } - return defaultValue; + _.memory.Free(resultObject); + return result; } /** @@ -660,22 +680,26 @@ public final function float GetFloatByJSON( JSONPointer jsonPointer, optional float defaultValue) { - local AcediaObject result; + local float result; + local AcediaObject resultObject; local FloatBox asBox; local FloatRef asRef; - result = GetItemByJSON(jsonPointer); - if (result == none) { + + resultObject = GetItemByJSON(jsonPointer); + if (resultObject == none) { return defaultValue; } - asBox = FloatBox(result); + result = defaultValue; + asBox = FloatBox(resultObject); if (asBox != none) { - return asBox.Get(); + result = asBox.Get(); } - asRef = FloatRef(result); + asRef = FloatRef(resultObject); if (asRef != none) { - return asRef.Get(); + result = asRef.Get(); } - return defaultValue; + _.memory.Free(resultObject); + return result; } /** @@ -697,22 +721,26 @@ public final function Vector GetVectorByJSON( JSONPointer jsonPointer, optional Vector defaultValue) { - local AcediaObject result; + local Vector result; + local AcediaObject resultObject; local VectorBox asBox; local VectorRef asRef; - result = GetItemByJSON(jsonPointer); - if (result == none) { + + resultObject = GetItemByJSON(jsonPointer); + if (resultObject == none) { return defaultValue; } - asBox = VectorBox(result); + result = defaultValue; + asBox = VectorBox(resultObject); if (asBox != none) { - return asBox.Get(); + result = asBox.Get(); } - asRef = VectorRef(result); + asRef = VectorRef(resultObject); if (asRef != none) { - return asRef.Get(); + result = asRef.Get(); } - return defaultValue; + _.memory.Free(resultObject); + return result; } /** @@ -736,14 +764,16 @@ public final function string GetStringByJSON( { local AcediaObject result; local BaseText asText; + result = GetItemByJSON(jsonPointer); if (result == none) { return defaultValue; } asText = BaseText(result); if (asText != none) { - return asText.ToString(); + return _.text.ToString(asText); } + _.memory.Free(result); return defaultValue; } @@ -768,14 +798,16 @@ public final function string GetFormattedStringByJSON( { local AcediaObject result; local BaseText asText; + result = GetItemByJSON(jsonPointer); if (result == none) { return defaultValue; } asText = BaseText(result); if (asText != none) { - return asText.ToFormattedString(); + return _.text.ToFormattedString(asText); } + _.memory.Free(result); return defaultValue; } @@ -793,43 +825,16 @@ public final function string GetFormattedStringByJSON( */ public final function Text GetTextByJSON(JSONPointer jsonPointer) { - return Text(GetItemByJSON(jsonPointer)); -} - -/** - * Returns an `AssociativeArray` value stored (in the caller `Collection` or - * one of it's sub-collections) pointed by JSON pointer. - * See `GetItemByJSON()` for more information. - * - * Referred value must be stored as `AssociativeArray` - * (or one of it's sub-classes) for this method to work. - * - * @param jsonPointer JSON path to the `AssociativeArray` value. - * @return `AssociativeArray` value, stored at `jsonPointerAsText` or - * `none` if it is missing or has a different type. - */ -public final function AssociativeArray GetAssociativeArrayByJSON( - JSONPointer jsonPointer) -{ - return AssociativeArray(GetItemByJSON(jsonPointer)); -} + local AcediaObject result; + local Text asText; -/** - * Returns an `DynamicArray` value stored (in the caller `Collection` or - * one of it's sub-collections) pointed by JSON pointer. - * See `GetItemByJSON()` for more information. - * - * Referred value must be stored as `DynamicArray` - * (or one of it's sub-classes) for this method to work. - * - * @param jsonPointer JSON path to the `DynamicArray` value. - * @return `DynamicArray` value, stored at `jsonPointerAsText` or - * `none` if it is missing or has a different type. - */ -public final function DynamicArray GetDynamicArrayByJSON( - JSONPointer jsonPointer) -{ - return DynamicArray(GetItemByJSON(jsonPointer)); + result = GetItemByJSON(jsonPointer); + asText = Text(result); + if (asText != none) { + return asText; + } + _.memory.Free(result); + return none; } /** @@ -847,7 +852,16 @@ public final function DynamicArray GetDynamicArrayByJSON( public final function HashTable GetHashTableByJSON( JSONPointer jsonPointer) { - return HashTable(GetItemByJSON(jsonPointer)); + local AcediaObject result; + local HashTable asHashTable; + + result = GetItemByJSON(jsonPointer); + asHashTable = HashTable(result); + if (asHashTable != none) { + return asHashTable; + } + _.memory.Free(result); + return none; } /** @@ -865,7 +879,16 @@ public final function HashTable GetHashTableByJSON( public final function ArrayList GetArrayListByJSON( JSONPointer jsonPointer) { - return ArrayList(GetItemByJSON(jsonPointer)); + local AcediaObject result; + local ArrayList asArrayList; + + result = GetItemByJSON(jsonPointer); + asArrayList = ArrayList(result); + if (asArrayList != none) { + return asArrayList; + } + _.memory.Free(result); + return none; } defaultproperties diff --git a/sources/Data/Collections/CollectionsAPI.uc b/sources/Data/Collections/CollectionsAPI.uc index 8aa141e..87b0877 100644 --- a/sources/Data/Collections/CollectionsAPI.uc +++ b/sources/Data/Collections/CollectionsAPI.uc @@ -91,80 +91,6 @@ public final function HashTable EmptyHashTable() return HashTable(_.memory.Allocate(class'HashTable')); } -/** - * Creates a new `DynamicArray`, optionally filling it with objects from - * a given native array. - * - * @param objectArray Objects to place inside created `DynamicArray`; - * if empty (by default) - new, empty `DynamicArray` will be returned. - * Objects will be added in the same order as in `objectArray`. - * @param managed Flag that indicates whether objects from - * `objectArray` argument should be added as managed. - * By default `false` - they would not be managed. - * @return New `DynamicArray`, optionally filled with contents of - * `objectArray`. Guaranteed to be not `none` and to not contain any items - * outside of `objectArray`. - */ -public final function DynamicArray NewDynamicArray( - array objectArray, - optional bool managed) -{ - local int i; - local DynamicArray result; - result = DynamicArray(_.memory.Allocate(class'DynamicArray')); - for (i = 0; i < objectArray.length; i += 1) { - result.AddItem(objectArray[i], managed); - } - return result; -} - -/** - * Creates a new empty `DynamicArray`. - * - * @return New empty instance of `DynamicArray`. - */ -public final function DynamicArray EmptyDynamicArray() -{ - return DynamicArray(_.memory.Allocate(class'DynamicArray')); -} - -/** - * Creates a new `AssociativeArray`, optionally filling it with entries - * (key/value pairs) from a given native array. - * - * @param entriesArray Entries (key/value pairs) to place inside created - * `AssociativeArray`; if empty (by default) - new, - * empty `AssociativeArray` will be returned. - * @param managed Flag that indicates whether values from - * `entriesArray` argument should be added as managed. - * By default `false` - they would not be managed. - * @return New `AssociativeArray`, optionally filled with contents of - * `entriesArray`. Guaranteed to be not `none` and to not contain any items - * outside of `entriesArray`. - */ -public final function AssociativeArray NewAssociativeArray( - array entriesArray, - optional bool managed) -{ - local int i; - local AssociativeArray result; - result = AssociativeArray(_.memory.Allocate(class'AssociativeArray')); - for (i = 0; i < entriesArray.length; i += 1) { - result.SetItem(entriesArray[i].key, entriesArray[i].value, managed); - } - return result; -} - -/** - * Creates a new empty `AssociativeArray`. - * - * @return New empty instance of `AssociativeArray`. - */ -public final function AssociativeArray EmptyAssociativeArray() -{ - return AssociativeArray(_.memory.Allocate(class'AssociativeArray')); -} - defaultproperties { } \ No newline at end of file diff --git a/sources/Data/Collections/DynamicArray.uc b/sources/Data/Collections/DynamicArray.uc deleted file mode 100644 index 0a723e5..0000000 --- a/sources/Data/Collections/DynamicArray.uc +++ /dev/null @@ -1,817 +0,0 @@ -/** - * Dynamic array object for storing arbitrary types of data. Generic - * storage is achieved by using `AcediaObject` as the stored type. Native - * variable types such as `int`, `bool`, etc. can be stored by boxing them into - * `AcediaObject`s. - * Appropriate classes and APIs for their construction are provided for - * main primitive types and can be extended to any custom `struct`. - * Copyright 2020 - 2021 Anton Tarasenko - *------------------------------------------------------------------------------ - * This file is part of Acedia. - * - * Acedia is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License, or - * (at your option) any later version. - * - * Acedia 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Acedia. If not, see . - */ -class DynamicArray extends Collection; - -// Actual storage of all our data. -var private array storedObjects; -// `managedFlags[i] > 0` iff `contents[i]` is a managed object. -// Invariant `managedFlags.length == contents.length` should be enforced by -// all methods. -var private array managedFlags; -// Recorded `lifeVersions` of all stored objects. -// Invariant `lifeVersions.length == contents.length` should be enforced by -// all methods. -var private array lifeVersions; - -// Free array data -protected function Finalizer() -{ - Empty(); -} - -// Method, used to compare array values at different indices. -// Does not check boundary conditions, so make sure passed indices are valid. -private function bool AreEqual(AcediaObject object1, AcediaObject object2) -{ - if (object1 == none && object2 == none) return true; - if (object1 == none || object2 == none) return false; - - return object1.IsEqual(object2); -} - -/** - * Returns current length of dynamic `DynamicArray`. - * Cannot fail. - * - * @return Returns length of the caller `DynamicArray`. - * Guaranteed to be non-negative. - */ -public final function int GetLength() -{ - return storedObjects.length; -} - -/** - * Changes length of the caller `DynamicArray`. - * If `DynamicArray` size is increased as a result - added items will be - * filled with `none`s. - * If `DynamicArray` size is decreased - erased managed items will be - * automatically deallocated. - * - * @param newLength New length of an `DynamicArray`. - * If negative value is passes - method will do nothing. - * @return Reference to the caller `DynamicArray` to allow for method chaining. - */ -public final function DynamicArray SetLength(int newLength) -{ - local int i; - if (newLength < 0) { - return self; - } - for (i = newLength; i < storedObjects.length; i += 1) { - FreeManagedItem(i); - } - storedObjects.length = newLength; - managedFlags.length = newLength; - lifeVersions.length = newLength; - return self; -} - -/** - * Deallocates an item at a given index `index`, if it's managed. - * Does not check `DynamicArray` bounds for `index`, so you must ensure that - * `index` is valid. - * - * @param index Index of the managed item to deallocate. - * @return Reference to the caller `DynamicArray` to allow for method chaining. - */ -protected final function DynamicArray FreeManagedItem(int index) -{ - if (storedObjects[index] == none) return self; - if (!storedObjects[index].IsAllocated()) return self; - if (managedFlags[index] <= 0) return self; - if (lifeVersions[index] != storedObjects[index].GetLifeVersion()) { - return self; - } - if ( storedObjects[index] != none && managedFlags[index] > 0 - && lifeVersions[index] == storedObjects[index].GetLifeVersion()) - { - storedObjects[index].FreeSelf(); - storedObjects[index] = none; - } - return self; -} - -public function Empty(optional bool deallocateKeys) -{ - local int i; - local Collection subCollection; - if (deallocateKeys) - { - for (i = 0; i < storedObjects.length; i += 1) - { - subCollection = Collection(GetItem(i)); - if (subCollection != none) { - subCollection.Empty(true); - } - } - } - SetLength(0); -} - -/** - * Adds `amountOfNewItems` empty (`none`) items at the end of - * the `DynamicArray`. - * To insert items at an arbitrary array index, use `Insert()`. - * - * @param amountOfNewItems Amount of items to add at the end. - * If non-positive value is passed, - method does nothing. - * @return Reference to the caller `DynamicArray` to allow for method chaining. - */ -public final function DynamicArray Add(int amountOfNewItems) -{ - if (amountOfNewItems > 0) { - SetLength(storedObjects.length + amountOfNewItems); - } - return self; -} - -/** - * Inserts `count` empty (`none`) items into the `DynamicArray` - * at specified position. - * The indices of the following items are increased by `count` in order - * to make room for the new items. - * - * To add items at the end of an `DynamicArray`, consider using `Add()`, - * which is equivalent to `array.Insert(array.GetLength(), ...)`. - * - * @param index Index, where first inserted item will be located. - * Must belong to `[0; self.GetLength()]` inclusive interval, - * otherwise method does nothing. - * @param count Amount of new items to insert. - * Must be positive, otherwise method does nothing. - * @return Reference to the caller `DynamicArray` to allow for method chaining. - */ -public final function DynamicArray Insert(int index, int count) -{ - local int i; - local int swapIndex; - local int amountToShift; - - if (count <= 0) return self; - if (index < 0 || index > storedObjects.length) return self; - - amountToShift = storedObjects.length - index; - Add(count); - if (amountToShift == 0) { - return self; - } - for (i = 0; i < amountToShift; i += 1) - { - swapIndex = storedObjects.length - i - 1; - Swap(swapIndex, swapIndex - count); - } - return self; -} - -/** - * Swaps two `DynamicArray` items, along with information about their - * managed status. - * - * @param index1 Index of item to swap. - * @param index2 Index of item to swap. - */ -protected final function Swap(int index1, int index2) -{ - local AcediaObject temporaryItem; - local int temporaryNumber; - // Swap object - temporaryItem = storedObjects[index1]; - storedObjects[index1] = storedObjects[index2]; - storedObjects[index2] = temporaryItem; - // Swap life versions - temporaryNumber = lifeVersions[index1]; - lifeVersions[index1] = lifeVersions[index2]; - lifeVersions[index2] = temporaryNumber; - // Swap managed flags - temporaryNumber = managedFlags[index1]; - managedFlags[index1] = managedFlags[index2]; - managedFlags[index2] = temporaryNumber; -} - -/** - * Removes number items from the `DynamicArray`, starting at `index`. - * All items before position and from `index + count` on are not changed, - * but the item indices change, - they shift to close the gap, - * created by removed items. - * - * @param index Remove items starting from this index. - * Must belong to `[0; self.GetLength() - 1]` inclusive interval, - * otherwise method does nothing. - * @param count Removes at most this much items. - * Must be positive, otherwise method does nothing. - * Specifying more items than can be removed simply removes - * all items, starting from `index`. - * @return Reference to the caller `DynamicArray` to allow for method chaining. - */ -public final function DynamicArray Remove(int index, int count) -{ - local int i; - if (count <= 0) return self; - if (index < 0 || index > storedObjects.length) return self; - - count = Min(count, storedObjects.length - index); - for (i = 0; i < count; i += 1) { - FreeManagedItem(index + i); - } - storedObjects.Remove(index, count); - managedFlags.Remove(index, count); - lifeVersions.Remove(index, count); - return self; -} - -/** - * Removes item at a given index, shifting all the items that come after - * one place backwards. - * - * @param index Remove items starting from this index. - * Must belong to `[0; self.GetLength() - 1]` inclusive interval, - * otherwise method does nothing. - * @return Reference to the caller `DynamicArray` to allow for method chaining. - */ -public final function DynamicArray RemoveIndex(int index) -{ - Remove(index, 1); - return self; -} - -/** - * Checks if caller `DynamicArray`'s value at index `index` is managed. - * - * Managed values will be automatically deallocated once they are removed - * (or overwritten) from the caller `DynamicArray`. - * - * @return `true` if value, recorded in caller `DynamicArray` at index `index` - * is managed and `false` otherwise. - * If `index` is invalid (outside of `DynamicArray` bounds) - * also returns `false`. - */ -public final function bool IsManaged(int index) -{ - if (index < 0) return false; - if (index >= storedObjects.length) return false; - if (storedObjects[index] == none) return false; - if (!storedObjects[index].IsAllocated()) return false; - if (storedObjects[index].GetLifeVersion() != lifeVersions[index]) { - return false; - } - - return (managedFlags[index] > 0); -} - -/** - * Returns item at `index` and replaces it with `none` inside `DynamicArray`. - * If index is invalid, returns `none`. - * - * If returned value was managed, it won't be deallocated - * and will stop being managed. - * - * @param index Index of an item that `DynamicArray` has to return. - * @return Either value at `index` in the caller `DynamicArray` or `none` if - * passed `index` is invalid. - */ -public final function AcediaObject TakeItem(int index) -{ - local AcediaObject result; - if (index < 0) return none; - if (index >= storedObjects.length) return none; - if (storedObjects[index] == none) return none; - if (!storedObjects[index].IsAllocated()) return none; - if (storedObjects[index].GetLifeVersion() != lifeVersions[index]) { - return none; - } - - result = storedObjects[index]; - storedObjects[index] = none; - managedFlags[index] = 0; - lifeVersions[index] = 0; - return result; -} - -/** - * Returns item at `index`. If index is invalid, returns `none`. - * - * @param index Index of an item that `DynamicArray` has to return. - * @return Either value at `index` in the caller `DynamicArray` or `none` if - * passed `index` is invalid. - */ -public final function AcediaObject GetItem(int index) -{ - if (index < 0) return none; - if (index >= storedObjects.length) return none; - if (storedObjects[index] == none) return none; - if (!storedObjects[index].IsAllocated()) return none; - if (storedObjects[index].GetLifeVersion() != lifeVersions[index]) { - return none; - } - - return storedObjects[index]; -} - -/** - * Changes `DynamicArray`'s value at `index` to `item`. - * - * @param index Index, at which to change the value. If `DynamicArray` is - * not long enough to hold it, it will be automatically expanded. - * If passed index is negative - method will do nothing. - * @param item Value to be set at a given index. - * @param managed Whether `item` should be managed by `DynamicArray`. - * By default (`false`) all items are not managed. - * @return Reference to the caller `DynamicArray` to allow for method chaining. - */ -public final function DynamicArray SetItem( - int index, - AcediaObject item, - optional bool managed) -{ - if (index < 0) { - return self; - } - if (index >= storedObjects.length) { - SetLength(index + 1); - } - else if (item != storedObjects[index]) { - FreeManagedItem(index); - } - storedObjects[index] = item; - managedFlags[index] = 0; - if (managed) { - managedFlags[index] = 1; - } - if (item != none) { - lifeVersions[index] = item.GetLifeVersion(); - } - return self; -} - -/** - * Creates a new instance of class `valueClass` and records it's value at index - * `index` in the caller `DynamicArray`. Value is recorded as managed. - * - * @param index Index, at which to change the value. If `DynamicArray` - * is not long enough to hold it, it will be automatically expanded. - * If passed index is negative - method will do nothing. - * @param valueClass Class of object to create. Will only be created if - * passed `index` is valid. - * @return Caller `DynamicArray` to allow for method chaining. - */ -public final function DynamicArray CreateItem( - int index, - class valueClass) -{ - if (index < 0) return self; - if (valueClass == none) return self; - - return SetItem(index, AcediaObject(_.memory.Allocate(valueClass)), true); -} - -/** - * Adds given `item` at the end of the `DynamicArray`, expanding it by - * one item. - * Cannot fail. - * - * @param item Item to be added at the end of the `DynamicArray`. - * @param managed Whether `item` should be managed by `DynamicArray`. - * By default (`false`) all items are not managed. - * @return Reference to the caller `DynamicArray` to allow for method chaining. - */ -public final function DynamicArray AddItem( - AcediaObject item, - optional bool managed) -{ - return SetItem(storedObjects.length, item, managed); -} - -/** - * Inserts given `item` at index `index` of the `DynamicArray`, - * shifting all the items starting from `index` one position to the right. - * Cannot fail. - * - * @param index Index at which to insert new item. Must belong to - * inclusive range `[0; self.GetLength()]`, otherwise method does nothing. - * @param item Item to insert. - * @param managed Whether `item` should be managed by `DynamicArray`. - * By default (`false`) all items are not managed. - * @return Reference to the caller `DynamicArray` to allow for method chaining. - */ -public final function DynamicArray InsertItem( - int index, - AcediaObject item, - optional bool managed) -{ - if (index < 0) return self; - if (index > storedObjects.length) return self; - - Insert(index, 1); - SetItem(index, item, managed); - return self; -} - -/** - * Returns all occurrences of `item` in the caller `DynamicArray` - * (optionally only first one). - * - * @param item Item that needs to be removed from a `DynamicArray`. - * @param onlyFirstItem Set to `true` to only remove first occurrence. - * By default `false`, which means all occurrences will be removed. - * @return Reference to the caller `DynamicArray` to allow for method chaining. - */ -public final function DynamicArray RemoveItem( - AcediaObject item, - optional bool onlyFirstItem) -{ - local int i; - while (i < storedObjects.length) - { - if (AreEqual(storedObjects[i], item)) - { - Remove(i, 1); - if (onlyFirstItem) { - return self; - } - } - else { - i += 1; - } - } - return self; -} - -/** - * Finds first occurrence of `item` in caller `DynamicArray` and returns - * it's index. - * - * @param item Item to find in `DynamicArray`. - * @return Index of first occurrence of `item` in caller `DynamicArray`. - * `-1` if `item` is not found. - */ -public final function int Find(AcediaObject item) -{ - local int i; - for (i = 0; i < storedObjects.length; i += 1) - { - if (AreEqual(storedObjects[i], item)) { - return i; - } - } - return -1; -} - -protected function AcediaObject GetByText(BaseText key) -{ - local int index, consumed; - local Parser parser; - parser = _.text.Parse(key); - parser.MUnsignedInteger(index,,, consumed); - if (!parser.Ok()) - { - parser.FreeSelf(); - return none; - } - parser.FreeSelf(); - return GetItem(index); -} - -/** - * Returns `bool` item at `index`. If index is invalid or - * stores a non-`bool` value, returns `defaultValue`. - * - * Referred value must be stored as `BoolBox` or `BoolRef` - * (or one of their sub-classes) for this method to work. - * - * @param index Index of a `bool` item that `DynamicArray` - * has to return. - * @param defaultValue Value to return if there is either no item recorded - * at `index` or it has a wrong type. - * @return `bool` value at `index` in the caller `DynamicArray`. - * `defaultValue` if passed `index` is invalid or non-`bool` value - * is stored there. - */ -public final function bool GetBool(int index, optional bool defaultValue) -{ - local AcediaObject result; - local BoolBox asBox; - local BoolRef asRef; - result = GetItem(index); - if (result == none) { - return defaultValue; - } - asBox = BoolBox(result); - if (asBox != none) { - return asBox.Get(); - } - asRef = BoolRef(result); - if (asRef != none) { - return asRef.Get(); - } - return defaultValue; -} - -/** - * Changes `DynamicArray`'s value at `index` to `value` that will be recorded - * as either `BoolBox` or `BoolRef`, depending of `asRef` optional parameter. - * - * Inserted value will always be recorded as a managed value, i.e. it will be - * automatically deallocated when overwritten, removed or caller `DynamicArray` - * is deallocated. - * - * @param index Index, at which to change the value. If `DynamicArray` is - * not long enough to hold it, it will be automatically expanded. - * If passed index is negative - method will do nothing. - * @param value Value to be set at a given index. - * @param asRef Given `bool` value will be recorded as immutable `BoolBox` - * by default (`asRef == false`). Setting this parameter to `true` will - * make this method record it as a mutable `BoolRef`. - * @return Reference to the caller `DynamicArray` to allow for method chaining. - */ -public final function DynamicArray SetBool( - int index, - bool value, - optional bool asRef) -{ - if (asRef) { - SetItem(index, _.ref.bool(value), true); - } - else { - SetItem(index, _.box.bool(value), true); - } - return self; -} - -/** - * Returns `byte` item at `index`. If index is invalid or - * stores a non-`byte` value, returns `defaultValue`. - * - * Referred value must be stored as `ByteBox` or `ByteRef` - * (or one of their sub-classes) for this method to work. - * - * @param index Index of a `byte` item that `DynamicArray` - * has to return. - * @param defaultValue Value to return if there is either no item recorded - * at `index` or it has a wrong type. - * @return `byte` value at `index` in the caller `DynamicArray`. - * `defaultValue` if passed `index` is invalid or non-`byte` value - * is stored there. - */ -public final function byte GetByte(int index, optional byte defaultValue) -{ - local AcediaObject result; - local ByteBox asBox; - local ByteRef asRef; - result = GetItem(index); - if (result == none) { - return defaultValue; - } - asBox = ByteBox(result); - if (asBox != none) { - return asBox.Get(); - } - asRef = ByteRef(result); - if (asRef != none) { - return asRef.Get(); - } - return defaultValue; -} - -/** - * Changes `DynamicArray`'s value at `index` to `value` that will be recorded - * as either `ByteBox` or `ByteRef`, depending of `asRef` optional parameter. - * - * Inserted value will always be recorded as a managed value, i.e. it will be - * automatically deallocated when overwritten, removed or caller `DynamicArray` - * is deallocated. - * - * @param index Index, at which to change the value. If `DynamicArray` is - * not long enough to hold it, it will be automatically expanded. - * If passed index is negative - method will do nothing. - * @param value Value to be set at a given index. - * @param asRef Given `byte` value will be recorded as immutable `ByteBox` - * by default (`asRef == false`). Setting this parameter to `true` will - * make this method record it as a mutable `ByteRef`. - * @return Reference to the caller `DynamicArray` to allow for method chaining. - */ -public final function DynamicArray SetByte( - int index, - byte value, - optional bool asRef) -{ - if (asRef) { - SetItem(index, _.ref.byte(value), true); - } - else { - SetItem(index, _.box.byte(value), true); - } - return self; -} - -/** - * Returns `int` item at `index`. If index is invalid or - * stores a non-`int` value, returns `defaultValue`. - * - * Referred value must be stored as `IntBox` or `IntRef` - * (or one of their sub-classes) for this method to work. - * - * @param index Index of a `int` item that `DynamicArray` - * has to return. - * @param defaultValue Value to return if there is either no item recorded - * at `index` or it has a wrong type. - * @return `int` value at `index` in the caller `DynamicArray`. - * `defaultValue` if passed `index` is invalid or non-`int` value - * is stored there. - */ -public final function int GetInt(int index, optional int defaultValue) -{ - local AcediaObject result; - local IntBox asBox; - local IntRef asRef; - result = GetItem(index); - if (result == none) { - return defaultValue; - } - asBox = IntBox(result); - if (asBox != none) { - return asBox.Get(); - } - asRef = IntRef(result); - if (asRef != none) { - return asRef.Get(); - } - return defaultValue; -} - -/** - * Changes `DynamicArray`'s value at `index` to `value` that will be recorded - * as either `IntBox` or `IntRef`, depending of `asRef` optional parameter. - * - * Inserted value will always be recorded as a managed value, i.e. it will be - * automatically deallocated when overwritten, removed or caller `DynamicArray` - * is deallocated. - * - * @param index Index, at which to change the value. If `DynamicArray` is - * not long enough to hold it, it will be automatically expanded. - * If passed index is negative - method will do nothing. - * @param value Value to be set at a given index. - * @param asRef Given `int` value will be recorded as immutable `IntBox` - * by default (`asRef == false`). Setting this parameter to `true` will - * make this method record it as a mutable `IntRef`. - * @return Reference to the caller `DynamicArray` to allow for method chaining. - */ -public final function DynamicArray SetInt( - int index, - int value, - optional bool asRef) -{ - if (asRef) { - SetItem(index, _.ref.int(value), true); - } - else { - SetItem(index, _.box.int(value), true); - } - return self; -} - -/** - * Returns `float` item at `index`. If index is invalid or - * stores a non-`int` value, returns `defaultValue`. - * - * Referred value must be stored as `FloatBox` or `FloatRef` - * (or one of their sub-classes) for this method to work. - * - * @param index Index of a `float` item that `DynamicArray` - * has to return. - * @param defaultValue Value to return if there is either no item recorded - * at `index` or it has a wrong type. - * @return `float` value at `index` in the caller `DynamicArray`. - * `defaultValue` if passed `index` is invalid or non-`float` value - * is stored there. - */ -public final function float GetFloat(int index, optional float defaultValue) -{ - local AcediaObject result; - local FloatBox asBox; - local FloatRef asRef; - result = GetItem(index); - if (result == none) { - return defaultValue; - } - asBox = FloatBox(result); - if (asBox != none) { - return asBox.Get(); - } - asRef = FloatRef(result); - if (asRef != none) { - return asRef.Get(); - } - return defaultValue; -} - -/** - * Changes `DynamicArray`'s value at `index` to `value` that will be recorded - * as either `FloatBox` or `FloatRef`, depending of `asRef` optional parameter. - * - * Inserted value will always be recorded as a managed value, i.e. it will be - * automatically deallocated when overwritten, removed or caller `DynamicArray` - * is deallocated. - * - * @param index Index, at which to change the value. If `DynamicArray` is - * not long enough to hold it, it will be automatically expanded. - * If passed index is negative - method will do nothing. - * @param value Value to be set at a given index. - * @param asRef Given `float` value will be recorded as immutable `FloatBox` - * by default (`asRef == false`). Setting this parameter to `true` will - * make this method record it as a mutable `FloatRef`. - * @return Reference to the caller `DynamicArray` to allow for method chaining. - */ -public final function DynamicArray SetFloat( - int index, - float value, - optional bool asRef) -{ - if (asRef) { - SetItem(index, _.ref.float(value), true); - } - else { - SetItem(index, _.box.float(value), true); - } - return self; -} - -/** - * Returns `Text` item at `index`. If index is invalid or - * stores a non-`Text` value, returns `none`. - * - * Referred value must be stored as `Text` (or one of it's sub-classes, - * such as `MutableText`) for this method to work. - * - * @param index Index of a `Text` item that `DynamicArray` has to return. - * @return `Text` value at `index` in the caller `DynamicArray`. - * `none` if passed `index` is invalid or non-`Text` value - * is stored there. - */ -public final function Text GetText(int index) -{ - return Text(GetItem(index)); -} - -/** - * Returns `AssociativeArray` item at `index`. If index is invalid or - * stores a non-`AssociativeArray` value, returns `none`. - * - * Referred value must be stored as `AssociativeArray` - * (or one of it's sub-classes) for this method to work. - * - * @param index Index of an `AssociativeArray` item that `DynamicArray` - * has to return. - * @return `AssociativeArray` value at `index` in the caller `DynamicArray`. - * `none` if passed `index` is invalid or non-`AssociativeArray` value - * is stored there. - */ -public final function AssociativeArray GetAssociativeArray(int index) -{ - return AssociativeArray(GetItem(index)); -} - -/** - * Returns `DynamicArray` item at `index`. If index is invalid or - * stores a non-`DynamicArray` value, returns `none`. - * - * Referred value must be stored as `Text` (or one of it's sub-classes, - * such as `MutableText`) for this method to work. - * - * @param index Index of a `DynamicArray` item that caller `DynamicArray` - * has to return. - * @return `DynamicArray` value at `index` in the caller `DynamicArray`. - * `none` if passed `index` is invalid or non-`DynamicArray` value - * is stored there. - */ -public final function DynamicArray GetDynamicArray(int index) -{ - return DynamicArray(GetItem(index)); -} - -defaultproperties -{ - iteratorClass = class'DynamicArrayIterator' -} \ No newline at end of file diff --git a/sources/Data/Collections/DynamicArrayIterator.uc b/sources/Data/Collections/DynamicArrayIterator.uc deleted file mode 100644 index b6c48d4..0000000 --- a/sources/Data/Collections/DynamicArrayIterator.uc +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Iterator for iterating over `DynamicArray`'s items. - * Copyright 2020 Anton Tarasenko - *------------------------------------------------------------------------------ - * This file is part of Acedia. - * - * Acedia is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License, or - * (at your option) any later version. - * - * Acedia 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Acedia. If not, see . - */ -class DynamicArrayIterator extends CollectionIterator; - -var private DynamicArray relevantCollection; -var private int currentIndex; - -protected function Finalizer() -{ - relevantCollection = none; -} - -public function bool Initialize(Collection relevantArray) -{ - currentIndex = 0; - relevantCollection = DynamicArray(relevantArray); - if (relevantCollection == none) { - return false; - } - return true; -} - -public function Iter Next(optional bool skipNone) -{ - local int collectionLength; - if (!skipNone) - { - currentIndex += 1; - return self; - } - collectionLength = relevantCollection.GetLength(); - while (currentIndex < collectionLength) - { - currentIndex += 1; - if (relevantCollection.GetItem(currentIndex) != none) { - return self; - } - } - return self; -} - -public function AcediaObject Get() -{ - return relevantCollection.GetItem(currentIndex); -} - -/** - * Note that for `DynamicArrayIterator` this method produces a new `IntBox` - * object each time and requires manual deallocation. - */ -public function AcediaObject GetKey() -{ - return _.box.int(currentIndex); -} - -public function bool HasFinished() -{ - return currentIndex >= relevantCollection.GetLength(); -} - -defaultproperties -{ -} \ No newline at end of file diff --git a/sources/Data/Collections/HashTable.uc b/sources/Data/Collections/HashTable.uc index 1e56de1..2435a88 100644 --- a/sources/Data/Collections/HashTable.uc +++ b/sources/Data/Collections/HashTable.uc @@ -453,7 +453,7 @@ public final function HashTable RemoveItem(AcediaObject key) return self; } -public function Empty(optional bool deprecated) +public function Empty() { local int i, j; local array nextEntries; @@ -1155,54 +1155,6 @@ public final function Text GetText(AcediaObject key) return result; } -/** - * Returns `AssociativeArray` item stored at key `key`. If key is invalid or - * stores a non-`AssociativeArray` value, returns `none`. - * - * Referred value must be stored as `AssociativeArray` - * (or one of it's sub-classes) for this method to work. - * - * @param key Key of an `AssociativeArray` item that caller `HashTable` - * has to return. - * @return `AssociativeArray` value recorded with `key` in the caller - * `HashTable`. `none` if passed `key` is invalid or - * non-`AssociativeArray` value is stored with it. - */ -public final function AssociativeArray GetAssociativeArray(AcediaObject key) -{ - local AssociativeArray result; - - result = AssociativeArray(BorrowItem(key)); - if (result != none) { - result.NewRef(); - } - return result; -} - -/** - * Returns `DynamicArray` item stored at key `key`. If key is invalid or - * stores a non-`DynamicArray` value, returns `none`. - * - * Referred value must be stored as `DynamicArray` - * (or one of it's sub-classes) for this method to work. - * - * @param key Key of a `DynamicArray` item that caller `HashTable` - * has to return. - * @return `DynamicArray` value recorded with `key` in the caller - * `HashTable`. `none` if passed `key` is invalid or - * non-`DynamicArray` value is stored with it. - */ -public final function DynamicArray GetDynamicArray(AcediaObject key) -{ - local DynamicArray result; - - result = DynamicArray(BorrowItem(key)); - if (result != none) { - result.NewRef(); - } - return result; -} - /** * Returns `HashTable` item stored at key `key`. If key is invalid or * stores a non-`HashTable` value, returns `none`. diff --git a/sources/Data/Collections/HashTableIterator.uc b/sources/Data/Collections/HashTableIterator.uc index 0a578ce..4463a44 100644 --- a/sources/Data/Collections/HashTableIterator.uc +++ b/sources/Data/Collections/HashTableIterator.uc @@ -57,7 +57,7 @@ public function Iter LeaveOnlyNotNone() return self; } -public function Iter Next(optional bool deprecated) +public function Iter Next() { local int collectionLength; diff --git a/sources/Data/Collections/Tests/TEST_AssociativeArray.uc b/sources/Data/Collections/Tests/TEST_AssociativeArray.uc deleted file mode 100644 index bbc9ec0..0000000 --- a/sources/Data/Collections/Tests/TEST_AssociativeArray.uc +++ /dev/null @@ -1,488 +0,0 @@ -/** - * Set of tests for `AssociativeArray` class. - * Copyright 2020 Anton Tarasenko - *------------------------------------------------------------------------------ - * This file is part of Acedia. - * - * Acedia is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License, or - * (at your option) any later version. - * - * Acedia 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Acedia. If not, see . - */ -class TEST_AssociativeArray extends TestCase - abstract; - -protected static function TESTS() -{ - Test_GetSet(); - Test_HasKey(); - Test_GetKeys(); - Test_CopyTextKeys(); - Test_Remove(); - Test_CreateItem(); - Test_Empty(); - Test_Length(); - Test_Managed(); - Test_DeallocationHandling(); - Test_Take(); - Test_LargeArray(); -} - -protected static function Test_GetSet() -{ - local Text textObject; - local AssociativeArray array; - array = AssociativeArray(__().memory.Allocate(class'AssociativeArray')); - - Context("Testing getters and setters for items of `AssociativeArray`."); - Issue("`SetItem()` does not correctly set new items."); - textObject = __().text.FromString("value"); - array.SetItem(__().text.FromString("key"), textObject); - array.SetItem(__().box.int(13), __().text.FromString("value #2")); - array.SetItem(__().box.float(345.2), __().box.bool(true)); - TEST_ExpectTrue( Text(array.GetItem(__().box.int(13))).ToString() - == "value #2"); - TEST_ExpectTrue(array.GetItem(__().text.FromString("key")) == textObject); - TEST_ExpectTrue(BoolBox(array.GetItem(__().box.float(345.2))).Get()); - - Issue("`SetItem()` does not correctly overwrite new items."); - array.SetItem(__().text.FromString("key"), __().text.FromString("value")); - array.SetItem(__().box.int(13), __().box.int(11)); - TEST_ExpectFalse(array.GetItem(__().text.FromString("key")) == textObject); - TEST_ExpectTrue( Text(array.GetItem(__().text.FromString("key"))) - .ToString() == "value"); - TEST_ExpectTrue( IntBox(array.GetItem(__().box.int(13))).Get() - == 11); - - Issue("`GetItem()` does not return `none` for non-existing keys."); - TEST_ExpectNone(array.GetItem(__().box.int(12))); - TEST_ExpectNone(array.GetItem(__().box.byte(67))); - TEST_ExpectNone(array.GetItem(__().box.float(43.1234))); - TEST_ExpectNone(array.GetItem(__().text.FromString("Some random stuff"))); -} - -protected static function Test_HasKey() -{ - local AssociativeArray array; - array = AssociativeArray(__().memory.Allocate(class'AssociativeArray')); - Context("Testing `HasKey()` method for `AssociativeArray`."); - array.SetItem(__().text.FromString("key"), __().text.FromString("value")); - array.SetItem(__().box.int(13), __().text.FromString("value #2")); - array.SetItem(__().box.float(345.2), __().box.bool(true)); - Issue("`HasKey()` reports that added keys do not exist in" - @ "`AssociativeArray`."); - TEST_ExpectTrue(array.HasKey(__().text.FromString("key"))); - TEST_ExpectTrue(array.HasKey(__().box.int(13))); - TEST_ExpectTrue(array.HasKey(__().box.float(345.2))); - - Issue("`HasKey()` reports that `AssociativeArray` contains keys that" - @ "were never added."); - TEST_ExpectFalse(array.HasKey(none)); - TEST_ExpectFalse(array.HasKey(__().box.float(13))); - TEST_ExpectFalse(array.HasKey(__().box.byte(139))); -} - -protected static function Test_GetKeys() -{ - local int i; - local AcediaObject key1, key2, key3; - local array keys; - local AssociativeArray array; - array = AssociativeArray(__().memory.Allocate(class'AssociativeArray')); - Context("Testing `GetKeys()` method for `AssociativeArray`."); - key1 = __().text.FromString("key"); - key2 = __().box.int(13); - key3 = __().box.float(345.2); - array.SetItem(key1, __().text.FromString("value")); - array.SetItem(key2, __().text.FromString("value #2")); - array.SetItem(key3, __().box.bool(true)); - keys = array.GetKeys(); - Issue("`GetKeys()` returns array with wrong amount of elements."); - TEST_ExpectTrue(keys.length == 3); - - Issue("`GetKeys()` returns array with duplicate keys."); - TEST_ExpectTrue(keys[0] != keys[1]); - TEST_ExpectTrue(keys[0] != keys[2]); - TEST_ExpectTrue(keys[1] != keys[2]); - - Issue("`GetKeys()` returns array with incorrect keys."); - for (i = 0; i < 3; i += 1) { - TEST_ExpectTrue(keys[i] == key1 || keys[i] == key2 || keys[i] == key3); - } - - keys = array.RemoveItem(key1).GetKeys(); - Issue("`GetKeys()` returns array with incorrect keys after removing" - @ "an element."); - TEST_ExpectTrue(keys.length == 2); - TEST_ExpectTrue(keys[0] != keys[1]); - for (i = 0; i < 2; i += 1) { - TEST_ExpectTrue(keys[i] == key2 || keys[i] == key3); - } -} - -protected static function Test_CopyTextKeys() -{ - local array allKeys; - local array keys; - local AssociativeArray array; - array = AssociativeArray(__().memory.Allocate(class'AssociativeArray')); - Context("Testing `CopyTextKeys()` method for `AssociativeArray`."); - array.SetItem(__().text.FromString("key"), __().text.FromString("value")); - array.SetItem(__().box.int(13), __().text.FromString("value #2")); - array.SetItem(__().box.float(-925.274), __().text.FromString("value #2")); - array.SetItem(__().text.FromString("second key"), __().box.bool(true)); - - Issue("`CopyTextKeys()` does not return correct set of keys."); - keys = array.CopyTextKeys(); - TEST_ExpectTrue(keys.length == 2); - TEST_ExpectTrue( - (keys[0].ToString() == "key" - && keys[1].ToString() == "second key") - || (keys[0].ToString() == "second key" - && keys[1].ToString() == "key")); - - Issue("Deallocating keys returned by `CopyTextKeys()` affects their" - @ "source collection."); - allKeys = array.GetKeys(); - TEST_ExpectTrue(allKeys.length == 4); - TEST_ExpectNotNone(array.GetItem(__().text.FromString("key"))); - TEST_ExpectNotNone(array.GetItem(__().text.FromString("second key"))); - TEST_ExpectTrue( - array.GetText(__().text.FromString("key")).ToString() == "value"); -} - -protected static function Test_Remove() -{ - Context("Testing removing elements from `AssociativeArray`."); - SubTest_RemoveWithoutKeys(); - SubTest_RemoveWithKeys(); -} - -protected static function SubTest_RemoveWithoutKeys() -{ - local AcediaObject key1, key2, key3; - local array keys; - local AssociativeArray array; - array = AssociativeArray(__().memory.Allocate(class'AssociativeArray')); - key1 = __().text.FromString("some key"); - key2 = __().box.int(25); - key3 = __().box.float(0.07); - array.SetItem(key1, __().text.FromString("value")); - array.SetItem(key2, __().text.FromString("value #2")); - array.SetItem(key3, __().box.bool(true)); - Issue("Elements are not properly removed from `AssociativeArray`."); - array.RemoveItem(key1) - .RemoveItem(__().box.int(25)) - .RemoveItem(__().box.float(0.06)); - keys = array.GetKeys(); - TEST_ExpectTrue(array.GetLength() == 1); - TEST_ExpectTrue(keys.length == 1); - TEST_ExpectTrue(keys[0] == key3); - - Issue("Keys are deallocated upon removing elements from `AssociativeArray`" - @ "when they should not."); - TEST_ExpectTrue(key1.IsAllocated()); - TEST_ExpectTrue(key2.IsAllocated()); -} - -protected static function SubTest_RemoveWithKeys() -{ - local AcediaObject key1, key2, key3; - local array keys; - local AssociativeArray array; - array = AssociativeArray(__().memory.Allocate(class'AssociativeArray')); - key1 = __().text.FromString("some key"); - key2 = __().box.int(25); - key3 = __().box.float(0.07); - array.SetItem(key1, __().text.FromString("value")); - array.SetItem(key2, __().text.FromString("value #2")); - array.SetItem(key3, __().box.bool(true)); - Issue("Elements are not properly removed from `AssociativeArray`."); - array.RemoveItem(key1, true) - .RemoveItem(__().box.int(25), true) - .RemoveItem(__().box.float(0.06), true); - keys = array.GetKeys(); - TEST_ExpectTrue(array.GetLength() == 1); - TEST_ExpectTrue(keys.length == 1); - TEST_ExpectTrue(keys[0] == key3); - - Issue("Keys are not deallocated upon removing elements from" - @ "`AssociativeArray` when they should."); - TEST_ExpectFalse(key1.IsAllocated()); - TEST_ExpectFalse(key2.IsAllocated()); -} - -protected static function Test_CreateItem() -{ - local AssociativeArray array; - array = AssociativeArray(__().memory.Allocate(class'AssociativeArray')); - Context("Testing creating brand new items for `AssociativeArray`."); - Issue("`CreateItem()` incorrectly adds new values to the" - @ "`AssociativeArray`."); - array.CreateItem(__().text.FromString("key"), class'Text'); - array.CreateItem(__().box.float(17.895), class'IntRef'); - array.CreateItem(__().text.FromString("key #2"), class'BoolBox'); - TEST_ExpectTrue(Text(array.GetItem(__().text.FromString("key"))) - .ToString() == ""); - TEST_ExpectTrue( IntRef(array.GetItem(__().box.float(17.895))).Get() - == 0); - TEST_ExpectFalse(BoolBox(array.GetItem(__().text.FromString("key #2"))) - .Get()); - - Issue("`CreateItem()` incorrectly overrides existing values in the" - @ "`AssociativeArray`."); - array.SetItem(__().box.int(13), __().ref.int(7)); - array.CreateItem(__().box.int(13), class'StringRef'); - TEST_ExpectTrue( StringRef(array.GetItem(__().box.int(13))).Get() - == ""); - - class'MockItem'.default.objectCount = 0; - Issue("`CreateItem()` creates new object even if it cannot be recorded with" - @ "a given key."); - array.CreateItem(none, class'MockItem'); - TEST_ExpectTrue(class'MockItem'.default.objectCount == 0); -} - -protected static function Test_Empty() -{ - local AcediaObject key1, key2, key3; - local AssociativeArray array; - key1 = __().text.FromString("key"); - key2 = __().box.int(13); - key3 = __().box.float(345.2); - array = AssociativeArray(__().memory.Allocate(class'AssociativeArray')); - array.SetItem(key1, __().text.FromString("value")); - array.SetItem(key2, __().text.FromString("value #2"), true); - array.SetItem(key3, __().box.bool(true)); - - Context("Testing `Empty()` method for `AssociativeArray`."); - Issue("`AssociativeArray` still contains elements after being emptied."); - array.Empty(); - TEST_ExpectTrue(array.GetKeys().length == 0); - TEST_ExpectTrue(array.GetLength() == 0); - - Issue("`AssociativeArray` deallocated keys when not being told to do so."); - TEST_ExpectTrue(key1.IsAllocated()); - TEST_ExpectTrue(key2.IsAllocated()); - TEST_ExpectTrue(key3.IsAllocated()); - - Issue("`AssociativeArray` still contains elements after being emptied."); - array.SetItem(key1, __().text.FromString("value"), true); - array.SetItem(key2, __().text.FromString("value #2")); - array.SetItem(key3, __().box.bool(true), true); - array.Empty(true); - TEST_ExpectTrue(array.GetKeys().length == 0); - TEST_ExpectTrue(array.GetLength() == 0); - - Issue("`AssociativeArray` does not deallocate keys when told to do so."); - TEST_ExpectFalse(key1.IsAllocated()); - TEST_ExpectFalse(key2.IsAllocated()); - TEST_ExpectFalse(key3.IsAllocated()); -} - -protected static function Test_Length() -{ - local AssociativeArray array; - array = AssociativeArray(__().memory.Allocate(class'AssociativeArray')); - Context("Testing computing length of `AssociativeArray`."); - Issue("Length is not zero for newly created `AssociativeArray`."); - TEST_ExpectTrue(array.GetLength() == 0); - - Issue("Length is incorrectly computed after adding elements to" - @ "`AssociativeArray`."); - array.SetItem(__().text.FromString("key"), __().text.FromString("value")); - array.SetItem(__().box.int(4563), __().text.FromString("value #2")); - array.SetItem(__().box.float(3425.243), __().box.byte(23)); - TEST_ExpectTrue(array.GetLength() == 3); - - Issue("Length is incorrectly computed after removing elements from" - @ "`AssociativeArray`."); - array.RemoveItem(__().box.int(4563)); - TEST_ExpectTrue(array.GetLength() == 2); -} - -protected static function MockItem NewMockItem() -{ - return MockItem(__().memory.Allocate(class'MockItem')); -} - -protected static function Test_Managed() -{ - local AssociativeArray array; - class'MockItem'.default.objectCount = 0; - array = AssociativeArray(__().memory.Allocate(class'AssociativeArray')); - array.SetItem(__().box.int(0), NewMockItem()); - array.SetItem(__().box.int(1), NewMockItem()); - array.SetItem(__().box.int(2), NewMockItem()); - array.SetItem(__().box.int(3), NewMockItem(), true); - array.SetItem(__().box.int(4), NewMockItem(), true); - array.SetItem(__().box.int(5), NewMockItem(), true); - array.CreateItem(__().box.int(6), class'MockItem'); - array.CreateItem(__().box.int(7), class'MockItem'); - array.CreateItem(__().box.int(8), class'MockItem'); - Context("Testing how `AssociativeArray` deallocates managed objects."); - Issue("`RemoveItem()` incorrectly deallocates managed items."); - array.RemoveItem(__().box.int(0)); - array.RemoveItem(__().box.int(3)); - array.RemoveItem(__().box.int(6)); - TEST_ExpectTrue(class'MockItem'.default.objectCount == 7); - - Issue("Rewriting values with `SetItem()` incorrectly handles" - @ "managed items."); - array.SetItem(__().box.int(1), __().ref.int(28347)); - array.SetItem(__().box.int(4), __().ref.float(13.4)); - array.SetItem(__().box.int(7), __().ref.byte(94)); - TEST_ExpectTrue(class'MockItem'.default.objectCount == 5); - - Issue("Rewriting values with `CreateItem()` incorrectly handles" - @ "managed items."); - array.CreateItem(__().box.int(2), class'IntRef'); - array.CreateItem(__().box.int(5), class'StringRef'); - array.CreateItem(__().box.int(8), class'IntArrayBox'); - TEST_ExpectTrue(class'MockItem'.default.objectCount == 3); -} - -protected static function Test_DeallocationHandling() -{ - Context("Testing how `AssociativeArray` deals with external deallocation of" - @ "keys and managed objects."); - SubTest_DeallocationHandlingKeys(); - SubTest_DeallocationHandlingManagedObjects(); -} - -protected static function SubTest_DeallocationHandlingKeys() -{ - local IntBox key1; - local BoolBox key2; - local Text key3; - local AssociativeArray array; - local array keys; - key1 = __().box.int(3881); - key2 = __().box.bool(true); - key3 = __().text.FromString("Text key, bay bee"); - array = AssociativeArray(__().memory.Allocate(class'AssociativeArray')); - class'MockItem'.default.objectCount = 0; - array.SetItem(key1, NewMockItem()); - array.SetItem(key2, __().box.float(32.31), true); - array.SetItem(key3, NewMockItem(), true); - - Issue("Deallocating keys does not remove them from `AssociativeArray`."); - key1.FreeSelf(); - key3.FreeSelf(); - keys = array.GetKeys(); - TEST_ExpectTrue(keys.length == 1); - TEST_ExpectTrue(keys[0] == key2); - - Issue("`AssociativeArray` does not deallocate managed objects, even though" - @ "their keys were deallocated"); - TEST_ExpectTrue(class'MockItem'.default.objectCount < 2); - TEST_ExpectNone(array.GetItem(__().box.int(3881))); - - Issue("`AssociativeArray` deallocates unmanaged objects, when" - @ "their keys were deallocated"); - TEST_ExpectTrue(class'MockItem'.default.objectCount > 0); -} - -protected static function SubTest_DeallocationHandlingManagedObjects() -{ - local MockItem exampleItem; - local AssociativeArray array; - class'MockItem'.default.objectCount = 0; - exampleItem = NewMockItem(); - array = AssociativeArray(__().memory.Allocate(class'AssociativeArray')); - array.SetItem(__().box.int(-34), exampleItem, true); - array.SetItem(__().box.int(-7), exampleItem, true); - array.SetItem(__().box.int(23), NewMockItem()); - array.SetItem(__().box.int(242), NewMockItem(), true); - array.CreateItem(__().box.int(24532), class'MockItem'); - Issue("`AssociativeArray` does not return `none` even though stored object" - @ "was already deallocated."); - array.GetItem(__().box.int(23)).FreeSelf(); - TEST_ExpectTrue(class'MockItem'.default.objectCount == 3); - TEST_ExpectTrue(array.GetItem(__().box.int(23)) == none); - - Issue("Managed items are not deallocated when they are duplicated inside" - @ "`AssociativeArray`, but they should."); - array.RemoveItem(__().box.int(-7)); - // At this point we got rid of all the managed objects that were generated - // in `array` + deallocated `exampleObject`. - TEST_ExpectTrue(class'MockItem'.default.objectCount == 2); - TEST_ExpectTrue(array.GetItem(__().box.int(-34)) == none); -} - -protected static function Test_Take() -{ - local AssociativeArray.Entry entry; - local AssociativeArray array; - class'MockItem'.default.objectCount = 0; - array = AssociativeArray(__().memory.Allocate(class'AssociativeArray')); - array.SetItem(__().box.int(0), NewMockItem()); - array.SetItem(__().box.int(1), NewMockItem()); - array.SetItem(__().box.int(2), NewMockItem()); - array.SetItem(__().box.int(3), NewMockItem(), true); - array.SetItem(__().box.int(4), NewMockItem(), true); - array.SetItem(__().box.int(5), NewMockItem(), true); - array.CreateItem(__().box.int(6), class'MockItem'); - array.CreateItem(__().box.int(7), class'MockItem'); - array.CreateItem(__().box.int(8), class'MockItem'); - Context("Testing `TakeItem()` method of `AssociativeArray`."); - Issue("`TakeItem()` returns incorrect value."); - TEST_ExpectTrue(array.TakeItem(__().box.int(0)).class == class'MockItem'); - TEST_ExpectTrue(array.TakeItem(__().box.int(3)).class == class'MockItem'); - TEST_ExpectTrue(array.TakeItem(__().box.int(6)).class == class'MockItem'); - - Issue("`TakeEntry()` returns incorrect value."); - entry = array.TakeEntry(__().box.int(4)); - TEST_ExpectTrue(entry.key.class == class'IntBox'); - TEST_ExpectTrue(entry.value.class == class'MockItem'); - entry = array.TakeEntry(__().box.int(7)); - TEST_ExpectTrue(entry.key.class == class'IntBox'); - TEST_ExpectTrue(entry.value.class == class'MockItem'); - - Issue("Objects returned by `Take()` and `takeEntry()` are still managed by" - @ "`AssociativeArray`."); - array.Empty(); - TEST_ExpectTrue(class'MockItem'.default.objectCount == 7); -} - -protected static function Test_LargeArray() -{ - local int i; - local AcediaObject nextKey; - local AssociativeArray array; - Context("Testing storing large amount of elements in `AssociativeArray`."); - Issue("`AssociativeArray` cannot handle large amount of elements."); - array = AssociativeArray(__().memory.Allocate(class'AssociativeArray')); - for (i = 0; i < 2500; i += 1) { - if (i % 2 == 0) { - nextKey = __().text.FromString("var" @ i); - } - else { - nextKey = __().box.int(i * 56 - 435632); - } - array.SetItem(nextKey, __().ref.int(i)); - } - for (i = 0; i < 2500; i += 1) { - if (i % 2 == 0) { - nextKey = __().text.FromString("var" @ i); - } - else { - nextKey = __().box.int(i * 56 - 435632); - } - TEST_ExpectTrue(IntRef(array.GetItem(nextKey)).Get() == i); - } -} - -defaultproperties -{ - caseGroup = "Collections" - caseName = "AssociativeArray" -} \ No newline at end of file diff --git a/sources/Data/Collections/Tests/TEST_DynamicArray.uc b/sources/Data/Collections/Tests/TEST_DynamicArray.uc deleted file mode 100644 index f2b96fa..0000000 --- a/sources/Data/Collections/Tests/TEST_DynamicArray.uc +++ /dev/null @@ -1,322 +0,0 @@ -/** - * Set of tests for `DynamicArray` class. - * Copyright 2020 Anton Tarasenko - *------------------------------------------------------------------------------ - * This file is part of Acedia. - * - * Acedia is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License, or - * (at your option) any later version. - * - * Acedia 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Acedia. If not, see . - */ -class TEST_DynamicArray extends TestCase - abstract; - -protected static function TESTS() -{ - Test_GetSet(); - Test_CreateItem(); - Test_Length(); - Test_Empty(); - Test_AddInsert(); - Test_Remove(); - Test_Find(); - Test_Managed(); - Test_DeallocationHandling(); - Test_Take(); -} - -protected static function Test_GetSet() -{ - local DynamicArray array; - array = DynamicArray(__().memory.Allocate(class'DynamicArray')); - Context("Testing getters and setters for items of `DynamicArray`."); - Issue("Setters do not correctly expand `DynamicArray`."); - array.SetItem(0, __().box.int(-9)).SetItem(2, __().text.FromString("text")); - TEST_ExpectTrue(array.GetLength() == 3); - TEST_ExpectTrue(IntBox(array.GetItem(0)).Get() == -9); - TEST_ExpectNone(array.GetItem(1)); - TEST_ExpectTrue(Text(array.GetItem(2)).ToString() == "text"); - - Issue("Setters do not correctly overwrite items of `DynamicArray`."); - array.SetItem(1, __().box.float(34.76)); - array.SetItem(2, none); - TEST_ExpectTrue(array.GetLength() == 3); - TEST_ExpectTrue(IntBox(array.GetItem(0)).Get() == -9); - TEST_ExpectTrue(FloatBox(array.GetItem(1)).Get() == 34.76); - TEST_ExpectNone(array.GetItem(2)); -} - -protected static function Test_CreateItem() -{ - local DynamicArray array; - array = DynamicArray(__().memory.Allocate(class'DynamicArray')); - Context("Testing creating brand new items for `DynamicArray`."); - Issue("`CreateItem()` incorrectly adds new values to the" - @ "`DynamicArray`."); - array.CreateItem(1, class'Text'); - array.CreateItem(3, class'IntRef'); - array.CreateItem(4, class'BoolBox'); - TEST_ExpectNone(array.GetItem(0)); - TEST_ExpectNone(array.GetItem(2)); - TEST_ExpectTrue(Text(array.GetItem(1)).ToString() == ""); - TEST_ExpectTrue(IntRef(array.GetItem(3)).Get() == 0); - TEST_ExpectFalse(BoolBox(array.GetItem(4)).Get()); - - Issue("`CreateItem()` incorrectly overrides existing values in the" - @ "`DynamicArray`."); - array.SetItem(5, __().ref.int(7)); - array.CreateItem(5, class'StringRef'); - TEST_ExpectTrue(StringRef(array.GetItem(5)).Get() == ""); - - class'MockItem'.default.objectCount = 0; - Issue("`CreateItem()` creates new object even if it cannot be recorded at" - @ "a given index."); - array.CreateItem(-1, class'MockItem'); - TEST_ExpectTrue(class'MockItem'.default.objectCount == 0); -} - -protected static function Test_Length() -{ - local DynamicArray array; - array = DynamicArray(__().memory.Allocate(class'DynamicArray')); - Context("Testing length getter and setter for `DynamicArray`."); - Issue("Length of just created `DynamicArray` is not zero."); - TEST_ExpectTrue(array.GetLength() == 0); - - Issue("`SetLength()` incorrectly changes length of the `DynamicArray`."); - array.SetLength(200).SetItem(198, __().box.int(25)); - TEST_ExpectTrue(array.GetLength() == 200); - TEST_ExpectTrue(IntBox(array.GetItem(198)).Get() == 25); - array.SetLength(0); - TEST_ExpectTrue(array.GetLength() == 0); - - Issue("Shrinking size of `DynamicArray` does not remove recorded items."); - array.SetLength(1000); - TEST_ExpectNone(array.GetItem(198)); -} - -protected static function Test_Empty() -{ - local DynamicArray array; - array = DynamicArray(__().memory.Allocate(class'DynamicArray')); - Context("Testing emptying `DynamicArray`."); - array.AddItem(__().box.int(1)).AddItem(__().box.int(3)) - .AddItem(__().box.int(1)).AddItem(__().box.int(3)); - Issue("`Empty()` does not produce an empty array."); - array.Empty(); - TEST_ExpectTrue(array.GetLength() == 0); -} - -protected static function Test_AddInsert() -{ - local DynamicArray array; - array = DynamicArray(__().memory.Allocate(class'DynamicArray')); - Context("Testing adding new items to `DynamicArray`."); - Issue("`Add()`/`AddItem()` incorrectly add new items to" - @ "the `DynamicArray`."); - array.AddItem(__().box.int(3)).Add(3).AddItem(__().box.byte(7)).Add(1); - TEST_ExpectTrue(array.GetLength() == 6); - TEST_ExpectNotNone(IntBox(array.GetItem(0))); - TEST_ExpectTrue(IntBox(array.GetItem(0)).Get() == 3); - TEST_ExpectNone(array.GetItem(1)); - TEST_ExpectNone(array.GetItem(2)); - TEST_ExpectNone(array.GetItem(3)); - TEST_ExpectNotNone(ByteBox(array.GetItem(4))); - TEST_ExpectTrue(ByteBox(array.GetItem(4)).Get() == 7); - TEST_ExpectNone(array.GetItem(5)); - - Issue("`Insert()`/`InsertItem()` incorrectly add new items to" - @ "the `DynamicArray`."); - array.Insert(2, 2).InsertItem(0, __().ref.bool(true)); - TEST_ExpectTrue(array.GetLength() == 9); - TEST_ExpectNotNone(BoolRef(array.GetItem(0))); - TEST_ExpectTrue(BoolRef(array.GetItem(0)).Get()); - TEST_ExpectNotNone(IntBox(array.GetItem(1))); - TEST_ExpectTrue(IntBox(array.GetItem(1)).Get() == 3); - TEST_ExpectNone(array.GetItem(2)); - TEST_ExpectNone(array.GetItem(6)); - TEST_ExpectNotNone(ByteBox(array.GetItem(7))); - TEST_ExpectTrue(ByteBox(array.GetItem(7)).Get() == 7); - TEST_ExpectNone(array.GetItem(8)); -} - -protected static function Test_Remove() -{ - local DynamicArray array; - array = DynamicArray(__().memory.Allocate(class'DynamicArray')); - Context("Testing removing items from `DynamicArray`."); - array.AddItem(__().box.int(1)).AddItem(__().box.int(3)) - .AddItem(__().box.int(1)).AddItem(__().box.int(3)) - .AddItem(__().box.int(5)).AddItem(__().box.int(2)) - .AddItem(__().box.int(4)).AddItem(__().box.int(7)) - .AddItem(__().box.int(5)).AddItem(__().box.int(1)) - .AddItem(__().box.int(5)).AddItem(__().box.int(0)); - Issue("`Remove()` incorrectly removes items from array."); - array.Remove(3, 2).Remove(0, 2).Remove(7, 9); - TEST_ExpectTrue(array.GetLength() == 7); - TEST_ExpectTrue(IntBox(array.GetItem(0)).Get() == 1); - TEST_ExpectTrue(IntBox(array.GetItem(1)).Get() == 2); - TEST_ExpectTrue(IntBox(array.GetItem(2)).Get() == 4); - TEST_ExpectTrue(IntBox(array.GetItem(3)).Get() == 7); - TEST_ExpectTrue(IntBox(array.GetItem(4)).Get() == 5); - TEST_ExpectTrue(IntBox(array.GetItem(5)).Get() == 1); - TEST_ExpectTrue(IntBox(array.GetItem(6)).Get() == 5); - - Issue("`RemoveItem()` incorrectly removes items from array."); - array.RemoveItem(__().box.int(1)).RemoveItem(__().box.int(5), true); - TEST_ExpectTrue(array.GetLength() == 4); - TEST_ExpectTrue(IntBox(array.GetItem(0)).Get() == 2); - TEST_ExpectTrue(IntBox(array.GetItem(1)).Get() == 4); - TEST_ExpectTrue(IntBox(array.GetItem(2)).Get() == 7); - TEST_ExpectTrue(IntBox(array.GetItem(3)).Get() == 5); - - Issue("`RemoveIndex()` incorrectly removes items from array."); - array.RemoveIndex(0).RemoveIndex(1).RemoveIndex(1); - TEST_ExpectTrue(array.GetLength() == 1); - TEST_ExpectTrue(IntBox(array.GetItem(0)).Get() == 4); -} - -protected static function Test_Find() -{ - local DynamicArray array; - array = DynamicArray(__().memory.Allocate(class'DynamicArray')); - Context("Testing searching for items in `DynamicArray`."); - array.AddItem(__().box.int(1)).AddItem(__().box.int(3)) - .AddItem(__().box.int(1)).AddItem(__().box.int(3)) - .AddItem(__().box.int(5)).AddItem(__().box.bool(true)) - .AddItem(none).AddItem(__().box.float(72.54)) - .AddItem(__().box.int(5)).AddItem(__().box.int(1)) - .AddItem(__().box.int(5)).AddItem(__().box.int(0)); - Issue("`Find()` does not properly find indices of existing items."); - TEST_ExpectTrue(array.Find(__().box.int(5)) == 4); - TEST_ExpectTrue(array.Find(__().box.int(1)) == 0); - TEST_ExpectTrue(array.Find(__().box.int(0)) == 11); - TEST_ExpectTrue(array.Find(__().box.float(72.54)) == 7); - TEST_ExpectTrue(array.Find(__().box.bool(true)) == 5); - TEST_ExpectTrue(array.Find(none) == 6); - - Issue("`Find()` does not return `-1` on missing values."); - TEST_ExpectTrue(array.Find(__().box.int(42)) == -1); - TEST_ExpectTrue(array.Find(__().box.float(72.543)) == -1); - TEST_ExpectTrue(array.Find(__().box.bool(false)) == -1); - TEST_ExpectTrue(array.Find(__().box.byte(128)) == -1); -} - -protected static function MockItem NewMockItem() -{ - return MockItem(__().memory.Allocate(class'MockItem')); -} - -// Creates array with mock objects, also zeroing their count. -// Managed items' count: 6, but 12 items total. -protected static function DynamicArray NewMockArray() -{ - local DynamicArray array; - class'MockItem'.default.objectCount = 0; - array = DynamicArray(__().memory.Allocate(class'DynamicArray')); - array.AddItem(NewMockItem(), true).AddItem(NewMockItem(), false) - .InsertItem(2, NewMockItem(), true).AddItem(NewMockItem(), true) - .AddItem(NewMockItem(), false).AddItem(NewMockItem(), true) - .InsertItem(6, NewMockItem(), false).AddItem(NewMockItem(), true) - .InsertItem(3, NewMockItem(), false).AddItem(NewMockItem(), false) - .InsertItem(10, NewMockItem(), true).AddItem(NewMockItem(), false); - return array; -} - -protected static function Test_Managed() -{ - local MockItem exampleItem; - local DynamicArray array; - exampleItem = NewMockItem(); - // Managed items' count: 6, but 12 items total. - array = NewMockArray(); - Context("Testing how `DynamicArray` deallocates managed objects."); - Issue("`Remove()` incorrectly deallocates managed items."); - // -2 managed items - array.Remove(3, 2).Remove(0, 2).Remove(7, 9); - TEST_ExpectTrue(class'MockItem'.default.objectCount == 10); - - Issue("`RemoveIndex()` incorrectly deallocates managed items."); - // -1 managed items - array.RemoveIndex(3).RemoveIndex(0); - TEST_ExpectTrue(class'MockItem'.default.objectCount == 9); - // -1 managed items - array.RemoveItem(exampleItem, true).RemoveItem(exampleItem, true); - TEST_ExpectTrue(class'MockItem'.default.objectCount == 8); - // -2 managed items - array.RemoveItem(exampleItem); - TEST_ExpectTrue(class'MockItem'.default.objectCount == 6); - - array = NewMockArray(); - Issue("Shrinking array with `SetLength()` incorrectly handles" - @ "managed items"); - // -4 managed items - array.SetLength(3); - TEST_ExpectTrue(class'MockItem'.default.objectCount == 8); - - Issue("Rewriting values with `SetItem()` incorrectly handles" - @ "managed items."); - // -2 managed items - array.SetItem(0, exampleItem, true); - array.SetItem(2, exampleItem, true); - TEST_ExpectTrue(class'MockItem'.default.objectCount == 6); -} - -protected static function Test_DeallocationHandling() -{ - local MockItem exampleItem; - local DynamicArray array; - exampleItem = NewMockItem(); - // Managed items' count: 6, but 12 items total. - array = NewMockArray(); - Context("Testing how `DynamicArray` deals with external deallocation of" - @ "managed objects."); - Issue("`DynamicArray` does not return `none` even though stored object" - @ "was already deallocated."); - array.GetItem(0).FreeSelf(); - TEST_ExpectTrue(class'MockItem'.default.objectCount == 11); - TEST_ExpectTrue(array.GetItem(0) == none); - - Issue("Managed items are not deallocated when they are duplicated inside" - @ "`DynamicArray`, but they should."); - array.SetItem(1, exampleItem, true).SetItem(2, exampleItem, true); - TEST_ExpectTrue(class'MockItem'.default.objectCount == 10); - array.SetLength(2); - // At this point we got rid of all the managed objects that were generated - // in `array` + deallocated `exampleObject`. - TEST_ExpectTrue(class'MockItem'.default.objectCount == 5); - TEST_ExpectTrue(array.GetItem(1) == none); -} - -protected static function Test_Take() -{ - local DynamicArray array; - Context("Testing `TakeItem()` method of `DynamicArray`."); - // Managed items' count: 6, but 12 items total. - array = NewMockArray(); - Issue("`TakeItem()` returns incorrect value."); - TEST_ExpectTrue(array.TakeItem(0).class == class'MockItem'); - TEST_ExpectTrue(array.TakeItem(3).class == class'MockItem'); - TEST_ExpectTrue(array.TakeItem(4).class == class'MockItem'); - TEST_ExpectTrue(array.TakeItem(6).class == class'MockItem'); - - Issue("Objects returned by `TakeItem()` are still managed by" - @ "`DynamicArray`."); - array.Empty(); - TEST_ExpectTrue(class'MockItem'.default.objectCount == 9); -} - -defaultproperties -{ - caseGroup = "Collections" - caseName = "DynamicArray" -} \ No newline at end of file diff --git a/sources/Data/Collections/Tests/TEST_IteratorOld.uc b/sources/Data/Collections/Tests/TEST_IteratorOld.uc deleted file mode 100644 index cb7bc4c..0000000 --- a/sources/Data/Collections/Tests/TEST_IteratorOld.uc +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Set of tests for `Iterator` classes. - * Copyright 2020 Anton Tarasenko - *------------------------------------------------------------------------------ - * This file is part of Acedia. - * - * Acedia is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License, or - * (at your option) any later version. - * - * Acedia 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Acedia. If not, see . - */ -class TEST_IteratorOld extends TestCase - abstract; - -var const int TESTED_ITEMS_AMOUNT; -var array items; -var array seenFlags; - -protected static function CreateItems() -{ - local int i; - ResetFlags(); - for (i = 0; i < default.TESTED_ITEMS_AMOUNT; i += 1) { - default.items[default.items.length] = __().ref.float(i*2 + 1/i); - } -} - -protected static function ResetFlags() -{ - default.seenFlags.length = 0; - default.seenFlags.length = default.TESTED_ITEMS_AMOUNT; -} - -protected static function DoTestIterator(Iter iter) -{ - local int i; - local int seenCount; - local AcediaObject nextObject; - ResetFlags(); - while (!iter.HasFinished()) - { - nextObject = iter.Get(); - for (i = 0; i < default.items.length; i += 1) - { - if (default.items[i] == nextObject) - { - if (default.seenFlags[i] == 0) { - seenCount += 1; - } - default.seenFlags[i] = 1; - continue; - } - } - iter.Next(); - } - TEST_ExpectTrue(seenCount == default.TESTED_ITEMS_AMOUNT); -} - -protected static function TESTS() -{ - // Prepare - CreateItems(); - // Test - Test_DynamicArray(); - Test_AssociativeArray(); -} - -protected static function Test_DynamicArray() -{ - local int i; - local CollectionIterator iter; - local DynamicArray array; - - array = DynamicArray(__().memory.Allocate(class'DynamicArray')); - iter = array.Iterate(); - Context("Testing iterator for `DynamicArray`"); - Issue("`DynamicArray` returns `none` iterator."); - TEST_ExpectNotNone(iter); - - Issue("Iterator for empty `DynamicArray` is not finished by default."); - TEST_ExpectTrue(iter.HasFinished()); - - Issue("Iterator for empty `DynamicArray` does not return `none` as" - @ "a current item."); - TEST_ExpectNone(iter.Get()); - TEST_ExpectNone(iter.Next().Get()); - - for (i = 0; i < default.items.length; i += 1) { - array.AddItem(default.items[i]); - } - iter = array.Iterate(); - Issue("`DynamicArray` returns `none` iterator."); - TEST_ExpectNotNone(iter); - - Issue("`DynamicArray`'s iterator iterates over incorrect set of items."); - DoTestIterator(iter); -} - -protected static function Test_AssociativeArray() -{ - local int i; - local CollectionIterator iter; - local AssociativeArray array; - array = AssociativeArray(__().memory.Allocate(class'AssociativeArray')); - iter = array.Iterate(); - Context("Testing iterator for `AssociativeArray`"); - Issue("`AssociativeArray` returns `none` iterator."); - TEST_ExpectNotNone(iter); - - Issue("Iterator for empty `AssociativeArray` is not finished by default."); - TEST_ExpectTrue(iter.HasFinished()); - - Issue("Iterator for empty `AssociativeArray` does not return `none` as" - @ "a current item."); - TEST_ExpectNone(iter.Get()); - TEST_ExpectNone(iter.Next().Get()); - - for (i = 0; i < default.items.length; i += 1) { - array.SetItem(__().box.int(i), default.items[i]); - } - iter = array.Iterate(); - Issue("`AssociativeArray` returns `none` iterator."); - TEST_ExpectNotNone(iter); - - Issue("`AssociativeArray`'s iterator iterates over incorrect set of" - @ "items."); - DoTestIterator(iter); -} - -defaultproperties -{ - caseGroup = "Collections" - caseName = "IteratorOld" - TESTED_ITEMS_AMOUNT = 100 -} \ No newline at end of file diff --git a/sources/Data/Database/DBAPI.uc b/sources/Data/Database/DBAPI.uc index a9ef625..df5f038 100644 --- a/sources/Data/Database/DBAPI.uc +++ b/sources/Data/Database/DBAPI.uc @@ -25,7 +25,7 @@ var private const class localDBClass; // Store all already loaded databases to make sure we do not create two // different `LocalDatabaseInstance` that are trying to make changes // separately. -var private AssociativeArray loadedLocalDatabases; +var private HashTable loadedLocalDatabases; var private LoggerAPI.Definition infoLocalDatabaseCreated; var private LoggerAPI.Definition infoLocalDatabaseDeleted; @@ -34,7 +34,7 @@ var private LoggerAPI.Definition infoLocalDatabaseLoaded; private final function CreateLocalDBMapIfMissing() { if (loadedLocalDatabases == none) { - loadedLocalDatabases = __().collections.EmptyAssociativeArray(); + loadedLocalDatabases = __().collections.EmptyHashTable(); } } @@ -126,6 +126,7 @@ public final function LocalDatabaseInstance NewLocal(BaseText databaseName) { local DBRecord rootRecord; local Text rootRecordName; + local Text databaseNameCopy; local LocalDatabase newConfig; local LocalDatabaseInstance newLocalDBInstance; @@ -138,13 +139,14 @@ public final function LocalDatabaseInstance NewLocal(BaseText databaseName) if (loadedLocalDatabases.HasKey(databaseName)) return none; newLocalDBInstance = LocalDatabaseInstance(_.memory.Allocate(localDBClass)); - loadedLocalDatabases.SetItem(databaseName.Copy(), newLocalDBInstance); + databaseNameCopy = databaseName.Copy(); + loadedLocalDatabases.SetItem(databaseNameCopy, newLocalDBInstance); rootRecord = class'DBRecord'.static.NewRecord(databaseName); rootRecordName = _.text.FromString(string(rootRecord.name)); newConfig.SetRootName(rootRecordName); newConfig.Save(); newLocalDBInstance.Initialize(newConfig, rootRecord); - _.logger.Auto(infoLocalDatabaseCreated).Arg(databaseName.Copy()); + _.logger.Auto(infoLocalDatabaseCreated).Arg(databaseNameCopy); _.memory.Free(rootRecordName); return newLocalDBInstance; } @@ -201,6 +203,7 @@ public final function LocalDatabaseInstance LoadLocal(BaseText databaseName) newLocalDBInstance.Initialize(newConfig, rootRecord); _.logger.Auto(infoLocalDatabaseLoaded).Arg(databaseName.Copy()); _.memory.Free(rootRecordName); + _.memory.Free(newLocalDBInstance); return newLocalDBInstance; } @@ -212,9 +215,16 @@ public final function LocalDatabaseInstance LoadLocal(BaseText databaseName) */ public final function bool ExistsLocal(BaseText databaseName) { - return LoadLocal(databaseName) != none; + local bool result; + local LocalDatabaseInstance instance; + + instance = LoadLocal(databaseName); + result = (instance != none); + _.memory.Free(instance); + return result; } +// TODO: deleted database must be marked as disposed + change tests too /** * Deletes local database with name `databaseName`. * @@ -224,17 +234,19 @@ public final function bool ExistsLocal(BaseText databaseName) */ public final function bool DeleteLocal(BaseText databaseName) { - local LocalDatabase localDatabaseConfig; - local LocalDatabaseInstance localDatabase; - local AssociativeArray.Entry dbEntry; + local LocalDatabase localDatabaseConfig; + local LocalDatabaseInstance localDatabase; + local HashTable.Entry dbEntry; if (databaseName == none) { return false; } CreateLocalDBMapIfMissing(); // To delete database we first need to load it localDatabase = LoadLocal(databaseName); - if (localDatabase != none) { + if (localDatabase != none) + { localDatabaseConfig = localDatabase.GetConfig(); + _.memory.Free(localDatabase); } dbEntry = loadedLocalDatabases.TakeEntry(databaseName); // Delete `LocalDatabaseInstance` before erasing the package, diff --git a/sources/Data/Database/Tests/TEST_LocalDatabase.uc b/sources/Data/Database/Tests/TEST_LocalDatabase.uc index 47fbd6f..fd4200a 100644 --- a/sources/Data/Database/Tests/TEST_LocalDatabase.uc +++ b/sources/Data/Database/Tests/TEST_LocalDatabase.uc @@ -243,6 +243,8 @@ protected static function Test_LoadingPrepared() SubTest_LoadingPreparedGetSizeNegative(db); SubTest_LoadingPreparedGetKeysSuccess(db); SubTest_LoadingPreparedGetKeysFail(db); + __().memory.Free(db); + __().memory.Free(db); } protected static function SubTest_LoadingPreparedSuccessRoot( @@ -491,6 +493,8 @@ protected static function Test_Writing() Issue("`DeleteLocal()` does not return `true` after deleting existing" @ "local database."); + __().memory.Free(db); // For `NewLocal()` call + __().memory.Free(db); // For `LoadLocal()` call TEST_ExpectTrue(__().db.DeleteLocal(P("TEST_DB"))); Issue("Newly created database is reported to still exist after deletion."); @@ -516,6 +520,7 @@ protected static function Test_Recreate() SubTest_WritingArrayIndicies(db); __().db.DeleteLocal(P("TEST_DB")); Issue("Newly created database is reported to still exist after deletion."); + __().memory.Free(db); TEST_ExpectFalse(__().db.ExistsLocal(P("TEST_DB"))); TEST_ExpectFalse(db.IsAllocated()); } @@ -540,14 +545,14 @@ protected static function HashTable GetJSONSubTemplateObject() { local Parser parser; parser = __().text.ParseString("{\"A\":\"simpleValue\",\"B\":11.12}"); - return HashTable(__().json.ParseWith(parser,, true)); + return HashTable(__().json.ParseWith(parser)); } protected static function ArrayList GetJSONSubTemplateArray() { local Parser parser; parser = __().text.ParseString("[true, null, \"huh\"]"); - return ArrayList(__().json.ParseWith(parser,, true)); + return ArrayList(__().json.ParseWith(parser)); } /* diff --git a/sources/Gameplay/KF1Frontend/BaseImplementation/EKFInventory.uc b/sources/Gameplay/KF1Frontend/BaseImplementation/EKFInventory.uc index 0ec096c..cff004b 100644 --- a/sources/Gameplay/KF1Frontend/BaseImplementation/EKFInventory.uc +++ b/sources/Gameplay/KF1Frontend/BaseImplementation/EKFInventory.uc @@ -579,7 +579,7 @@ public function bool Remove( local NativeActorRef pawnRef; local Inventory nativeInstance; local KFWeapon kfWeapon; - local DynamicArray removalList; + local ArrayList removalList; if (EAmmo(itemToRemove) != none) return false; nativeInstance = GetItemNativeInstance(itemToRemove); if (nativeInstance == none) return false; @@ -600,8 +600,8 @@ public function bool Remove( // guaranteed. // Only optimize this if this method will become // a bottleneck somewhere. - removalList = _.collections.EmptyDynamicArray(); - removalList.AddItem(_server.unreal.ActorRef(nativeInstance), true); + removalList = _.collections.EmptyArrayList(); + removalList.AddItem(_server.unreal.ActorRef(nativeInstance)); pawnRef = _server.unreal.ActorRef(pawn); result = RemoveInventoryArray( pawnRef, removalList, keepItem, forceRemoval, true); @@ -623,7 +623,7 @@ public function bool RemoveTemplate( local bool result; local Pawn pawn; local NativeActorRef pawnRef; - local DynamicArray removalList; + local ArrayList removalList; local class inventoryClass; local class weaponClass; if (template == none) return false; @@ -635,7 +635,7 @@ public function bool RemoveTemplate( if (pawn == none) return false; pawnRef = _server.unreal.ActorRef(pawn); - removalList = _.collections.EmptyDynamicArray(); + removalList = _.collections.EmptyArrayList(); // All removal works the same - form a "kill list", then remove // all `Inventory` at once with `RemoveInventoryArray` AddClassForRemoval(removalList, inventoryClass, forceRemoval, removeAll); @@ -654,7 +654,7 @@ public function bool RemoveTemplate( // Searches `EKFInventory`'s owner's inventory chain for items of class // `inventoryClass` and adds them to the `removalArray` (for later removal). private function AddClassForRemoval( - DynamicArray removalArray, + ArrayList removalArray, class inventoryClass, optional bool forceRemoval, optional bool removeAll) @@ -686,7 +686,7 @@ private function AddClassForRemoval( } if (canRemoveInventory) { - removalArray.AddItem(_server.unreal.ActorRef(nextInventory), true); + removalArray.AddItem(_server.unreal.ActorRef(nextInventory)); if (!removeAll) { break; } @@ -709,12 +709,12 @@ public function bool RemoveAll( local NativeActorRef pawnRef; local KFWeapon kfWeapon; local Inventory nextInventory; - local DynamicArray inventoryToRemove; + local ArrayList inventoryToRemove; pawn = GetOwnerPawn(); if (pawn == none) { return false; } - inventoryToRemove = _.collections.EmptyDynamicArray(); + inventoryToRemove = _.collections.EmptyArrayList(); nextInventory = pawn.inventory; while (nextInventory != none) { @@ -723,11 +723,8 @@ public function bool RemoveAll( && (forceRemoval || !kfWeapon.bKFNeverThrow); canRemoveItem = canRemoveItem || (includeHidden && Ammunition(nextInventory) == none); - if (canRemoveItem) - { - inventoryToRemove.AddItem( - _server.unreal.ActorRef(nextInventory), - true); + if (canRemoveItem) { + inventoryToRemove.AddItem(_server.unreal.ActorRef(nextInventory)); } nextInventory = nextInventory.inventory; } @@ -745,7 +742,7 @@ public function bool RemoveAll( // dropped/removed. private function bool RemoveInventoryArray( NativeActorRef ownerPawnRef, - DynamicArray itemsToRemove, + ArrayList itemsToRemove, bool keepItems, bool forceRemoval, bool completeRemoval) diff --git a/sources/Gameplay/KF1Frontend/World/KF1_EntityIterator.uc b/sources/Gameplay/KF1Frontend/World/KF1_EntityIterator.uc index 287c373..00962f2 100644 --- a/sources/Gameplay/KF1Frontend/World/KF1_EntityIterator.uc +++ b/sources/Gameplay/KF1Frontend/World/KF1_EntityIterator.uc @@ -101,7 +101,7 @@ private final function TryIterating() iterated = true; } -public function Iter Next(optional bool skipNone) +public function Iter Next() { if (!initialized) { return self; diff --git a/sources/Gameplay/KF1Frontend/World/KF1_TracingIterator.uc b/sources/Gameplay/KF1Frontend/World/KF1_TracingIterator.uc index 549802c..7767e99 100644 --- a/sources/Gameplay/KF1Frontend/World/KF1_TracingIterator.uc +++ b/sources/Gameplay/KF1Frontend/World/KF1_TracingIterator.uc @@ -126,7 +126,7 @@ private final function TryTracing() traced = true; } -public function Iter Next(optional bool skipNone) +public function Iter Next() { if (!initialized) { return self; diff --git a/sources/Logger/Logger.uc b/sources/Logger/Logger.uc index 7240897..b05c5c5 100644 --- a/sources/Logger/Logger.uc +++ b/sources/Logger/Logger.uc @@ -33,7 +33,7 @@ class Logger extends AcediaObject abstract; // Named loggers are stored here to avoid recreating them -var protected AssociativeArray loadedLoggers; +var protected HashTable loadedLoggers; // Should `Logger` display prefix indicating it's a log message from Acedia? var protected config bool acediaStamp; @@ -68,7 +68,7 @@ public final static function Logger GetLogger(BaseText loggerName) return none; } if (default.loadedLoggers == none) { - default.loadedLoggers = __().collections.EmptyAssociativeArray(); + default.loadedLoggers = __().collections.EmptyHashTable(); } loggerKey = loggerName.LowerCopy(); loggerInstance = Logger(default.loadedLoggers.GetItem(loggerKey)); diff --git a/sources/Text/JSON/JSONAPI.uc b/sources/Text/JSON/JSONAPI.uc index 7895440..44db0a2 100644 --- a/sources/Text/JSON/JSONAPI.uc +++ b/sources/Text/JSON/JSONAPI.uc @@ -91,11 +91,11 @@ public final function JSONPointer Pointer(optional BaseText pointerAsText) * * Compatible objects are `none` and any object that has one of the following * classes: `BoolBox`, `BoolRef`, `ByteBox`, `ByteRef`, `IntBox`, `IntRef`, - * `FloatBox`, `FloatRef`, `Text`, `MutableText`, `DynamicArray`, - * `AssociativeArray`. + * `FloatBox`, `FloatRef`, `Text`, `MutableText`, `ArrayList`, + * `HashTable`. * - * This method does not check whether objects stored inside `DynamicArray`, - * `AssociativeArray` are compatible. If they are not, they will normally be + * This method does not check whether objects stored inside `ArrayList`, + * `HashTable` are compatible. If they are not, they will normally be * defaulted to JSON null upon any conversion. */ public function bool IsCompatible(AcediaObject data) @@ -110,9 +110,7 @@ public function bool IsCompatible(AcediaObject data) || dataClass == class'IntBox' || dataClass == class'IntRef' || dataClass == class'FloatBox' || dataClass == class'FloatRef' || dataClass == class'Text' || dataClass == class'MutableText' - || dataClass == class'ArrayList' || dataClass == class'HashTable' - || dataClass == class'DynamicArray' - || dataClass == class'AssociativeArray'; + || dataClass == class'ArrayList' || dataClass == class'HashTable'; } /** @@ -598,75 +596,6 @@ public final function BaseText ParseString( return immutableTextValue; } -/** - * Uses given parser to parse a JSON array. - * - * This method will parse JSON values that are contained in parsed JSON array - * according to description given for `ParseWith()` method. - * - * It does not matter what content follows parsed value in the `parser`, - * method will be successful as long as it manages to parse correct - * JSON array (from the current `parser`'s position). - * - * To check whether parsing have failed, simply check if `parser` is in - * a failed state after the method call. - * - * @param parser Parser that method would use to parse JSON array - * from it's current position. It's confirmed state will not be changed. - * If parsing was successful it will point at the next available character. - * Parser will be in a failed state after this method iff - * parsing has failed. - * @param parseAsMutable `true` if you want this method to parse array's - * items as mutable values and `false` otherwise (as immutable ones). - * @return Parsed JSON array as `DynamicArray` if parsing was successful and - * `none` otherwise. To check for parsing success check the state of - * the `parser`. - */ -public final function DynamicArray ParseArrayWith( - Parser parser, - optional bool parseAsMutable) -{ - local bool parsingSucceeded; - local Parser.ParserState confirmedState; - local AcediaObject nextValue; - local array parsedValues; - if (parser == none) return none; - - confirmedState = - parser.Skip().Match(T(default.TOPEN_BRACKET)).GetCurrentState(); - while (parser.Ok() && !parser.HasFinished()) - { - confirmedState = parser.Skip().GetCurrentState(); - // Check for JSON array ending and ONLY THEN declare parsing - // is successful, not encountering '}' implies bad JSON format. - if (parser.Match(T(default.TCLOSE_BRACKET)).Ok()) { - parsingSucceeded = true; - break; - } - parser.RestoreState(confirmedState); - // Look for comma after each element - if (parsedValues.length > 0) - { - if (!parser.Match(T(default.TCOMMA)).Skip().Ok()) { - break; - } - confirmedState = parser.GetCurrentState(); - } - // Parse next value - nextValue = ParseWith(parser, parseAsMutable); - parsedValues[parsedValues.length] = nextValue; - if (!parser.Ok()) { - break; - } - } - if (parsingSucceeded) { - return _.collections.NewDynamicArray(parsedValues, true); - } - _.memory.FreeMany(parsedValues); - parser.Fail(); - return none; -} - /** * Uses given parser to parse a JSON array. * @@ -725,7 +654,7 @@ public final function ArrayList ParseArrayListWith( confirmedState = parser.GetCurrentState(); } // Parse next value - nextValue = ParseWith(parser, parseAsMutable, true); + nextValue = ParseWith(parser, parseAsMutable); parsedValues[parsedValues.length] = nextValue; if (!parser.Ok()) { break; @@ -741,79 +670,6 @@ public final function ArrayList ParseArrayListWith( return result; } -/** - * Uses given parser to parse a JSON object. - * - * This method will parse JSON values that are contained in parsed JSON object - * according to description given for `ParseWith()` method. - * - * It does not matter what content follows parsed value in the `parser`, - * method will be successful as long as it manages to parse correct - * JSON object (from the current `parser`'s position). - * - * To check whether parsing have failed, simply check if `parser` is in - * a failed state after the method call. - * - * @param parser Parser that method would use to parse JSON object - * from it's current position. It's confirmed state will not be changed. - * If parsing was successful it will point at the next available character. - * Parser will be in a failed state after this method iff - * parsing has failed. - * @param parseAsMutable `true` if you want this method to parse object's - * items as mutable values and `false` otherwise (as immutable ones). - * @return Parsed JSON object as `AssociativeArray` if parsing was successful - * and `none` otherwise. To check for parsing success check the state of - * the `parser`. - */ -public function AssociativeArray ParseObjectWith( - Parser parser, - optional bool parseAsMutable) -{ - local bool parsingSucceeded; - local Parser.ParserState confirmedState; - local array parsedEntries; - if (parser == none) return none; - - // Ensure that parser starts pointing at what looks like a JSON object - confirmedState = - parser.Skip().Match(T(default.TOPEN_BRACE)).GetCurrentState(); - if (!parser.Ok()) { - return none; - } - while (parser.Ok() && !parser.HasFinished()) - { - confirmedState = parser.Skip().GetCurrentState(); - // Check for JSON object ending and ONLY THEN declare parsing - // is successful, not encountering '}' implies bad JSON format. - if (parser.Match(T(default.TCLOSE_BRACE)).Ok()) - { - parsingSucceeded = true; - break; - } - parser.RestoreState(confirmedState); - // Look for comma after each key-value pair - if (parsedEntries.length > 0) - { - if (!parser.Match(T(default.TCOMMA)).Skip().Ok()) { - break; - } - confirmedState = parser.GetCurrentState(); - } - // Parse property - parsedEntries[parsedEntries.length] = - ParseProperty(parser, parseAsMutable); - if (!parser.Ok()) { - break; - } - } - if (parsingSucceeded) { - return _.collections.NewAssociativeArray(parsedEntries, true); - } - FreeEntries(parsedEntries); - parser.Fail(); - return none; -} - /** * Uses given parser to parse a JSON object. * @@ -892,20 +748,7 @@ public function HashTable ParseHashTableWith( return result; } -// Parses a JSON key-value pair (there must not be any leading spaces). -private function AssociativeArray.Entry ParseProperty( - Parser parser, - bool parseAsMutable) -{ - local MutableText nextKey; - local AssociativeArray.Entry entry; - parser.MStringLiteral(nextKey).Skip().Match(T(default.TCOLON)).Skip(); - entry.key = nextKey.Copy(); - nextKey.FreeSelf(); - entry.value = ParseWith(parser, parseAsMutable); - return entry; -} - +// TODO: ParseProperty // Parses a JSON key-value pair (there must not be any leading spaces). private function HashTable.Entry ParseHashTableProperty( Parser parser, @@ -915,21 +758,11 @@ private function HashTable.Entry ParseHashTableProperty( local HashTable.Entry entry; parser.MStringLiteral(nextKey).Skip().Match(T(default.TCOLON)).Skip(); entry.key = nextKey.IntoText(); - entry.value = ParseWith(parser, parseAsMutable, true); + entry.value = ParseWith(parser, parseAsMutable); return entry; } -// Auxiliary method for deallocating unneeded objects in entry pairs. -private function FreeEntries(array entries) -{ - local int i; - for (i = 0; i < entries.length; i += 1) - { - _.memory.Free(entries[i].key); - _.memory.Free(entries[i].value); - } -} - +// TODO: FreeEntries // Auxiliary method for deallocating unneeded objects in entry pairs. private function FreeHashTableEntries(array entries) { @@ -955,9 +788,9 @@ private function FreeHashTableEntries(array entries) * `parseAsMutable` parameter (boxes are immutable, refs are mutable); * 3. String values will be parsed as `Text`/`MutableText`, based on * `parseAsMutable` parameter; - * 4. Array values will be parsed as a `DynamicArray`, it's items parsed + * 4. Array values will be parsed as a `ArrayList`s, their items parsed * according to these rules (`parseAsMutable` parameter is propagated). - * 5. Object values will be parsed as a `AssociativeArray`, it's items + * 5. Object values will be parsed as a `HashTable`s, their items * parsed according to these rules (`parseAsMutable` parameter is * propagated) and recorded under the keys parsed into `Text`. * @@ -983,8 +816,7 @@ private function FreeHashTableEntries(array entries) */ public final function AcediaObject ParseWith( Parser parser, - optional bool parseAsMutable, - optional bool parseAsNew) + optional bool parseAsMutable) { local AcediaObject result; local Parser.ParserState initState; @@ -1008,35 +840,17 @@ public final function AcediaObject ParseWith( if (parser.Ok()) { return result; } - if (parseAsNew) - { - result = ParseArrayListWith( - parser.RestoreState(initState), - parseAsMutable); - if (parser.Ok()) { - return result; - } - result = ParseHashTableWith( - parser.RestoreState(initState), - parseAsMutable); - if (parser.Ok()) { - return result; - } + result = ParseArrayListWith( + parser.RestoreState(initState), + parseAsMutable); + if (parser.Ok()) { + return result; } - else - { - result = ParseArrayWith( - parser.RestoreState(initState), - parseAsMutable); - if (parser.Ok()) { - return result; - } - result = ParseObjectWith( - parser.RestoreState(initState), - parseAsMutable); - if (parser.Ok()) { - return result; - } + result = ParseHashTableWith( + parser.RestoreState(initState), + parseAsMutable); + if (parser.Ok()) { + return result; } return none; } @@ -1054,10 +868,10 @@ public final function AcediaObject ParseWith( * 3. Integer (`IntBox`/`IntRef`) and float (`FloatBox`/`FloatRef`) types * are printed into JSON number value; * 4. `Text` and `MutableText` are printed into JSON string value; - * 5. `DynamicArray` is printed into JSON array with `Print()` method + * 5. `ArrayList` is printed into JSON array with `Print()` method * applied to each of it's items. If some of them have not printable * types - "none" will be used as a fallback. - * 6. `AssociativeArray` is printed into JSON object with `Print()` method + * 6. `HashTable` is printed into JSON object with `Print()` method * applied to each of it's items. Only items with `Text` keys are * printed, the rest is omitted. If some of them have not printable * types - "none" will be used as a fallback. @@ -1094,66 +908,15 @@ public final function MutableText Print(AcediaObject toPrint) || toPrint.class == class'MutableText') { return DisplayText(BaseText(toPrint)); } - if (toPrint.class == class'DynamicArray') { - return PrintArray(DynamicArray(toPrint)); - } if (toPrint.class == class'ArrayList') { return PrintArrayList(ArrayList(toPrint)); } - if (toPrint.class == class'AssociativeArray') { - return PrintObject(AssociativeArray(toPrint)); - } if (toPrint.class == class'HashTable') { return PrintHashTable(HashTable(toPrint)); } return none; } -/** - * "Prints" given `DynamicArray` value, saving it as a JSON array in - * `MutableText`. - * - * "Prints" given `DynamicArray` in a minimal way, for a human-readable output - * use `PrettyPrintArray()` method. - * - * It's items must either be equal to `none` or have one of the following - * classes: `BoolBox`, `BoolRef`, `IntBox`, `IntRef`, `FloatBox`, `FloatRef`, - * `Text`, `MutableText`, `DynamicArray`, `AssociativeArray`. - * Otherwise items will be printed as "null" values. - * Also see `Print()` method. - * - * @param toPrint Array to "print" into `MutableText`. - * @return Text version of given `toPrint`, if it has one of the printable - * classes. Otherwise returns `none`. - * Note that `none` is considered printable and will produce "null". - */ -public final function MutableText PrintArray(DynamicArray toPrint) -{ - local int i, length; - local MutableText result, printedItem; - if (toPrint == none) return none; - - length = toPrint.GetLength(); - result = T(default.TOPEN_BRACKET).MutableCopy(); - for (i = 0; i < length; i += 1) - { - if (i > 0) { - result.Append(T(default.TCOMMA)); - } - printedItem = Print(toPrint.GetItem(i)); - if (printedItem != none) - { - result.Append(printedItem); - printedItem.FreeSelf(); - } - else { - result.Append(T(default.TNULL)); - } - } - result.Append(T(default.TCLOSE_BRACKET)); - return result; -} - /** * "Prints" given `ArrayList` value, saving it as a JSON array in * `MutableText`. @@ -1163,7 +926,7 @@ public final function MutableText PrintArray(DynamicArray toPrint) * * It's items must either be equal to `none` or have one of the following * classes: `BoolBox`, `BoolRef`, `IntBox`, `IntRef`, `FloatBox`, `FloatRef`, - * `Text`, `MutableText`, `DynamicArray`, `AssociativeArray`. + * `Text`, `MutableText`, `ArrayList`, `HashTable`. * Otherwise items will be printed as "null" values. * Also see `Print()` method. * @@ -1204,65 +967,6 @@ public final function MutableText PrintArrayList(ArrayList toPrint) return result; } -/** - * "Prints" given `AssociativeArray` value, saving it as a JSON object in - * `MutableText`. - * - * "Prints" given `AssociativeArray` in a minimal way, for - * a human-readable output use `PrettyPrintObject()` method. - * - * Only prints items recorded with `Text` key, the rest is omitted. - * - * It's items must either be equal to `none` or have one of the following - * classes: `BoolBox`, `BoolRef`, `IntBox`, `IntRef`, `FloatBox`, `FloatRef`, - * `Text`, `MutableText`, `DynamicArray`, `AssociativeArray`. - * Otherwise items will be printed as "null" values. - * Also see `Print()` method. - * - * @param toPrint Array to "print" into `MutableText`. - * @return Text version of given `toPrint`, if it has one of the printable - * classes. Otherwise returns `none`. - * Note that `none` is considered printable and will produce "null". - */ -public final function MutableText PrintObject(AssociativeArray toPrint) -{ - local bool printedKeyValuePair; - local CollectionIterator iter; - local Text nextKey; - local AcediaObject nextValue; - local MutableText result, printedKey, printedValue; - if (toPrint == none) return none; - - result = T(default.TOPEN_BRACE).MutableCopy(); - iter = toPrint.Iterate(); - for (iter = toPrint.Iterate(); !iter.HasFinished(); iter.Next()) - { - if (printedKeyValuePair) { - result.Append(T(default.TCOMMA)); - } - nextKey = Text(iter.GetKey()); - nextValue = iter.Get(); - if (nextKey == none) continue; - if (nextKey.class != class'Text') continue; - printedKey = DisplayText(nextKey); - printedValue = Print(nextValue); - result.Append(printedKey).Append(T(default.TCOLON)); - printedKey.FreeSelf(); - if (printedValue != none) - { - result.Append(printedValue); - printedValue.FreeSelf(); - } - else { - result.Append(T(default.TNULL)); - } - printedKeyValuePair = true; - } - iter.FreeSelf(); - result.Append(T(default.TCLOSE_BRACE)); - return result; -} - /** * "Prints" given `HashTable` value, saving it as a JSON object in * `MutableText`. @@ -1274,7 +978,7 @@ public final function MutableText PrintObject(AssociativeArray toPrint) * * It's items must either be equal to `none` or have one of the following * classes: `BoolBox`, `BoolRef`, `IntBox`, `IntRef`, `FloatBox`, `FloatRef`, - * `Text`, `MutableText`, `DynamicArray`, `AssociativeArray`. + * `Text`, `MutableText`, `ArrayList`, `HashTable`. * Otherwise items will be printed as "null" values. * Also see `Print()` method. * @@ -1343,10 +1047,10 @@ public final function MutableText PrintHashTable(HashTable toPrint) * 3. Integer (`IntBox`/`IntRef`) and float (`FloatBox`/`FloatRef`) types * are printed into JSON number value; * 4. `Text` and `MutableText` are printed into JSON string value; - * 5. `DynamicArray` is printed into JSON array with `Print()` method + * 5. `ArrayList` is printed into JSON array with `Print()` method * applied to each of it's items. If some of them have not printable * types - "none" will be used as a fallback. - * 6. `AssociativeArray` is printed into JSON object with `Print()` method + * 6. `HashTable` is printed into JSON object with `Print()` method * applied to each of it's items. Only items with `Text` keys are * printed, the rest is omitted. If some of them have not printable * types - "none" will be used as a fallback. @@ -1367,35 +1071,6 @@ public final function MutableText PrettyPrint(AcediaObject toPrint) return result; } -/** - * "Prints" given `DynamicArray` value, saving it as a JSON array in - * `MutableText`. - * - * "Prints" given `DynamicArray` in human-readable way, for minimal output - * use `PrintArray()` method. - * - * It's items must either be equal to `none` or have one of the following - * classes: `BoolBox`, `BoolRef`, `IntBox`, `IntRef`, `FloatBox`, `FloatRef`, - * `Text`, `MutableText`, `DynamicArray`, `AssociativeArray`. - * Otherwise items will be printed as "null" values. - * Also see `Print()` method. - * - * @param toPrint Array to "print" into `MutableText`. - * @return Text version of given `toPrint`, if it has one of the printable - * classes. Otherwise returns `none`. - * Note that `none` is considered printable and will produce "null". - */ -public final function MutableText PrettyPrintArray(DynamicArray toPrint) -{ - local MutableText result; - local MutableText accumulatedIndent; - InitFormatting(); - accumulatedIndent = _.text.Empty(); - result = PrettyPrintArrayWithIndent(toPrint, accumulatedIndent); - accumulatedIndent.FreeSelf(); - return result; -} - /** * "Prints" given `ArrayList` value, saving it as a JSON array in * `MutableText`. @@ -1405,7 +1080,7 @@ public final function MutableText PrettyPrintArray(DynamicArray toPrint) * * It's items must either be equal to `none` or have one of the following * classes: `BoolBox`, `BoolRef`, `IntBox`, `IntRef`, `FloatBox`, `FloatRef`, - * `Text`, `MutableText`, `DynamicArray`, `AssociativeArray`. + * `Text`, `MutableText`, `ArrayList`, `HashTable`. * Otherwise items will be printed as "null" values. * Also see `Print()` method. * @@ -1426,37 +1101,6 @@ public final function MutableText PrettyPrintArrayList(ArrayList toPrint) return result; } -/** - * "Prints" given `AssociativeArray` value, saving it as a JSON object in - * `MutableText`. - * - * "Prints" given `AssociativeArray` in a human readable way, for - * a minimal output use `PrintObject()` method. - * - * Only prints items recorded with `Text` key, the rest is omitted. - * - * It's items must either be equal to `none` or have one of the following - * classes: `BoolBox`, `BoolRef`, `IntBox`, `IntRef`, `FloatBox`, `FloatRef`, - * `Text`, `MutableText`, `DynamicArray`, `AssociativeArray`. - * Otherwise items will be printed as "null" values. - * Also see `Print()` method. - * - * @param toPrint Array to "print" into `MutableText`. - * @return Text version of given `toPrint`, if it has one of the printable - * classes. Otherwise returns `none`. - * Note that `none` is considered printable and will produce "null". - */ -public final function MutableText PrettyPrintObject(AssociativeArray toPrint) -{ - local MutableText result; - local MutableText accumulatedIndent; - InitFormatting(); - accumulatedIndent = _.text.Empty(); - result = PrettyPrintObjectWithIndent(toPrint, accumulatedIndent); - accumulatedIndent.FreeSelf(); - return result; -} - /** * "Prints" given `HashTable` value, saving it as a JSON object in * `MutableText`. @@ -1468,7 +1112,7 @@ public final function MutableText PrettyPrintObject(AssociativeArray toPrint) * * It's items must either be equal to `none` or have one of the following * classes: `BoolBox`, `BoolRef`, `IntBox`, `IntRef`, `FloatBox`, `FloatRef`, - * `Text`, `MutableText`, `DynamicArray`, `AssociativeArray`. + * `Text`, `MutableText`, `ArrayList`, `HashTable`. * Otherwise items will be printed as "null" values. * Also see `Print()` method. * @@ -1532,20 +1176,11 @@ private final function MutableText PrettyPrintWithIndent( { return DisplayText(BaseText(toPrint)).ChangeFormatting(jString); } - if (toPrint.class == class'DynamicArray') - { - return PrettyPrintArrayWithIndent( DynamicArray(toPrint), - accumulatedIndent); - } if (toPrint.class == class'ArrayList') { return PrettyPrintArrayListWithIndent( ArrayList(toPrint), accumulatedIndent); } - if (toPrint.class == class'AssociativeArray') { - return PrettyPrintObjectWithIndent( AssociativeArray(toPrint), - accumulatedIndent); - } if (toPrint.class == class'HashTable') { return PrettyPrintHashTableWithIndent( HashTable(toPrint), accumulatedIndent); @@ -1553,48 +1188,6 @@ private final function MutableText PrettyPrintWithIndent( return none; } -// Does the actual job for `PrettyPrintArray()` method. -// Separated to hide `accumulatedIndent` parameter that is necessary for -// pretty printing. -// Assumes `InitFormatting()` was made and json formatting variables are -// initialized. -private final function MutableText PrettyPrintArrayWithIndent( - DynamicArray toPrint, - MutableText accumulatedIndent) -{ - local int i, length; - local MutableText extendedIndent; - local MutableText result, printedItem; - if (toPrint == none) { - return none; - } - length = toPrint.GetLength(); - extendedIndent = accumulatedIndent.MutableCopy().Append(T(TJSON_INDENT)); - result = T(default.TOPEN_BRACKET).MutableCopy() - .ChangeFormatting(jArrayBraces); - for (i = 0; i < length; i += 1) - { - if (i > 0) { - result.Append(T(default.TCOMMA), jComma); - } - printedItem = PrettyPrintWithIndent(toPrint.GetItem(i), extendedIndent); - if (printedItem != none) - { - result.AppendLineBreak().Append(extendedIndent).Append(printedItem); - printedItem.FreeSelf(); - } - else { - result.Append(T(default.TNULL), jNull); - } - } - if (i > 0) { - result.AppendLineBreak().Append(accumulatedIndent); - } - result.Append(T(default.TCLOSE_BRACKET), jArrayBraces); - extendedIndent.FreeSelf(); - return result; -} - // Does the actual job for `PrettyPrintArray()` method. // Separated to hide `accumulatedIndent` parameter that is necessary for // pretty printing. @@ -1641,49 +1234,6 @@ private final function MutableText PrettyPrintArrayListWithIndent( return result; } -// Does the actual job for `PrettyPrintObject()` method. -// Separated to hide `accumulatedIndent` parameter that is necessary for -// pretty printing. -// Assumes `InitFormatting()` was made and json formatting variables are -// initialized. -private final function MutableText PrettyPrintObjectWithIndent( - AssociativeArray toPrint, - MutableText accumulatedIndent) -{ - local bool printedKeyValuePair; - local CollectionIterator iter; - local Text nextKey; - local AcediaObject nextValue; - local MutableText extendedIndent; - local MutableText result; - if (toPrint == none) { - return none; - } - extendedIndent = accumulatedIndent.MutableCopy().Append(T(TJSON_INDENT)); - result = T(default.TOPEN_BRACE).MutableCopy() - .ChangeFormatting(jObjectBraces); - iter = toPrint.Iterate(); - for (iter = toPrint.Iterate(); !iter.HasFinished(); iter.Next()) - { - if (printedKeyValuePair) { - result.Append(T(default.TCOMMA), jComma); - } - nextKey = Text(iter.GetKey()); - nextValue = iter.Get(); - if (nextKey == none) continue; - if (nextKey.class != class'Text') continue; - PrettyPrintKeyValue(result, nextKey, nextValue, extendedIndent); - printedKeyValuePair = true; - } - if (printedKeyValuePair) { - result.AppendLineBreak().Append(accumulatedIndent); - } - iter.FreeSelf(); - result.Append(T(default.TCLOSE_BRACE), jObjectBraces); - extendedIndent.FreeSelf(); - return result; -} - // Does the actual job for `PrettyPrintHashTable()` method. // Separated to hide `accumulatedIndent` parameter that is necessary for // pretty printing. diff --git a/sources/Text/Tests/TEST_JSON.uc b/sources/Text/Tests/TEST_JSON.uc index 605d28b..ac5de65 100644 --- a/sources/Text/Tests/TEST_JSON.uc +++ b/sources/Text/Tests/TEST_JSON.uc @@ -525,17 +525,17 @@ protected static function SubTest_ParseSimpleValueSuccess() api = __().json; Issue("`ParseWith()` fails to parse correct JSON values."); parser = __().text.ParseString("false, 98.2, 42, \"hmmm\", null"); - TEST_ExpectFalse(BoolBox(api.ParseWith(parser,, true)).Get()); + TEST_ExpectFalse(BoolBox(api.ParseWith(parser)).Get()); parser.MatchS(",").Skip(); - TEST_ExpectTrue(FloatBox(api.ParseWith(parser,, true)).Get() == 98.2); + TEST_ExpectTrue(FloatBox(api.ParseWith(parser)).Get() == 98.2); parser.MatchS(",").Skip(); - TEST_ExpectTrue(IntRef(api.ParseWith(parser, true, true)).Get() == 42); + TEST_ExpectTrue(IntRef(api.ParseWith(parser, true)).Get() == 42); parser.MatchS(",").Skip(); TEST_ExpectTrue( - MutableText(api.ParseWith(parser, true, true)).ToString() + MutableText(api.ParseWith(parser, true)).ToString() == "hmmm"); parser.MatchS(",").Skip(); - TEST_ExpectNone(api.ParseWith(parser,, true)); + TEST_ExpectNone(api.ParseWith(parser)); TEST_ExpectTrue(parser.Ok()); } protected static function SubTest_ParseSimpleValueFailure() @@ -546,13 +546,13 @@ protected static function SubTest_ParseSimpleValueFailure() Issue("`ParseWith()` does not correctly handle parsing invalid" @ "JSON values."); parser = __().text.ParseString("tru"); - TEST_ExpectNone(api.ParseWith(parser,, true)); + TEST_ExpectNone(api.ParseWith(parser)); TEST_ExpectFalse(parser.Ok()); parser = __().text.ParseString(""); - TEST_ExpectNone(api.ParseWith(parser,, true)); + TEST_ExpectNone(api.ParseWith(parser)); TEST_ExpectFalse(parser.Ok()); parser = __().text.ParseString("NUL"); - TEST_ExpectNone(api.ParseWith(parser,, true)); + TEST_ExpectNone(api.ParseWith(parser)); TEST_ExpectFalse(parser.Ok()); } @@ -624,7 +624,7 @@ protected static function SubTest_ParseComplex() local HashTable root, mainObj, subObj, inner; Issue("`ParseHashTableWith()` cannot handle complex values."); parser = __().text.ParseString(default.complexJSONObject); - root = HashTable(__().json.ParseWith(parser,, true)); + root = HashTable(__().json.ParseWith(parser)); TEST_ExpectTrue(root.GetLength() == 3); TEST_ExpectTrue(FloatBox(root.GetItem(P("some_var"))).Get() == -7.32); TEST_ExpectTrue( Text(root.GetItem(P("another_var"))).ToString()