- Joined
- Jun 13, 2016
- Messages
- 586
Hey everyone. Recently I was working on my map and I ended up needing to extend the max size of my struct beyond 8191 instances. Now, I didn't want to rewrite a lot of my code that used the struct's nice access syntax and switch it over to manual HashTable usage, so I wrote a small snippet to get struct-like syntax with hashtables underneath.
I decided to share it here, because I think it might be useful to some people in certain corner cases where you need a lot of data and a struct-like syntax. This way, you don't have to worry about going over the 8191 instance limit.
Allocation based on the ubiquotous Alloc library used almost by everyone. Uses Bribe's Table library for TableArrays. It uses TextMacros to set it up, which is a bit inconvenient, but setting up a HashAlloc struct doesn't look that terrible.
And a Zinc variant (because I use Zinc):
Here's a usage example and test scenario demonstrating traversing a linked list of 16000 elements:
And here's a more complicated usage example from my map (albeit, in Zinc):
Wouldn't it be nice if vJass supported an "extends hashtable" syntax for tables like these?
I decided to share it here, because I think it might be useful to some people in certain corner cases where you need a lot of data and a struct-like syntax. This way, you don't have to worry about going over the 8191 instance limit.
Allocation based on the ubiquotous Alloc library used almost by everyone. Uses Bribe's Table library for TableArrays. It uses TextMacros to set it up, which is a bit inconvenient, but setting up a HashAlloc struct doesn't look that terrible.
JASS:
library HashAlloc requires Table
//! textmacro SetupHashAllocStart takes COUNT
private static Table recycler
private static TableArray fields
static method allocate takes nothing returns thistype
local thistype this = recycler[0]
if (not recycler.has(this)) then
set recycler[0] = this + 1
else
set recycler[0] = recycler[this]
endif
return this
endmethod
method deallocate takes nothing returns nothing
set recycler[this] = recycler[0]
set recycler[0] = this
endmethod
private static method onInit takes nothing returns nothing
set recycler = Table.create()
set recycler[0] = 1
set fields = TableArray[$COUNT$]
//! endtextmacro
//! textmacro SetupHashAllocEnd
endmethod
//! endtextmacro
//! textmacro SetupFieldThistype takes ID, NAME
method operator $NAME$ takes nothing returns thistype
return fields[$ID$].integer[this]
endmethod
method operator $NAME$= takes thistype arg returns nothing {
set fields[$ID$].integer[this] = arg
endmethod
//! endtextmacro
//! textmacro SetupField takes ID, NAME, TYPE
method operator $NAME$ takes nothing returns $TYPE$ {
return fields[$ID$].$TYPE$[this]
endmethod
method operator $NAME$= takes $TYPE$ arg returns nothing
set fields[$ID$].$TYPE$[this] = arg
endmethod
//! endtextmacro
endlibrary
And a Zinc variant (because I use Zinc):
JASS:
library HashAlloc requires Table {
//! textmacro SetupHashAllocStart takes COUNT
private static Table recycler;
private static TableArray fields;
static method allocate() -> thistype {
thistype this = recycler[0];
if (!recycler.has(this)) {
recycler[0] = this + 1;
} else {
recycler[0] = recycler[this];
}
return this;
}
method deallocate() {
recycler[this] = recycler[0];
recycler[0] = this;
}
private static method onInit() {
recycler = Table.create();
recycler[0] = 1;
fields = TableArray[$COUNT$];
//! endtextmacro
//! textmacro SetupHashAllocEnd
}
//! endtextmacro
//! textmacro SetupFieldThistype takes ID, NAME
method operator $NAME$() -> thistype {
return fields[$ID$].integer[this];
}
method operator $NAME$=(thistype arg) {
fields[$ID$].integer[this] = arg;
}
//! endtextmacro
//! textmacro SetupField takes ID, NAME, TYPE
method operator $NAME$() -> $TYPE$ {
return fields[$ID$].$TYPE$[this];
}
method operator $NAME$=($TYPE$ arg) {
fields[$ID$].$TYPE$[this] = arg;
}
//! endtextmacro
}
Here's a usage example and test scenario demonstrating traversing a linked list of 16000 elements:
JASS:
library Test
struct Test extends array
//! runtextmacro SetupHashAllocStart("10")
//! runtextmacro SetupHashAllocEnd()
//! runtextmacro SetupFieldThistype("0", "prev")
//! runtextmacro SetupField("1", "data", "string")
static method create takes string value, thistype prev returns thistype
local thistype this = allocate()
set this.data = value
set this.prev = prev
return this
endmethod
method destroy takes nothing returns nothing
call deallocate()
endmethod
endstruct
endlibrary
library TestCase initializer onInit requires Test
globals
private Test g_first
private Test g_last
private Test g_findFirst_ret
private integer g_i
endglobals
function CreateInstances takes nothing returns nothing
local integer i = 0
// create 16384 instances and link them together
loop
set g_last = Test.create("instance #" + I2S(g_i), g_last)
set i = i + 1
set g_i = g_i + 1
exitwhen i > 256 or g_i > 16384
endloop
if g_i < 16384 then
call CreateInstances.execute()
endif
endfunction
function FindFirst_aux takes Test node returns nothing
local integer i = 0
loop
set node = node.prev
set i = i + 1
exitwhen i > 256 or node.prev == 0
endloop
if (node.prev != 0) then
call FindFirst_aux.execute(node)
else
set g_findFirst_ret = node
endif
endfunction
function FindFirst takes Test node returns Test
call FindFirst_aux(node)
return g_findFirst_ret
endfunction
private function onInit takes nothing returns nothing
call BJDebugMsg("Setting up test")
set g_i = 0
set g_first = Test.create("first", 0)
set g_last = g_first
call BJDebugMsg("Creating instances")
call CreateInstances.execute()
call BJDebugMsg("First - " + g_first.data)
call BJDebugMsg("Last - " + g_last.data)
call BJDebugMsg("Traversed from last - " + FindFirst(g_last).data)
endfunction
endlibrary
And here's a more complicated usage example from my map (albeit, in Zinc):
JASS:
public struct LoadQueue[] {
//! runtextmacro SetupHashAllocStart("20")
//! runtextmacro SetupHashAllocEnd()
static thistype tops[]; // first in queue for each player
static integer sizes[]; // queue sizes for each player
// header
//! runtextmacro SetupFieldThistype("0", "next")
//! runtextmacro SetupFieldThistype("1", "prev")
//! runtextmacro SetupField("2", "owner", "integer")
//! runtextmacro SetupField("3", "objectType", "integer")
// shared
//! runtextmacro SetupField("4", "id", "integer")
//! runtextmacro SetupField("5", "x", "real")
//! runtextmacro SetupField("6", "y", "real")
// unit
//! runtextmacro SetupField("7", "angle", "real")
//! runtextmacro SetupField("8", "size", "real")
//! runtextmacro SetupField("9", "speed", "real")
//! runtextmacro SetupField("10", "animSpeed", "real")
//! runtextmacro SetupField("11", "z", "real")
//! runtextmacro SetupField("12", "color", "integer")
//! runtextmacro SetupField("13", "vertexR", "integer")
//! runtextmacro SetupField("14", "vertexG", "integer")
//! runtextmacro SetupField("15", "vertexB", "integer")
//! runtextmacro SetupField("16", "vertexA", "integer")
//! runtextmacro SetupField("17", "paused", "boolean")
//! runtextmacro SetupField("18", "tag", "string")
//! runtextmacro SetupField("19", "group", "integer")
// deform
//! runtextmacro SetupField("7", "magnitude", "real")
//! runtextmacro SetupField("8", "radius", "real")
}
Wouldn't it be nice if vJass supported an "extends hashtable" syntax for tables like these?