- Joined
- Aug 18, 2009
- Messages
- 4,099
Note: Ignore the hidden codes in case you are not familiar with my other system Structfollowers. My main purpose to start this thread was to tell of concepts, not how the result realization should look like.
Requires:
Head:
For implementation:
Here you get unique keys for the memory:
Actually, you could forget GetKey and instead always take GetKeyArray since the base key vJass provides gets blown either way but I think that's semantically tidier.
Implementation:
Call:
ExpositionYou can only have a certain amount of hashtables[1]. One hashtable suffices for most things, that's why I'd like to divide it neatly, instead of using multiples. vJass provides the key-function, that delivers another integer each time, so you can use them as keys in a hashtable. Even so, said function only gives you a one-dimensional series of numbers, you won't get another series that includes all of them again. That's why, in my opinion, the function should be used for the main table, specific tables will have their own way to obtain keys anyway[2]. Additionally, the key function starts at 0 or 1 to give out keys, not at the integer minimum[3], that method already blasts away half of the 4 billion slots. This does not please me - I try to change it. Furthermore, you often need a third dimension in the table to dedicate an undetermined amount of abilities to a unit for example and this unit already requires one of the two keys[4]. So I chop up the available keys in portions, to carve out a third dimension, so each unit receives a whole array of keys for use. In order to still being effective, I fragment the data, I don't dynamically reset the tables' positions where they fit but have a static max size. Thus it's crucial that when I want to assign arrays of values to an object, I only pass the first key and all other ones get calculated by the position index in the array, hence are reserved for this one array.
1. (you won't break this limit easily though unless you have a system that dynamically allocates them or you have an arithmetic array)
2. (e.g. by multiplication or powers)
3. -(2^8)^4/2 = -2^31 = -2147483648
4. (or at least is included in one of the keys)
The basic idea is
JASS://! textmacro GetKey takes name static key $name$_BASE static constant integer $name$ = Math.Integer.MIN + $name$_BASE //! endtextmacro //! textmacro GetKeyArray takes name static key $name$_BASE static constant integer $name$ = Math.Integer.MIN + Memory.IntegerKeys.Array.OFFSET + $name$_BASE * Memory.IntegerKeys.Array.SIZE //! endtextmacro
You get unique ids in constant distances, so the ids between can be treated as part of a coherent array. The first index, which you gain by GetKeyArray, is the root key you pass to the header and stores up to which index the array holds data.
The code below is an example for a shared memory that can be implemented by other structs that want to use it.
- Structfollowers
- some basic globals
JASS://! runtextmacro Folder("BasicConstants") globals constant integer STRUCT_MAX = 8190 constant integer STRUCT_MIN = 1 constant integer STRUCT_BASE = STRUCT_MAX + 1 constant integer STRUCT_EMPTY = STRUCT_MIN - 1 constant integer NULL = STRUCT_EMPTY endglobals endscope
- some maths
JASS://! runtextmacro Folder("Math") //! runtextmacro Struct("Integer") static integer MIN = -2147483645 endstruct endscope //! runtextmacro StaticStruct("Math") //! runtextmacro LinkToStaticStruct("Math", "Integer") static method RandomI takes integer lowBound, integer highBound returns integer return GetRandomInt(lowBound, highBound) endmethod endstruct
- CreateSimpleAddState (that's my standard textmacro for creating simple abstract numeric values as struct members)
JASS://! textmacro CreateSimpleAddState takes type, defaultValue $type$ value method Get takes nothing returns $type$ return this.value endmethod method Set takes $type$ value returns nothing set this.value = value endmethod method Add takes $type$ value returns nothing call this.Set(this.Get() + value) endmethod method Start takes nothing returns nothing call this.Set($defaultValue$) endmethod method Subtract takes $type$ value returns nothing call this.Set(this.Get() - value) endmethod //! endtextmacro
- Wrappers for GameCache and HashTable (not shown here because not important)
Head:
JASS:
//! runtextmacro Folder("Memory")
//! runtextmacro Folder("IntegerKeys")
//! runtextmacro Struct("Array")
static constant integer EMPTY = -1
static constant integer OFFSET = 8192
static constant integer SIZE = 8192 //you can have up to SIZE - 1 values in an array, that would fit with the normal max amount of instances of a struct
static constant integer STARTED = 0 //the first index holds the amount of elements
//! textmacro Memory_IntegerKeys_Array_CreateType takes name, type, bugConverter
static method Count$name$s takes integer parentKey, integer key returns integer
return (thistype.EMPTY + Memory.IntegerKeys.GetInteger(parentKey, key))
endmethod
static method Count$name$sByHandle takes handle handleSource, integer key returns integer
return thistype.Count$name$s(GetHandleId(handleSource), key)
endmethod
static method Get$name$ takes integer parentKey, integer key, integer index returns $type$
return Memory.IntegerKeys.Get$name$(parentKey, key + thistype.EMPTY + index + 2)
endmethod
static method Get$name$ByHandle takes handle handleSource, integer key, integer index returns $type$
return thistype.Get$name$(GetHandleId(handleSource), key, index)
endmethod
static method Contains$name$ takes integer parentKey, integer key, $type$ value returns boolean
local integer iteration = Count$name$s(parentKey, key)
loop
exitwhen (iteration < thistype.STARTED)
exitwhen (Get$name$(parentKey, key, iteration) == value)
set iteration = iteration - 1
endloop
if (iteration < thistype.STARTED) then
return false
endif
return true
endmethod
static method Add$name$ takes integer parentKey, integer key, $type$ value returns boolean
local integer count = Count$name$s(parentKey, key) + 1
call Memory.IntegerKeys.SetInteger(parentKey, key, count - thistype.EMPTY)
call Memory.IntegerKeys.Set$name$(parentKey, key + thistype.EMPTY + count + 2, value)
return (count == thistype.STARTED)
endmethod
static method Add$name$ByHandle takes handle handleSource, integer key, $type$ value returns boolean
return thistype.Add$name$(GetHandleId(handleSource), key, value)
endmethod
static method Remove$name$ takes integer parentKey, integer key, $type$ value returns boolean
local integer count = Count$name$s(parentKey, key)
local integer iteration = count
loop
debug exitwhen (iteration < thistype.STARTED)
exitwhen (Get$name$(parentKey, key, iteration) == value)
set iteration = iteration - 1
endloop
debug if (iteration < thistype.STARTED) then
debug call BJDebugMsg("Failed to remove "+$BugConverter$(value)+" from array "+I2S(key)+" of parentKey "+I2S(parentKey)+" ("+I2S(count)+")")
debug else
call Memory.IntegerKeys.Set$name$(parentKey, key + thistype.EMPTY + iteration + 2, Get$name$(parentKey, key, count))
set count = count - 1
call Memory.IntegerKeys.SetInteger(parentKey, key, count - thistype.EMPTY)
debug endif
return (count == thistype.EMPTY)
endmethod
static method Remove$name$ByHandle takes handle handleSource, integer key, $type$ value returns boolean
return thistype.Remove$name$(GetHandleId(handleSource), key, value)
endmethod
static method Random$name$ takes integer parentKey, integer key, integer lowerBound, integer higherBound returns $type$
return thistype.Get$name$(parentKey, key, Math.RandomI(lowerBound, higherBound))
endmethod
static method Random$name$ByHandle takes handle handleSource, integer key, integer lowerBound, integer higherBound returns $type$
return thistype.Random$name$(GetHandleId(handleSource), key, lowerBound, higherBound)
endmethod
static method Random$name$All takes integer parentKey, integer key returns $type$
return thistype.Random$name$(parentKey, key, thistype.STARTED, Count$name$s(parentKey, key))
endmethod
//! endtextmacro
//! runtextmacro Memory_IntegerKeys_Array_CreateType("Boolean", "boolean", "B2S")
//! runtextmacro Memory_IntegerKeys_Array_CreateType("Integer", "integer", "I2S")
//! runtextmacro Memory_IntegerKeys_Array_CreateType("Real", "real", "R2S")
//! runtextmacro Memory_IntegerKeys_Array_CreateType("String", "string", "")
endstruct
endscope
//! runtextmacro Struct("IntegerKeys")
static HashTable CACHE
//! runtextmacro LinkToStruct("IntegerKeys", "Array")
static method RemoveChild takes integer parentKey returns nothing
call CACHE.RemoveMission(parentKey)
endmethod
//! textmacro Memory_IntegerKeys_CreateType takes name, type
static method Set$name$ takes integer parentKey, integer key, $type$ value returns nothing
call CACHE.$name$.Set(parentKey, key, value)
endmethod
static method Set$name$ByHandle takes handle handleSource, integer key, $type$ value returns nothing
call thistype.Set$name$(GetHandleId(handleSource), key, value)
endmethod
static method Remove$name$ takes integer parentKey, integer key returns nothing
call CACHE.$name$.Remove(parentKey, key)
endmethod
static method Remove$name$ByHandle takes handle handleSource, integer key returns nothing
call thistype.Remove$name$(GetHandleId(handleSource), key)
endmethod
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static method Get$name$ takes integer handleSource, integer key returns $type$
return CACHE.$name$.Get(handleSource, key)
endmethod
static method Get$name$ByHandle takes handle handleSource, integer key returns $type$
return thistype.Get$name$(GetHandleId(handleSource), key)
endmethod
//! endtextmacro
//! runtextmacro Memory_IntegerKeys_CreateType("Boolean", "boolean")
//! runtextmacro Memory_IntegerKeys_CreateType("Integer", "integer")
//! runtextmacro Memory_IntegerKeys_CreateType("Real", "real")
//! runtextmacro Memory_IntegerKeys_CreateType("String", "string")
static method Init takes nothing returns nothing
set CACHE = HashTable.Create()
endmethod
endstruct
//! runtextmacro Folder("StringKeys")
//! runtextmacro Struct("Array")
static constant integer EMPTY = -1
static constant integer STARTED = 0
//! textmacro Memory_StringKeys_Array_CreateType takes name, type, bugConverter
static method Count$name$s takes string parentKey, string key returns integer
return (thistype.EMPTY + Memory.StringKeys.GetInteger(parentKey, key))
endmethod
static method Get$name$ takes string parentKey, string key, integer index returns $type$
return Memory.StringKeys.Get$name$(parentKey, key + I2S(thistype.EMPTY + index + 2))
endmethod
static method Contains$name$ takes string parentKey, string key, $type$ value returns boolean
local integer iteration = Count$name$s(parentKey, key)
loop
exitwhen (iteration < thistype.STARTED)
exitwhen (Get$name$(parentKey, key, iteration) == value)
set iteration = iteration - 1
endloop
if (iteration < thistype.STARTED) then
return false
endif
return true
endmethod
static method Add$name$ takes string parentKey, string key, $type$ value returns boolean
local integer count = Count$name$s(parentKey, key) + 1
call Memory.StringKeys.SetInteger(parentKey, key, count - thistype.EMPTY)
call Memory.StringKeys.Set$name$(parentKey, key + I2S(thistype.EMPTY + count + 2), value)
return (count == thistype.STARTED)
endmethod
static method Remove$name$ takes string parentKey, string key, $type$ value returns boolean
local integer count = Count$name$s(parentKey, key)
local integer iteration = count
loop
debug exitwhen (iteration < thistype.STARTED)
exitwhen (Get$name$(parentKey, key, iteration) == value)
set iteration = iteration - 1
endloop
debug if (iteration < thistype.STARTED) then
debug call BJDebugMsg("Failed to remove "+$BugConverter$(value)+" from array "+key+" of parentKey "+parentKey+" ("+I2S(count)+")")
debug else
call Memory.StringKeys.Set$name$(parentKey, key + I2S(thistype.EMPTY + iteration + 2), Get$name$(parentKey, key, count))
set count = count - 1
call Memory.StringKeys.SetInteger(parentKey, key, count)
debug endif
return (count == thistype.EMPTY)
endmethod
static method Random$name$ takes string parentKey, string key returns $type$
return thistype.Get$name$(parentKey, key, Math.RandomI(thistype.STARTED, Count$name$s(parentKey, key)))
endmethod
//! endtextmacro
//! runtextmacro Memory_StringKeys_Array_CreateType("Boolean", "boolean", "B2S")
//! runtextmacro Memory_StringKeys_Array_CreateType("Integer", "integer", "I2S")
//! runtextmacro Memory_StringKeys_Array_CreateType("Real", "real", "R2S")
//! runtextmacro Memory_StringKeys_Array_CreateType("String", "string", "")
endstruct
endscope
//! runtextmacro Struct("StringKeys")
static GameCache CACHE
//! runtextmacro LinkToStruct("StringKeys", "Array")
static method RemoveChild takes string parentKey returns nothing
call CACHE.RemoveMission(parentKey)
endmethod
//! textmacro Memory_StringKeys_CreateType takes name, type
static method Set$name$ takes string parentKey, string key, $type$ value returns nothing
call CACHE.$name$.Set(parentKey, key, value)
endmethod
static method Remove$name$ takes string parentKey, string key returns nothing
call CACHE.$name$.Remove(parentKey, key)
endmethod
static method Get$name$ takes string parentKey, string key returns $type$
return CACHE.$name$.Get(parentKey, key)
endmethod
//! endtextmacro
//! runtextmacro Memory_StringKeys_CreateType("Boolean", "boolean")
//! runtextmacro Memory_StringKeys_CreateType("Integer", "integer")
//! runtextmacro Memory_StringKeys_CreateType("Real", "real")
//! runtextmacro Memory_StringKeys_CreateType("String", "string")
static method Init takes nothing returns nothing
set CACHE = GameCache.Create("blub")
endmethod
endstruct
endscope
//! runtextmacro StaticStruct("Memory")
//! runtextmacro LinkToStaticStruct("Memory", "IntegerKeys")
//! runtextmacro LinkToStaticStruct("Memory", "StringKeys")
static method Init takes nothing returns nothing
call IntegerKeys.Init()
call StringKeys.Init()
endmethod
endstruct
For implementation:
JASS:
//! textmacro Data_Implement takes baseType
method Destroy takes nothing returns nothing
call Memory.IntegerKeys.RemoveChild($baseType$(this).Id.Get())
endmethod
//! endtextmacro
//! textmacro Data_Type_Implement takes baseType, whichTypeName, whichType
method Get takes integer key returns $whichType$
return Memory.IntegerKeys.Get$whichTypeName$($baseType$(this).Id.Get(), key)
endmethod
method Is takes integer key returns boolean
return (this.Get(key) != HASH_TABLE.$whichTypeName$.DEFAULT_VALUE)
endmethod
method Remove takes integer key returns nothing
call Memory.IntegerKeys.Remove$whichTypeName$($baseType$(this).Id.Get(), key)
endmethod
method Set takes integer key, $whichType$ value returns nothing
call Memory.IntegerKeys.Set$whichTypeName$($baseType$(this).Id.Get(), key, value)
endmethod
//! endtextmacro
//! textmacro Data_Type_Array_Implement takes baseType, whichTypeName, whichType
method Contains takes integer key, $whichType$ value returns boolean
return Memory.IntegerKeys.Array.Contains$whichTypeName$($baseType$(this).Id.Get(), key, value)
endmethod
method Count takes integer key returns integer
return Memory.IntegerKeys.Array.Count$whichTypeName$s($baseType$(this).Id.Get(), key)
endmethod
method Get takes integer key, integer index returns $whichType$
return Memory.IntegerKeys.Array.Get$whichTypeName$($baseType$(this).Id.Get(), key, index)
endmethod
method Clear takes integer key returns nothing
call Memory.IntegerKeys.Array.Clear($baseType$(this).Id.Get(), key)
endmethod
method Remove takes integer key, $whichType$ value returns boolean
return Memory.IntegerKeys.Array.Remove$whichTypeName$($baseType$(this).Id.Get(), key, value)
endmethod
method RemoveSorted takes integer key, $whichType$ value returns boolean
return Memory.IntegerKeys.Array.RemoveSorted$whichTypeName$($baseType$(this).Id.Get(), key, value)
endmethod
method Add takes integer key, $whichType$ value returns boolean
return Memory.IntegerKeys.Array.Add$whichTypeName$($baseType$(this).Id.Get(), key, value)
endmethod
method AddSorted takes integer key, $whichType$ value returns boolean
return Memory.IntegerKeys.Array.AddSorted$whichTypeName$($baseType$(this).Id.Get(), key, value)
endmethod
method Random takes integer key, integer lowerBound, integer higherBound returns $whichType$
return Memory.IntegerKeys.Array.Random$whichTypeName$($baseType$(this).Id.Get(), key, lowerBound, higherBound)
endmethod
method RandomAll takes integer key returns $whichType$
return this.Random(key, Memory.IntegerKeys.Array.STARTED, this.Count(key))
endmethod
//! endtextmacro
//additional
//! textmacro Data_Boolean_Implement takes baseType
method Add takes integer key returns boolean
local boolean oldValue = this.Get(key)
call this.Set(key, HASH_TABLE.Boolean.DEFAULT_VALUE == false)
return (oldValue == HASH_TABLE.Boolean.DEFAULT_VALUE)
endmethod
method Subtract takes integer key returns boolean
local boolean oldValue = this.Get(key)
call this.Set(key, HASH_TABLE.Boolean.DEFAULT_VALUE)
return (oldValue != HASH_TABLE.Boolean.DEFAULT_VALUE)
endmethod
//! endtextmacro
//! textmacro Data_Integer_Implement takes baseType
method Contains takes integer key returns boolean
return (this.Get(key) > HASH_TABLE.Integer.DEFAULT_VALUE)
endmethod
method Add takes integer key, integer value returns boolean
local integer oldValue = this.Get(key)
call this.Set(key, oldValue + value)
if (oldValue != HASH_TABLE.Integer.DEFAULT_VALUE) then
return false
endif
return (value != HASH_TABLE.Integer.DEFAULT_VALUE)
endmethod
method Subtract takes integer key, integer value returns boolean
local integer oldValue = this.Get(key)
set value = (oldValue - value)
call this.Set(key, value)
if (oldValue == HASH_TABLE.Integer.DEFAULT_VALUE) then
return false
endif
return (value == HASH_TABLE.Integer.DEFAULT_VALUE)
endmethod
//! endtextmacro
//! textmacro Data_Real_Implement takes baseType
method Add takes integer key, real value returns boolean
local real oldValue = this.Get(key)
call this.Set(key, oldValue + value)
if (oldValue != HASH_TABLE.Real.DEFAULT_VALUE) then
return false
endif
return (value != HASH_TABLE.Real.DEFAULT_VALUE)
endmethod
method Subtract takes integer key, real value returns boolean
local real oldValue = this.Get(key)
set value = (oldValue - value)
call this.Set(key, value)
if (oldValue == HASH_TABLE.Real.DEFAULT_VALUE) then
return false
endif
return (value == HASH_TABLE.Real.DEFAULT_VALUE)
endmethod
//! endtextmacro
//! textmacro Data_String_Implement takes baseType
method Add takes integer key, string value returns boolean
local string oldValue = this.Get(key)
call this.Set(key, oldValue + value)
if (oldValue != HASH_TABLE.String.DEFAULT_VALUE) then
return false
endif
return (value != HASH_TABLE.String.DEFAULT_VALUE)
endmethod
method Subtract takes integer key, real value returns boolean
local string oldValue = this.Get(key)
set value = (oldValue - value)
call this.Set(key, value)
if (oldValue == HASH_TABLE.String.DEFAULT_VALUE) then
return false
endif
return (value == HASH_TABLE.String.DEFAULT_VALUE)
endmethod
//! endtextmacro
//for string keys
//! textmacro Data_StringKey_Implement
static method Destroy takes string whichString returns nothing
call Memory.StringKeys.RemoveChild(whichString)
endmethod
//! endtextmacro
//! textmacro Data_StringKey_Type_Implement takes whichTypeName, whichType
static method Get takes string whichString, integer key returns $whichType$
return Memory.StringKeys.Get$whichTypeName$(whichString, Integer.ToString(key))
endmethod
static method Remove takes string whichString, integer key returns nothing
call Memory.StringKeys.Remove$whichTypeName$(whichString, Integer.ToString(key))
endmethod
static method Set takes string whichString, integer key, $whichType$ value returns nothing
call Memory.StringKeys.Set$whichTypeName$(whichString, Integer.ToString(key), value)
endmethod
//! endtextmacro
//! textmacro Data_StringKey_Type_Array_Implement takes whichTypeName, whichType
static method Count takes string whichString, integer key returns integer
return Memory.StringKeys.Array.Count$whichTypeName$s(whichString, Integer.ToString(key))
endmethod
static method Get takes string whichString, integer key, integer index returns $whichType$
return Memory.StringKeys.Array.Get$whichTypeName$(whichString, Integer.ToString(key), index)
endmethod
static method Remove takes string whichString, integer key, $whichType$ value returns boolean
return Memory.StringKeys.Array.Remove$whichTypeName$(whichString, Integer.ToString(key), value)
endmethod
static method Add takes string whichString, integer key, $whichType$ value returns boolean
return Memory.StringKeys.Array.Add$whichTypeName$(whichString, Integer.ToString(key), value)
endmethod
static method Random takes string whichString, integer key, integer lowerBound, integer higherBound returns $whichType$
return Memory.StringKeys.Array.Random$whichTypeName$(whichString, Integer.ToString(key), lowerBound, higherBound)
endmethod
static method RandomAll takes string whichString, integer key returns $whichType$
return thistype.Random(whichString, key, Memory.IntegerKeys.Array.STARTED, thistype.Count(whichString, key))
endmethod
//! endtextmacro
Here you get unique keys for the memory:
JASS:
//! textmacro GetKey takes name
static key $name$_BASE
static constant integer $name$ = Math.Integer.MIN + $name$_BASE
//! endtextmacro
//! textmacro GetKeyArray takes name
static key $name$_BASE
static constant integer $name$ = Math.Integer.MIN + Memory.IntegerKeys.Array.OFFSET + $name$_BASE * Memory.IntegerKeys.Array.SIZE
//! endtextmacro
Actually, you could forget GetKey and instead always take GetKeyArray since the base key vJass provides gets blown either way but I think that's semantically tidier.
Implementation:
The following shows how it is implemented in a struct, so this struct automatically gets a parentkey and you can easily store data belonging to it.
Macros:
Example on Unit:
Macros:
Example on Unit:
JASS:
//! runtextmacro Folder("Unit")
//! runtextmacro Folder("Data")
//! runtextmacro Struct("Boolean")
//! runtextmacro Data_Type_Implement("Unit", "Boolean", "boolean")
endstruct
//! runtextmacro Folder("Integer")
//! runtextmacro Struct("Array")
//! runtextmacro Data_Type_Array_Implement("Unit", "Integer", "integer")
endstruct
endscope
//! runtextmacro Struct("Integer")
//! runtextmacro LinkToStruct("Integer", "Array")
//! runtextmacro Data_Type_Implement("Unit", "Integer", "integer")
endstruct
endscope
//! runtextmacro Struct("Data")
//! runtextmacro LinkToStruct("Data", "Boolean")
//! runtextmacro LinkToStruct("Data", "Integer")
//! runtextmacro Data_Implement("Unit")
endstruct
//! runtextmacro Struct("Id")
//! runtextmacro GetKeyArray("KEY_ARRAY")
//! runtextmacro CreateSimpleAddState("integer", "KEY_ARRAY + this")
endstruct
endscope
//! runtextmacro BaseStruct("Unit", "UNIT")
//! runtextmacro LinkToStruct("Unit", "Data")
//! runtextmacro LinkToStruct("Unit", "Id")
static method Create takes <params> returns thistype
local unit self = <unit>
local thistype this = thistype.allocate()
set this.self = self
call this.Id.Start() //must be called to equip the unit with its parentkey for the memory
return this
endmethod
endstruct
Call:
When you now want to use it:
JASS:
struct IceCream
//! runtextmacro GetKey("KEY")
//! runtextmacro GetKeyArray("SCOOP_KEY_ARRAY")
Waffle base
method AddToUnit takes Unit whichUnit returns nothing
call whichUnit.Data.Integer.Set(KEY, this)
endmethod
method AddScoop takes IceCreamType whichType returns nothing
call this.Data.Integer.Array.Add(SCOOP_KEY_ARRAY, whichType)
endmethod
static method Create takes nothing returns thistype
local thistype this = thistype.allocate()
set this.base = Waffle.Create()
return this
endmethod
endstruct
Last edited: