1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.
  2. We need your help nominating resources for or next YouTube video. Post here now.
    Dismiss Notice
  3. Cast your vote for the best created hero from the Hive Workshop! [POLL] +100 REP Team Contest - Hive Member
    Dismiss Notice
  4. The Terraining Mini Contest Reload #1 - Mist has began! Create a scene laden in thick fog, with only 20 doodads!
    Dismiss Notice
  5. Full steam ahead! Choose the best steampunk song at our Music Contest #9 - Poll
    Dismiss Notice
  6. Join us in our custom games night next Saturday, January 27th. We'll see you on Battle.net and Discord!
    Dismiss Notice

[Snippet] New Table

Discussion in 'JASS Resources' started by Bribe, Jan 25, 2011.

  1. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,478
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Table is based on the philosophy that you can use one hashtable for your whole map. What it does is divide one hashtable into many different components, and each system in the map can have its own share of the hashtable. Taking advantage of parent keys and child keys to their fullest extent, the ability to have 1 hashtable for the whole map can now be realized.

    I came up with the idea for this project after using Vexorian's Table and hitting the limits a number of times. All of those limitations have been fulfilled by this project:

    1. You have access to all the hashtable API, so you can now save handles, booleans, reals, strings and integers, instead of just integers.
    2. You can have up to 2 ^ 31 - 1 Table instances. Previously, you could have 400,000 if you set the constant appropriately, but that generates hundreds of lines of code. This means you don't ever have to worry about creating too many Tables, because you can never really have too many.
    3. 2-D array syntax is perfected and allows you to create things called TableArrays. The old method used the volatile StringHash, which easily bypasses the integer limit and starts saving into unpredictable places. This is dangerous when using a shared hashtable because you could overwrite someone else's data without even knowing it.
    4. Table instances can save/load data from within module initializers. This didn't work before because the hashtable was initialized from a struct instead of from the globals block.

    You can only have 256 hashtables at a time, so with 2 ^ 31 - 1 Table instances at your disposal and (that's right, I said "and", not "or") roughly 2 ^ 18 TableArray instances with array size 8192 (JASS max array size), you will find yourself with more storage options than you know what to do with.

    If you take advantage of this system to its fullest, you will never need to call InitHashtable() again.

    How it works

    The basis of how it works is this: you often run into situations where you have no use for two keys (only a single key). Usually you just waste a key as 0 and run everything else through child-keys. This system (as well as Vexorian's Table) simply gives you a parent-key to use in a globally-shared hashtable. Just initialize an Table instance via
    Table.create()
    .

    But sometimes you need more than that, and both keys actually mean something to you. Usually the parent-key would be "this" from a struct and the child-keys are various other bits. A TableArray - an array of Tables - is a great way to achieve this. Instanciate a TableArray via
    TableArray[16]
    for an array of Tables sized 16, or
    TableArray[0x2000]
    for an array of Tables with the same size as a normal JASS array. The size can be very large if you want, but to be practical keep it smaller than a few million, because the total accumulated size of all your TableArrays must remain under 2 ** 31 - 1 (a little more than 2 billion) because that's the limit of how high integers can go.

    If you're dealing with more randomly-accessed numbers or consistently very large indices in the parent-key field and couldn't previously accomplish it with a pre-sized TableArray, I have (on July 21, 2015) updated this to NewTable 4.0 where you can instantiate HashTables to do just that!

    I now imagine this a completed project, so please let me know if there's more you'd like to see in the future and I'll take it into consideration.


    Code (vJASS):

    library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 4.1.0.1.
       
        One map, one hashtable. Welcome to NewTable 4.1.0.1
       
        This newest iteration of Table introduces the new HashTable struct.
        You can now instantiate HashTables which enables the use of large
        parent and large child keys, just like a standard hashtable. Previously,
        the user would have to instantiate a Table to do this on their own which -
        while doable - is something the user should not have to do if I can add it
        to this resource myself (especially if they are inexperienced).
       
        This library was originally called NewTable so it didn't conflict with
        the API of Table by Vexorian. However, the damage is done and it's too
        late to change the library name now. To help with damage control, I
        have provided an extension library called TableBC, which bridges all
        the functionality of Vexorian's Table except for 2-D string arrays &
        the ".flush(integer)" method. I use ".flush()" to flush a child hash-
        table, because I wanted the API in NewTable to reflect the API of real
        hashtables (I thought this would be more intuitive).
       
        API
       
        ------------
        struct Table
        | static method create takes nothing returns Table
        |     create a new Table
        |    
        | method destroy takes nothing returns nothing
        |     destroy it
        |    
        | method flush takes nothing returns nothing
        |     flush all stored values inside of it
        |    
        | method remove takes integer key returns nothing
        |     remove the value at index "key"
        |    
        | method operator []= takes integer key, $TYPE$ value returns nothing
        |     assign "value" to index "key"
        |    
        | method operator [] takes integer key returns $TYPE$
        |     load the value at index "key"
        |    
        | method has takes integer key returns boolean
        |     whether or not the key was assigned
        |
        ----------------
        struct TableArray
        | static method operator [] takes integer array_size returns TableArray
        |     create a new array of Tables of size "array_size"
        |
        | method destroy takes nothing returns nothing
        |     destroy it
        |
        | method flush takes nothing returns nothing
        |     flush and destroy it
        |
        | method operator size takes nothing returns integer
        |     returns the size of the TableArray
        |
        | method operator [] takes integer key returns Table
        |     returns a Table accessible exclusively to index "key"
    */

       
    globals
        private integer less = 0    //Index generation for TableArrays (below 0).
        private integer more = 8190 //Index generation for Tables.
        //Configure it if you use more than 8190 "key" variables in your map (this will never happen though).
       
        private hashtable ht = InitHashtable()
        private key sizeK
        private key listK
    endglobals
       
    private struct dex extends array
        static method operator size takes nothing returns Table
            return sizeK
        endmethod
        static method operator list takes nothing returns Table
            return listK
        endmethod
    endstruct
       
    private struct handles extends array
        method has takes integer key returns boolean
            return HaveSavedHandle(ht, this, key)
        endmethod
        method remove takes integer key returns nothing
            call RemoveSavedHandle(ht, this, key)
        endmethod
    endstruct
       
    private struct agents extends array
        method operator []= takes integer key, agent value returns nothing
            call SaveAgentHandle(ht, this, key, value)
        endmethod
    endstruct
       
    //! textmacro NEW_ARRAY_BASIC takes SUPER, FUNC, TYPE
    private struct $TYPE$s extends array
        method operator [] takes integer key returns $TYPE$
            return Load$FUNC$(ht, this, key)
        endmethod
        method operator []= takes integer key, $TYPE$ value returns nothing
            call Save$FUNC$(ht, this, key, value)
        endmethod
        method has takes integer key returns boolean
            return HaveSaved$SUPER$(ht, this, key)
        endmethod
        method remove takes integer key returns nothing
            call RemoveSaved$SUPER$(ht, this, key)
        endmethod
    endstruct
    private module $TYPE$m
        method operator $TYPE$ takes nothing returns $TYPE$s
            return this
        endmethod
    endmodule
    //! endtextmacro
       
    //! textmacro NEW_ARRAY takes FUNC, TYPE
    private struct $TYPE$s extends array
        method operator [] takes integer key returns $TYPE$
            return Load$FUNC$Handle(ht, this, key)
        endmethod
        method operator []= takes integer key, $TYPE$ value returns nothing
            call Save$FUNC$Handle(ht, this, key, value)
        endmethod
        method has takes integer key returns boolean
            return HaveSavedHandle(ht, this, key)
        endmethod
        method remove takes integer key returns nothing
            call RemoveSavedHandle(ht, this, key)
        endmethod
    endstruct
    private module $TYPE$m
        method operator $TYPE$ takes nothing returns $TYPE$s
            return this
        endmethod
    endmodule
    //! endtextmacro
       
    //Run these textmacros to include the entire hashtable API as wrappers.
    //Don't be intimidated by the number of macros - Vexorian's map optimizer is
    //supposed to kill functions which inline (all of these functions inline).
    //! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
    //! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
    //! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
    //New textmacro to allow table.integer[] syntax for compatibility with textmacros that might desire it.
    //! runtextmacro NEW_ARRAY_BASIC("Integer", "Integer", "integer")
       
    //! runtextmacro NEW_ARRAY("Player", "player")
    //! runtextmacro NEW_ARRAY("Widget", "widget")
    //! runtextmacro NEW_ARRAY("Destructable", "destructable")
    //! runtextmacro NEW_ARRAY("Item", "item")
    //! runtextmacro NEW_ARRAY("Unit", "unit")
    //! runtextmacro NEW_ARRAY("Ability", "ability")
    //! runtextmacro NEW_ARRAY("Timer", "timer")
    //! runtextmacro NEW_ARRAY("Trigger", "trigger")
    //! runtextmacro NEW_ARRAY("TriggerCondition", "triggercondition")
    //! runtextmacro NEW_ARRAY("TriggerAction", "triggeraction")
    //! runtextmacro NEW_ARRAY("TriggerEvent", "event")
    //! runtextmacro NEW_ARRAY("Force", "force")
    //! runtextmacro NEW_ARRAY("Group", "group")
    //! runtextmacro NEW_ARRAY("Location", "location")
    //! runtextmacro NEW_ARRAY("Rect", "rect")
    //! runtextmacro NEW_ARRAY("BooleanExpr", "boolexpr")
    //! runtextmacro NEW_ARRAY("Sound", "sound")
    //! runtextmacro NEW_ARRAY("Effect", "effect")
    //! runtextmacro NEW_ARRAY("UnitPool", "unitpool")
    //! runtextmacro NEW_ARRAY("ItemPool", "itempool")
    //! runtextmacro NEW_ARRAY("Quest", "quest")
    //! runtextmacro NEW_ARRAY("QuestItem", "questitem")
    //! runtextmacro NEW_ARRAY("DefeatCondition", "defeatcondition")
    //! runtextmacro NEW_ARRAY("TimerDialog", "timerdialog")
    //! runtextmacro NEW_ARRAY("Leaderboard", "leaderboard")
    //! runtextmacro NEW_ARRAY("Multiboard", "multiboard")
    //! runtextmacro NEW_ARRAY("MultiboardItem", "multiboarditem")
    //! runtextmacro NEW_ARRAY("Trackable", "trackable")
    //! runtextmacro NEW_ARRAY("Dialog", "dialog")
    //! runtextmacro NEW_ARRAY("Button", "button")
    //! runtextmacro NEW_ARRAY("TextTag", "texttag")
    //! runtextmacro NEW_ARRAY("Lightning", "lightning")
    //! runtextmacro NEW_ARRAY("Image", "image")
    //! runtextmacro NEW_ARRAY("Ubersplat", "ubersplat")
    //! runtextmacro NEW_ARRAY("Region", "region")
    //! runtextmacro NEW_ARRAY("FogState", "fogstate")
    //! runtextmacro NEW_ARRAY("FogModifier", "fogmodifier")
    //! runtextmacro NEW_ARRAY("Hashtable", "hashtable")
       
    struct Table extends array
       
        // Implement modules for intuitive syntax (tb.handle; tb.unit; etc.)
        implement realm
        implement integerm
        implement booleanm
        implement stringm
        implement playerm
        implement widgetm
        implement destructablem
        implement itemm
        implement unitm
        implement abilitym
        implement timerm
        implement triggerm
        implement triggerconditionm
        implement triggeractionm
        implement eventm
        implement forcem
        implement groupm
        implement locationm
        implement rectm
        implement boolexprm
        implement soundm
        implement effectm
        implement unitpoolm
        implement itempoolm
        implement questm
        implement questitemm
        implement defeatconditionm
        implement timerdialogm
        implement leaderboardm
        implement multiboardm
        implement multiboarditemm
        implement trackablem
        implement dialogm
        implement buttonm
        implement texttagm
        implement lightningm
        implement imagem
        implement ubersplatm
        implement regionm
        implement fogstatem
        implement fogmodifierm
        implement hashtablem
       
        method operator handle takes nothing returns handles
            return this
        endmethod
       
        method operator agent takes nothing returns agents
            return this
        endmethod
       
        //set this = tb[GetSpellAbilityId()]
        method operator [] takes integer key returns Table
            return LoadInteger(ht, this, key) //return this.integer[key]
        endmethod
       
        //set tb[389034] = 8192
        method operator []= takes integer key, Table tb returns nothing
            call SaveInteger(ht, this, key, tb) //set this.integer[key] = tb
        endmethod
       
        //set b = tb.has(2493223)
        method has takes integer key returns boolean
            return HaveSavedInteger(ht, this, key) //return this.integer.has(key)
        endmethod
       
        //call tb.remove(294080)
        method remove takes integer key returns nothing
            call RemoveSavedInteger(ht, this, key) //call this.integer.remove(key)
        endmethod
       
        //Remove all data from a Table instance
        method flush takes nothing returns nothing
            call FlushChildHashtable(ht, this)
        endmethod
       
        //local Table tb = Table.create()
        static method create takes nothing returns Table
            local Table this = dex.list[0]
           
            if this == 0 then
                set this = more + 1
                set more = this
            else
                set dex.list[0] = dex.list[this]
                call dex.list.remove(this) //Clear hashed memory
            endif
           
            debug set dex.list[this] = -1
            return this
        endmethod
       
        // Removes all data from a Table instance and recycles its index.
        //
        //     call tb.destroy()
        //
        method destroy takes nothing returns nothing
            debug if dex.list[this] != -1 then
                debug call BJDebugMsg("Table Error: Tried to double-free instance: " + I2S(this))
                debug return
            debug endif
           
            call this.flush()
           
            set dex.list[this] = dex.list[0]
            set dex.list[0] = this
        endmethod
       
        //! runtextmacro optional TABLE_BC_METHODS()
    endstruct
       
    //! runtextmacro optional TABLE_BC_STRUCTS()
       
    struct TableArray extends array
       
        //Returns a new TableArray to do your bidding. Simply use:
        //
        //    local TableArray ta = TableArray[array_size]
        //
        static method operator [] takes integer array_size returns TableArray
            local Table tb = dex.size[array_size] //Get the unique recycle list for this array size
            local TableArray this = tb[0]         //The last-destroyed TableArray that had this array size
           
            debug if array_size <= 0 then
                debug call BJDebugMsg("TypeError: Invalid specified TableArray size: " + I2S(array_size))
                debug return 0
            debug endif
           
            if this == 0 then
                set this = less - array_size
                set less = this
            else
                set tb[0] = tb[this]  //Set the last destroyed to the last-last destroyed
                call tb.remove(this)  //Clear hashed memory
            endif
           
            set dex.size[this] = array_size //This remembers the array size
            return this
        endmethod
       
        //Returns the size of the TableArray
        method operator size takes nothing returns integer
            return dex.size[this]
        endmethod
       
        //This magic method enables two-dimensional[array][syntax] for Tables,
        //similar to the two-dimensional utility provided by hashtables them-
        //selves.
        //
        //ta[integer a].unit[integer b] = unit u
        //ta[integer a][integer c] = integer d
        //
        //Inline-friendly when not running in debug mode
        //
        method operator [] takes integer key returns Table
            static if DEBUG_MODE then
                local integer i = this.size
                if i == 0 then
                    call BJDebugMsg("IndexError: Tried to get key from invalid TableArray instance: " + I2S(this))
                    return 0
                elseif key < 0 or key >= i then
                    call BJDebugMsg("IndexError: Tried to get key [" + I2S(key) + "] from outside TableArray bounds: " + I2S(i))
                    return 0
                endif
            endif
            return this + key
        endmethod
       
        //Destroys a TableArray without flushing it; I assume you call .flush()
        //if you want it flushed too. This is a public method so that you don't
        //have to loop through all TableArray indices to flush them if you don't
        //need to (ie. if you were flushing all child-keys as you used them).
        //
        method destroy takes nothing returns nothing
            local Table tb = dex.size[this.size]
           
            debug if this.size == 0 then
                debug call BJDebugMsg("TypeError: Tried to destroy an invalid TableArray: " + I2S(this))
                debug return
            debug endif
           
            if tb == 0 then
                //Create a Table to index recycled instances with their array size
                set tb = Table.create()
                set dex.size[this.size] = tb
            endif
           
            call dex.size.remove(this) //Clear the array size from hash memory
           
            set tb[this] = tb[0]
            set tb[0] = this
        endmethod
       
        private static Table tempTable
        private static integer tempEnd
       
        //Avoids hitting the op limit
        private static method clean takes nothing returns nothing
            local Table tb = .tempTable
            local integer end = tb + 0x1000
            if end < .tempEnd then
                set .tempTable = end
                call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
            else
                set end = .tempEnd
            endif
            loop
                call tb.flush()
                set tb = tb + 1
                exitwhen tb == end
            endloop
        endmethod
       
        //Flushes the TableArray and also destroys it. Doesn't get any more
        //similar to the FlushParentHashtable native than this.
        //
        method flush takes nothing returns nothing
            debug if this.size == 0 then
                debug call BJDebugMsg("TypeError: Tried to flush an invalid TableArray instance: " + I2S(this))
                debug return
            debug endif
            set .tempTable = this
            set .tempEnd = this + this.size
            call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
            call this.destroy()
        endmethod
       
    endstruct
       
    //NEW: Added in Table 4.0. A fairly simple struct but allows you to do more
    //than that which was previously possible.
    struct HashTable extends array

        //Enables myHash[parentKey][childKey] syntax.
        //Basically, it creates a Table in the place of the parent key if
        //it didn't already get created earlier.
        method operator [] takes integer index returns Table
            local Table t = Table(this)[index]
            if t == 0 then
                set t = Table.create()
                set Table(this)[index] = t //whoops! Forgot that line. I'm out of practice!
            endif
            return t
        endmethod

        //You need to call this on each parent key that you used if you
        //intend to destroy the HashTable or simply no longer need that key.
        method remove takes integer index returns nothing
            local Table t = Table(this)[index]
            if t != 0 then
                call t.destroy()
                call Table(this).remove(index)
            endif
        endmethod
       
        //Added in version 4.1
        method has takes integer index returns boolean
            return Table(this).has(index)
        endmethod
       
        //HashTables are just fancy Table indices.
        method destroy takes nothing returns nothing
            call Table(this).destroy()
        endmethod
       
        //Like I said above...
        static method create takes nothing returns thistype
            return Table.create()
        endmethod

    endstruct

    endlibrary
     


    Example usage of HashTable
    Code (vJASS):

    local HashTable hash = HashTable.create() //create it
    set hash['hfoo'][StringHash("poop")] = 66 //access large parent and child keys as needed
    set hash['hfoo'].unit[99999] = GetTriggerUnit() //still works with multiple-type syntax so you still have the full hashtable API.
    call hash.remove('hfoo') //This literally is calling FlushChildHashtable, and should be used when the parentkey and/or HashTable are to be retired
    call hash.destroy() //DOES NOT FLUSH THE HASHTABLE. You must manually remove each parent key, first, otherwise you'll have a lot of leaked Tables.
     


    Backwards Compatibility with Vexorian's Table
    Code (vJASS):

    library TableBC requires Table
    /*
        Backwards-compatibility add-on for scripts employing Vexorian's Table.

        Added 31 July 2015: introduced static method operator [] and
        static method flush2D for Table, HandleTable and StringTable. Now,
        almost all of the Vexorian API has been replicated (minus the .flush paradox).

        The Table library itself was unchanged to implement these
        enhancements, so you need only update this library to experience the
        improved syntax compatibility.
       
        Disclaimer:
       
        The following error does not occur with HandleTables & StringTables, only
        with the standard, integer-based Table, so you do not need to make any
        changes to StringTable/HandleTable-employing scripts.
       
        The this.flush(key) method from the original Table cannot be parsed with
        the new Table. For the scripts that use this method, they need to be up-
        dated to use the more fitting this.remove(key) method.
       
        Please don't try using StringTables/HandleTables with features exclusive
        to the new Table as they will cause syntax errors. I do not have any plan
        to endorse these types of Tables because delegation in JassHelper is not
        advanced enough for three types of Tables without copying every single
        method over again (as you can see this already generates plenty of code).
        StringTable & HandleTable are wrappers for StringHash & GetHandleId, so
        just type them out.
    */


    //! textmacro TABLE_BC_METHODS
        method reset takes nothing returns nothing
            call this.flush()
        endmethod
        method exists takes integer key returns boolean
            return this.has(key)
        endmethod
        static method operator [] takes string id returns Table
            local integer index = StringHash(id)
            local Table t = Table(thistype.typeid)[index]
            if t == 0 then
                set t = Table.create()
                set Table(thistype.typeid)[index] = t
            endif
            return t
        endmethod
        static method flush2D takes string id returns nothing
            local integer index = StringHash(id)
            local Table t = Table(thistype.typeid)[index]
            if t != 0 then
                call t.destroy()
                call Table(thistype.typeid).remove(index)
            endif
        endmethod
    //! endtextmacro

    //! textmacro TABLE_BC_STRUCTS
    struct HandleTable extends array
        static method operator [] takes string index returns thistype
            return Table[index]
        endmethod
        static method flush2D takes string index returns nothing
            call Table.flush2D(index)
        endmethod
        method operator [] takes handle key returns integer
            return Table(this)[GetHandleId(key)]
        endmethod
        method operator []= takes handle key, integer value returns nothing
            set Table(this)[GetHandleId(key)] = value
        endmethod
        method flush takes handle key returns nothing
            call Table(this).remove(GetHandleId(key))
        endmethod
        method exists takes handle key returns boolean
            return Table(this).has(GetHandleId(key))
        endmethod
        method reset takes nothing returns nothing
            call Table(this).flush()
        endmethod
        method destroy takes nothing returns nothing
            call Table(this).destroy()
        endmethod
        static method create takes nothing returns thistype
            return Table.create()
        endmethod
    endstruct

    struct StringTable extends array
        static method operator [] takes string index returns thistype
            return Table[index]
        endmethod
        static method flush2D takes string index returns nothing
            call Table.flush2D(index)
        endmethod
        method operator [] takes string key returns integer
            return Table(this)[StringHash(key)]
        endmethod
        method operator []= takes string key, integer value returns nothing
            set Table(this)[StringHash(key)] = value
        endmethod
        method flush takes string key returns nothing
            call Table(this).remove(StringHash(key))
        endmethod
        method exists takes string key returns boolean
            return Table(this).has(StringHash(key))
        endmethod
        method reset takes nothing returns nothing
            call Table(this).flush()
        endmethod
        method destroy takes nothing returns nothing
            call Table(this).destroy()
        endmethod
        static method create takes nothing returns thistype
            return Table.create()
        endmethod
    endstruct
    //! endtextmacro

    endlibrary
     


    Example Usage of Table:
    Code (vJASS):

    struct table_demo extends array
        private static method demo takes nothing returns nothing
            //Create it:
            local Table a = Table.create()
             
            //Use it:
            local boolean b = a.has(69)
            set a[654321] = 'A'
            set a[54321] = 'B'
            set a.unit[12345] = GetTriggerUnit()
            set a.unit[GetHandleId(a.unit[12345])] = GetSpellTargetUnit()
            set a.real['ABCD'] = 3.14159
            set a.integer[133] = 21

            //remove entries
            call a.handle.remove('ABCD')
            call a.remove(54321)

            //Flush/destroy it:
            call a.destroy()

            //Or, only flush it:
            call a.flush()
        endmethod
    endstruct
     


    Example Usage of TableArray:
    Code (vJASS):

    //Create it:
    local TableArray da = TableArray[0x2000]
     
    //Use it:
    local thistype this = 0
    loop
        set this = this.next
        exitwhen this == 0
        set this.save = this.save + 1
        set da[this].real[this.save * 3] = GetUnitX(this.unit)
        set da[this].real[this.save * 3 + 1] = GetUnitY(this.unit)
        set da[this].real[this.save * 3 + 2] = GetUnitFlyHeight(this.unit)
    endloop
     
    //Flush/destroy it:
    call da.flush()
     
    //Or, only destroy it (more efficient if you manage memory yourself)
    call da.destroy()
     

    Note the power of TableArray, how it splices the one hashtable into a many-dimensional array. This means you can have a multi-dimensional array (and more) per system.
     
    Last edited: Jul 31, 2015
  2. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,478
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Edit: updated
     
    Last edited: Jan 26, 2011
  3. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,143
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Honestly... I'd just do something like
    Code (vJASS):

    library Table
        globals
            private integer c = 0
            private integer array r
        endglobals
        struct Table extends array
            readonly static hashtable get = InitHashtable()
            method operator [] takes integer index returns integer
                return LoadInteger(get, this, index)
            endmethod
            method operator []= takes integer index, integer newVal returns nothing
                call SaveInteger(get, this, index, newVal)
            endmethod
            method has takes integer index returns boolean
                return HaveSavedInteger(get, this, index)
            endmethod
            method remove takes integer index returns nothing
                call RemoveSavedInteger(get, this, index)
            endmethod
            method clear takes nothing returns nothing
                call FlushChildHashtable(get, this)
            endmethod
            static method create takes nothing returns thistype
                local integer i
                if (r[0] == 0) then
                    set c = c + 1
                    return c
                endif
                set i = r[0]
                set r[0] = r[i]
                return i
            endmethod
            method destroy takes nothing returns nothing
                set r[this] = r[0]
                set r[0] = this
                call FlushChildHashtable(get, this)
            endmethod
        endstruct
    endlibrary
     


    Simple and straight to the point. Mass methods for all the different types is ehh ; |. At that point, you might as well use the table directly.

    This is a table script I'd use : \.
     
  4. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,219
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    @Nes: That works for handles and integers but not reals, booleans, strings, and code.

    I think the normal Table lib does it well enough, since it has a textmacro for any other tables that need to be implemented.
     
  5. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,143
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    ->I think the normal Table lib does it well enough, since it has a textmacro for any other tables that need to be implemented.

    it has onDestroy

    Also, you can use the get field to retrieve the hashtable directly ;|, meaning you can work with anything w/o having to spam methods.

    edit
    Interesting idea...

    table.unit[15]

    Code (vJASS):

    private struct UnitTable extends array
    //...
    endstruct

    struct Table extends array

    //...
    method operator unit takes nothing returns UnitTable
        return this
    endmethod

    endstruct
     




    Making textmacros for all of the types
    Code (vJASS):

    library Table
        //! textmacro CREATE_TABLE takes SUPER_TYPE, FUNC_TYPE, TYPE
            private struct $FUNCT_TYPE$Type extends array
                method operator [] takes integer index returns $TYPE$
                    return Load$FUNC_TYPE$(t, this, index)
                endmethod
                method operator []= takes integer index, $TYPE$ newVal returns nothing
                    call Save$FUNC_TYPE$(t, this, index, newVal)
                endmethod
                method has takes integer index returns boolean
                    return HaveSaved$SUPER_TYPE$(t, this, index)
                endmethod
                method remove takes integer index returns nothing
                    call RemoveSaved$SUPER_TYPE$(t, this, index)
                endmethod
            endstruct
            private module $FUNCT_TYPE$Mod
                method operator $TYPE$ takes nothing returns $FUNCT_TYPE$Type
                    return this
                endmethod
            endmodule
        //! endtextmacro

        globals
            private hashtable t = InitHashtable()
            private integer c = 0
            private integer p = 0
            private integer array a
            private integer array q
            private trigger e = CreateTrigger()
            private integer z = 0
        endglobals

        //! runtextmacro CREATE_TABLE("Integer", "Integer", "integer")
        //! runtextmacro CREATE_TABLE("Real", "Real", "real")
        //! runtextmacro CREATE_TABLE("Boolean", "Boolean", "boolean")
        //! runtextmacro CREATE_TABLE("String", "Str", "string")
        //! runtextmacro CREATE_TABLE("Handle", "PlayerHandle", "player")
        //! runtextmacro CREATE_TABLE("Handle", "WidgetHandle", "widget")
        //! runtextmacro CREATE_TABLE("Handle", "DestructableHandle", "destructable")
        //! runtextmacro CREATE_TABLE("Handle", "ItemHandle", "item")
        //! runtextmacro CREATE_TABLE("Handle", "UnitHandle", "unit")
        //! runtextmacro CREATE_TABLE("Handle", "AbilityHandle", "ability")
        //! runtextmacro CREATE_TABLE("Handle", "TimerHandle", "timer")
        //! runtextmacro CREATE_TABLE("Handle", "TriggerHandle", "trigger")
        //! runtextmacro CREATE_TABLE("Handle", "TriggerConditionHandle", "triggercondition")
        //! runtextmacro CREATE_TABLE("Handle", "TriggerActionHandle", "triggeraction")
        //! runtextmacro CREATE_TABLE("Handle", "TriggerEventHandle", "event")
        //! runtextmacro CREATE_TABLE("Handle", "ForceHandle", "force")
        //! runtextmacro CREATE_TABLE("Handle", "GroupHandle", "group")
        //! runtextmacro CREATE_TABLE("Handle", "LocationHandle", "location")
        //! runtextmacro CREATE_TABLE("Handle", "RectHandle", "rect")
        //! runtextmacro CREATE_TABLE("Handle", "BooleanExprHandle", "boolexpr")
        //! runtextmacro CREATE_TABLE("Handle", "SoundHandle", "sound")
        //! runtextmacro CREATE_TABLE("Handle", "EffectHandle", "effect")
        //! runtextmacro CREATE_TABLE("Handle", "UnitPoolHandle", "unitpool")
        //! runtextmacro CREATE_TABLE("Handle", "ItemPoolHandle", "itempool")
        //! runtextmacro CREATE_TABLE("Handle", "QuestHandle", "quest")
        //! runtextmacro CREATE_TABLE("Handle", "QuestItemHandle", "questitem")
        //! runtextmacro CREATE_TABLE("Handle", "DefeatConditionHandle", "defeatcondition")
        //! runtextmacro CREATE_TABLE("Handle", "TimerDialogHandle", "timerdialog")
        //! runtextmacro CREATE_TABLE("Handle", "LeaderboardHandle", "leaderboard")
        //! runtextmacro CREATE_TABLE("Handle", "MultiboardHandle", "multiboard")
        //! runtextmacro CREATE_TABLE("Handle", "MultiboardItemHandle", "multiboarditem")
        //! runtextmacro CREATE_TABLE("Handle", "TrackableHandle", "trackable")
        //! runtextmacro CREATE_TABLE("Handle", "DialogHandle", "dialog")
        //! runtextmacro CREATE_TABLE("Handle", "ButtonHandle", "button")
        //! runtextmacro CREATE_TABLE("Handle", "TextTagHandle", "texttag")
        //! runtextmacro CREATE_TABLE("Handle", "LightningHandle", "lightning")
        //! runtextmacro CREATE_TABLE("Handle", "ImageHandle", "image")
        //! runtextmacro CREATE_TABLE("Handle", "UbersplatHandle", "ubersplat")
        //! runtextmacro CREATE_TABLE("Handle", "RegionHandle", "region")
        //! runtextmacro CREATE_TABLE("Handle", "FogStateHandle", "fogstate")
        //! runtextmacro CREATE_TABLE("Handle", "FogModifierHandle", "fogmodifier")
        //! runtextmacro CREATE_TABLE("Handle", "AgentHandle", "agent")
        //! runtextmacro CREATE_TABLE("Handle", "HashtableHandle", "hashtable")
        struct Table extends array
            implement IntegerMod
            implement RealMod
            implement BooleanMod
            implement StrMod
            implement PlayerHandleMod
            implement WidgetHandleMod
            implement DestructableHandleMod
            implement ItemHandleMod
            implement UnitHandleMod
            implement AbilityHandleMod
            implement TimerHandleMod
            implement TriggerHandleMod
            implement TriggerConditionHandleMod
            implement TriggerActionHandleMod
            implement TriggerEventHandleMod
            implement ForceHandleMod
            implement GroupHandleMod
            implement LocationHandleMod
            implement RectHandleMod
            implement BooleanExprHandleMod
            implement SoundHandleMod
            implement EffectHandleMod
            implement UnitPoolHandleMod
            implement ItemPoolHandleMod
            implement QuestHandleMod
            implement QuestItemHandleMod
            implement DefeatConditionHandleMod
            implement TimerDialogHandleMod
            implement LeaderboardHandleMod
            implement MultiboardHandleMod
            implement MultiboardItemHandleMod
            implement TrackableHandleMod
            implement DialogHandleMod
            implement ButtonHandleMod
            implement TextTagHandleMod
            implement LightningHandleMod
            implement ImageHandleMod
            implement UbersplatHandleMod
            implement RegionHandleMod
            implement FogStateHandleMod
            implement FogModifierHandleMod
            implement AgentHandleMod
            implement HashtableHandleMod

            method clear takes nothing returns nothing
                call FlushChildHashtable(t, this)
            endmethod
            debug private static boolean array a
            debug private static integer overflow = 0
            static method create takes nothing returns thistype
                local integer i
                if (r[0] == 0) then
                    debug if (c < 8191) then
                        set c = c + 1
                        debug set a[c] = true
                        return c
                    debug else
                        debug set overflow = overflow + 1
                        debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Table Error: Instance Overflow " + I2S(overflow) + " times")
                        debug return 0
                    debug endif
                endif
                set i = r[0]
                set r[0] = r[i]
                debug set a[i] = true
                return i
            endmethod
            method destroy takes nothing returns nothing
                debug if (a[this]) then
                    set r[this] = r[0]
                    set r[0] = this
                    debug set a[this] = false
                    call FlushChildHashtable(t, this)
                debug else
                    debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Table Error: Multiply Destroyed Instance " + I2S(this))
                debug endif
            endmethod
        endstruct
        private module TableArrayMod
            private static method onInit takes nothing returns nothing
                call TriggerAddCondition(e, function thistype.doClear)
            endmethod
        endmethod
        struct TableArray extends array
            debug private static boolean array a
            debug private static integer overflow = 0
            method operator [] takes integer index returns Table
                debug if (a[this] then
                debug if index < 8192 then
                    return (this*8192)+index
                debug else
                        debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Table Array Error: Index Out Of Bounds " + I2S(this) + "[" + I2S(index) + "]")
                debug endif
                debug else
                        debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Table Array Error: Null Table Array " + I2S(this))
                debug endif
            endmethod
            static method create takes nothing returns thistype
                local integer i
                if (q[0] == 0) then
                    debug if (p < 8191) then
                        set p = p + 1
                        debug set a[p] = true
                        return p
                    debug else
                        debug set overflow = overflow + 1
                        debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Table Array Error: Instance Overflow " + I2S(overflow) + " times")
                        debug return 0
                    debug endif
                endif
                set i = q[0]
                set q[0] = q[i]
                debug set a[i] = true
                return i
            endmethod
            private static method doClear takes nothing returns boolean
                local integer i = z + 8191
                local integer m = z
                loop
                    call FlushChildHashtable(t, i)
                    exitwhen i == m
                    set i = i - 1
                endloop
                return false
            endmethod
            method destroy takes nothing returns nothing
                debug if (a[this]) then
                    set q[this] = q[0]
                    set q[0] = this
                    debug set a[this] = false
                    set z = this*8192
                    call TriggerEvaluate(e)
                debug else
                    debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Table Array Error: Multiply Destroyed Instance " + I2S(this))
                debug endif
            endmethod
            method clear takes nothing returns nothing
                set z = this*8192
                call TriggerEvaluate(e)
            endmethod
        endstruct
    endlibrary
     


    Usage
    Code (vJASS):

    struct Example extends array
       
        static TableArray tableArray
       
        static method onInit takes nothing returns nothing
            set tableArray = TableArray.create() // New TableArray of size 8192, can be destroyed
        endmethod

        static method loopAll takes nothing returns nothing
            local integer i = 15
            loop
                set tableArray[i].integer[0] = 'A'
                set tableArray[i].integer[1] = 'B'
                set tableArray[i].real[2] = 3.14159
                set tableArray[i].real[3] = 90
                exitwhen i == 0
                set i = i - 1
            endloop
            call tableArray.clear()
        endmethod
    endstruct
     
     
    Last edited: Jan 26, 2011
  6. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,478
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Nestharus, having access to a public hashtable makes it unreliable for systems to access keys without risking overwriting by conflicting key handling. I originally had the ability to destroy Table instances but removed it in favor of not limiting the number of destroyed instances to 8191. I can add it back if this limit doesn't matter in practical cases.

    Some pros and cons between this and Vexorian's Table:

    Vex: Hides the syntax of GetHandleId and StringHash with HandleTable and StringTable.
    This: Doesn't generate wasted methods when the user can easily type GetHandleId or StringHash

    Vex: Can only save/load integers
    This: Can save/load integers and reals

    Vex: 2-D Parent Key access is by strings, which are incredibly slow and can conflict unless the string keys are properly name-spaced.
    This: Can instantiate a ParentTable to do a 2-D array of arbitrary size for each system that wishes to use it, partitioning the parent keys to each system without conflicting key usage. ParentTable[8190] reserves a Table for each instance in your structs.

    Code (vJASS):

    set pt = ParentTable[8190]
    set pt[this] = someInt
     


    It may take some analyzation to get the hang of it, but this has the potential for extremely dynamic use. My next release of Retro will be using this, to show off its capability to be a very useable 4-dimensional array.
     
  7. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,143
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    If you didn't notice, I updated the code on the second last reply (you did notice though).

    It includes a list of all of the hashtable natives converted into a struct type format to be used with 1 parent hashtable. Useful if you only need 1 index and all of the stuff inlines except for create/destroy.

    With support of entire Hashtable API, there's no reason for a person not to use this script if they only need one index for their hashtable ;P.
     
    Last edited: Jan 26, 2011
  8. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,478
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Updated to 1.0.0.0, the version I am satisfied with and the only API that will change would be additions at this point, but nothing will be done at this point to break backwards compatibility.

    Four new things:

    1. All types are now able to be saved
    2. All types have beautiful syntax as Nestharus provided:
    Code (vJASS):

    tb.unit[this] = GetTriggerUnit()
    tb.real[that] = 3.14159
     

    3. Table instances can now be destroyed
    4. ParentTable instances can now be flushed
     
    Last edited: Jan 29, 2011
  9. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,478
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Updated to 1.1.0.0, ParentTable instances can now be destroyed. Calling .flush() on a ParentTable will also destroy it, I just separated the two methods so that if you were flushing instances the whole way around you can still just deallocate the ParentTable's index point.

    With this version it is now very easy to use a dynamic ParentTable of any specified size to do your bidding, and is incredibly, incredibly efficient.
     
  10. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,143
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    This is bugged with ParentTable, don't have time to go into detail atm. This has to do with the fact that the sizes can be anything, so you can't just add to the end. What you'd end up having to do is have different sets of recycles for different sizes ;|.

    This is silly
    Code (vJASS):

        private integer last = 0
        private integer array list
     
    .

    You only need the array, not the last. Just treat the array like a stack...

    I also suggest that you use different instances for ParentArrays and TableArrays, otherwise you are seriously going to screw up your tables. I had it right in my code >.>.


    edit
    Since you are determined to have dynamic sizes for array tables, this is an update that'll do it properly ; |.

    Fixed all of the syntax errors too ;P.

    edit
    nvm, our libs do like the same thing ;P, but yours will have a syntax error with agents as there is no LoadAgentHandle.

    Also mine includes tons of debug messages ;o.
    Code (vJASS):

    library Table
        //! textmacro CREATE_TABLE takes SUPER_TYPE, FUNC_TYPE, TYPE, LOAD
            private struct $FUNC_TYPE$Type extends array
                static if $LOAD$ then
                    method operator [] takes integer index returns $TYPE$
                        return Load$FUNC_TYPE$(t, this, index)
                    endmethod
                endif
                method operator []= takes integer index, $TYPE$ newVal returns nothing
                    call Save$FUNC_TYPE$(t, this, index, newVal)
                endmethod
                method has takes integer index returns boolean
                    return HaveSaved$SUPER_TYPE$(t, this, index)
                endmethod
                method remove takes integer index returns nothing
                    call RemoveSaved$SUPER_TYPE$(t, this, index)
                endmethod
            endstruct
            private module $FUNC_TYPE$Mod
                method operator $TYPE$ takes nothing returns $FUNC_TYPE$Type
                    return this
                endmethod
            endmodule
        //! endtextmacro

        globals
            private hashtable t = InitHashtable()
            private integer c = 0
            private integer p = 0
            private integer array r
            private trigger e = CreateTrigger()
            private integer z = 0
            private integer h = 0
        endglobals

        //! runtextmacro CREATE_TABLE("Integer", "Integer", "integer", "true")
        //! runtextmacro CREATE_TABLE("Real", "Real", "real", "true")
        //! runtextmacro CREATE_TABLE("Boolean", "Boolean", "boolean", "true")
        //! runtextmacro CREATE_TABLE("String", "Str", "string", "true")
        //! runtextmacro CREATE_TABLE("Handle", "PlayerHandle", "player", "true")
        //! runtextmacro CREATE_TABLE("Handle", "WidgetHandle", "widget", "true")
        //! runtextmacro CREATE_TABLE("Handle", "DestructableHandle", "destructable", "true")
        //! runtextmacro CREATE_TABLE("Handle", "ItemHandle", "item", "true")
        //! runtextmacro CREATE_TABLE("Handle", "UnitHandle", "unit", "true")
        //! runtextmacro CREATE_TABLE("Handle", "AbilityHandle", "ability", "true")
        //! runtextmacro CREATE_TABLE("Handle", "TimerHandle", "timer", "true")
        //! runtextmacro CREATE_TABLE("Handle", "TriggerHandle", "trigger", "true")
        //! runtextmacro CREATE_TABLE("Handle", "TriggerConditionHandle", "triggercondition", "true")
        //! runtextmacro CREATE_TABLE("Handle", "TriggerActionHandle", "triggeraction", "true")
        //! runtextmacro CREATE_TABLE("Handle", "TriggerEventHandle", "event", "true")
        //! runtextmacro CREATE_TABLE("Handle", "ForceHandle", "force", "true")
        //! runtextmacro CREATE_TABLE("Handle", "GroupHandle", "group", "true")
        //! runtextmacro CREATE_TABLE("Handle", "LocationHandle", "location", "true")
        //! runtextmacro CREATE_TABLE("Handle", "RectHandle", "rect", "true")
        //! runtextmacro CREATE_TABLE("Handle", "BooleanExprHandle", "boolexpr", "true")
        //! runtextmacro CREATE_TABLE("Handle", "SoundHandle", "sound", "true")
        //! runtextmacro CREATE_TABLE("Handle", "EffectHandle", "effect", "true")
        //! runtextmacro CREATE_TABLE("Handle", "UnitPoolHandle", "unitpool", "true")
        //! runtextmacro CREATE_TABLE("Handle", "ItemPoolHandle", "itempool", "true")
        //! runtextmacro CREATE_TABLE("Handle", "QuestHandle", "quest", "true")
        //! runtextmacro CREATE_TABLE("Handle", "QuestItemHandle", "questitem", "true")
        //! runtextmacro CREATE_TABLE("Handle", "DefeatConditionHandle", "defeatcondition", "true")
        //! runtextmacro CREATE_TABLE("Handle", "TimerDialogHandle", "timerdialog", "true")
        //! runtextmacro CREATE_TABLE("Handle", "LeaderboardHandle", "leaderboard", "true")
        //! runtextmacro CREATE_TABLE("Handle", "MultiboardHandle", "multiboard", "true")
        //! runtextmacro CREATE_TABLE("Handle", "MultiboardItemHandle", "multiboarditem", "true")
        //! runtextmacro CREATE_TABLE("Handle", "TrackableHandle", "trackable", "true")
        //! runtextmacro CREATE_TABLE("Handle", "DialogHandle", "dialog", "true")
        //! runtextmacro CREATE_TABLE("Handle", "ButtonHandle", "button", "true")
        //! runtextmacro CREATE_TABLE("Handle", "TextTagHandle", "texttag", "true")
        //! runtextmacro CREATE_TABLE("Handle", "LightningHandle", "lightning", "true")
        //! runtextmacro CREATE_TABLE("Handle", "ImageHandle", "image", "true")
        //! runtextmacro CREATE_TABLE("Handle", "UbersplatHandle", "ubersplat", "true")
        //! runtextmacro CREATE_TABLE("Handle", "RegionHandle", "region", "true")
        //! runtextmacro CREATE_TABLE("Handle", "FogStateHandle", "fogstate", "true")
        //! runtextmacro CREATE_TABLE("Handle", "FogModifierHandle", "fogmodifier", "true")
        //! runtextmacro CREATE_TABLE("Handle", "AgentHandle", "agent", "false")
        //! runtextmacro CREATE_TABLE("Handle", "HashtableHandle", "hashtable", "true")
        struct Table extends array
            implement IntegerMod
            implement RealMod
            implement BooleanMod
            implement StrMod
            implement PlayerHandleMod
            implement WidgetHandleMod
            implement DestructableHandleMod
            implement ItemHandleMod
            implement UnitHandleMod
            implement AbilityHandleMod
            implement TimerHandleMod
            implement TriggerHandleMod
            implement TriggerConditionHandleMod
            implement TriggerActionHandleMod
            implement TriggerEventHandleMod
            implement ForceHandleMod
            implement GroupHandleMod
            implement LocationHandleMod
            implement RectHandleMod
            implement BooleanExprHandleMod
            implement SoundHandleMod
            implement EffectHandleMod
            implement UnitPoolHandleMod
            implement ItemPoolHandleMod
            implement QuestHandleMod
            implement QuestItemHandleMod
            implement DefeatConditionHandleMod
            implement TimerDialogHandleMod
            implement LeaderboardHandleMod
            implement MultiboardHandleMod
            implement MultiboardItemHandleMod
            implement TrackableHandleMod
            implement DialogHandleMod
            implement ButtonHandleMod
            implement TextTagHandleMod
            implement LightningHandleMod
            implement ImageHandleMod
            implement UbersplatHandleMod
            implement RegionHandleMod
            implement FogStateHandleMod
            implement FogModifierHandleMod
            implement AgentHandleMod
            implement HashtableHandleMod

            method clear takes nothing returns nothing
                call FlushChildHashtable(t, this)
            endmethod
            debug private static boolean array a
            debug private static integer overflow = 0
            static method create takes nothing returns thistype
                local integer i
                if (r[0] == 0) then
                    debug if (c < 8190) then
                        set c = c + 1
                        debug set a[c] = true
                        return c
                    debug else
                        debug set overflow = overflow + 1
                        debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Table Error: Instance Overflow " + I2S(overflow) + " times")
                        debug return 0
                    debug endif
                endif
                set i = r[0]
                set r[0] = r[i]
                debug set a[i] = true
                return i
            endmethod
            method destroy takes nothing returns nothing
                debug if (a[this]) then
                    set r[this] = r[0]
                    set r[0] = this
                    debug set a[this] = false
                    call FlushChildHashtable(t, this)
                debug else
                    debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Table Error: Multiply Destroyed Instance " + I2S(this))
                debug endif
            endmethod
        endstruct
        private module TableArrayMod
            private static method onInit takes nothing returns nothing
                call TriggerAddCondition(e, function thistype.doClear)
            endmethod
        endmodule
        struct TableArray extends array
            debug private static boolean array a
            debug private static integer overflow = 0
            readonly thistype size
            method operator [] takes integer index returns Table
                debug if (a[this]) then
                debug if index < integer(size) then
                    return (this+8191)+index
                debug else
                        debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Table Array Error: Index Out Of Bounds " + I2S(this) + "[" + I2S(index) + "]")
                debug endif
                debug else
                        debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Table Array Error: Null Table Array " + I2S(this))
                debug endif
                debug return 0
            endmethod
            static method operator [] takes integer size returns thistype
                local integer i = LoadInteger(t, -size, 0)
                debug if (size > 0) then
                    if (i == 0) then
                        debug if (p < 8190) then
                            set p = p + 1
                            debug set a[p] = true
                            return p
                        debug else
                            debug set overflow = overflow + 1
                            debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Table Array Error: Instance Overflow " + I2S(overflow) + " times")
                            debug return 0
                        debug endif
                    endif
                    call SaveInteger(t, -size, 0, i-1)
                    set i = LoadInteger(t, -size, i-1)
                    set thistype(i).size = size
                    debug set a[i] = true
                    return i
                debug else
                    debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Table Array Error: Invalid Size " + I2S(size))
                debug endif
                debug return 0
            endmethod
            private static method doClear takes nothing returns boolean
                local integer i = h
                local integer m = h-5000
                if (m < z) then
                    set m = z
                endif
                loop
                    call FlushChildHashtable(t, i)
                    exitwhen i == m
                    set i = i - 1
                endloop
                if (i != z) then
                    set h = i
                    call TriggerEvaluate(e)
                endif
                return false
            endmethod
            method destroy takes nothing returns nothing
                local integer i
                debug if (a[this]) then
                    set i = LoadInteger(t, -size, 0)+1
                    call SaveInteger(t, -size, i, this)
                    call SaveInteger(t, -size, 0, i)
                    debug set a[this] = false
                    set z = this+8191
                    set h = size+z-1
                    call TriggerEvaluate(e)
                    set size = 0
                debug else
                    debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Table Array Error: Multiply Destroyed Instance " + I2S(this))
                debug endif
            endmethod
            method clear takes nothing returns nothing
                set z = this+8191
                set h = size+z-1
                call TriggerEvaluate(e)
            endmethod
        endstruct
    endlibrary
     


    Demo
    Code (vJASS):

    function Test takes nothing returns nothing
        local TableArray table = TableArray[16] //0-15
        set table[0].integer[1] = 5
        set table[15].real[6] = 1.1
        call table.destroy()
    endfunction
     
     
    Last edited: Jan 28, 2011
  11. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,478
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Yesterday I made a silent update to correct the syntax error (didn't have enough time to post an update message).

    Previously typecasting would be required to save a Table within a Table. Now, the original Table syntax is supported as well as storing Tables within Tables. One day I might remove the .integer syntax if no one is using it.

    Something like
    set pt[this][10]['A']['G'].unit[1] = GetTriggerUnit()
    is now supported (obviously the example is an exaggeration).

    For those reading this in the future, Nestharus and I were chatting about the mechanics of this library for a while behind the scenes and he gave some good feedback, just disregard the bits about ParentTable bugging because that was never the scenario.
     
  12. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,478
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    This is a readability update so that people can follow along my thinking with ParentTable.
     
  13. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,143
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    integer(this)
    is silly... you only need to do that for comparisons like > or == or < or w/e.

    This should be a debug statement (all safety should be in debug)
    if (this > 0 and lpos < 8191) then
    .


    You should also be clearing data on destroy rather than on create to save on memory and to keep hashtables running as fast as possible on the lookups.


    Also, it'd be nice to have more debug messages (debug on every possible error please). This really can help a lot of people.


    Also, I think that TableArray is a better name that ParrentTable personally, but eh... JASS kinda of breaks how hashtables are normally done. Hashtables have 1 index, not 2, lol...

    Example from c#
    Code (vJASS):

            // Add some elements to the hash table. There are no
            // duplicate keys, but some of the values are duplicates.
            openWith.Add("txt", "notepad.exe");
            openWith.Add("bmp", "paint.exe");
            openWith.Add("dib", "paint.exe");
            openWith.Add("rtf", "wordpad.exe");
     


    But anyways, if we want to follow JASS convention, Table would be a bad name for the first struct. In fact, it should be something like Hashtable for the second and I don't know what for the first, lol. That's why I just stuck with Table and TableArray (if you're going to break it, might as well go all the way ;P).

    Table->??
    ParentTable->Hashtable

    But then again, you are defining a size, so it wouldn't actually be a hashtable would it? (sneaky sneaky). It's more like an array actually. TableArray or HashtableArray is a much better name than ParentTable.

    Now, if you really want to keep ParentTable, there is a way (you have a hashtable on top of the hashtable, which would be ugly, but oh well, lol).


    So my suggestion is Table and TableArray or ???? and ????HashtableArray. As it stands, neither name reflects what the object actually is.

    Maybe MiniHashtable and MiniHashtableArray? Who knows.

    Also, if we really want to stick with the JASS style, you shouldn't be able to set the array size ;p, but array size is a cool feature that JASS should have probably had, so keep it in there =).


    Really need to come up with a specific style and specific objects for JASS since we have all of this chaos with JASS and vJASS : |. Procedural + OO programming = a spaghetti code, not that good JASS isn't spaghetti code (oddly enough -.-), lol.
     
  14. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,478
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    integer(this) is just the format Vex was using and I thought it looked trendy.

    Clearing data on destroy? I'm clearing data on destroy all the time.

    this > 0 and lpos < 8191 should not be on debug because it doesn't matter if the array can't recycle instances, I just throw out ones if the stack is full (integers can go into the trillions, who cares?)
     
  15. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,143
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Ah, your comments threw me off on the destroy, lol...

    What about the other points? : P
     
  16. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,478
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Table and TableArray would certainly represent the naming convention without problems, it's just that Blizzard called the first key parentKeys and the second key childKeys, and FlushChildHashtable/FlushParentHashtable, so I stuck with the naming convention ParentTable. At this point no one has mentioned they will be working with this library so breaking backwards compatibility isn't going to affect anyone I know of.

    I'm about to break backwards compatibility with this next update, and removing that .integer syntax that is now so useless.
     
  17. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,478
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Backwards compatibility broken. After reviewing it a few times, 2.0.0.0 looks appropriate and packed with good debug checking.
     
  18. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,478
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Created a description for anyone who was too confused about my rubbish shorthand programming style.
     
  19. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,143
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    This script is loaded with syntax errors and I don't trust it to be logically correct >.>.

    This is w/o syntax errors
    Code (vJASS):

    library Table // made by Bribe, special thanks to Nestharus, v2.0.0.0
       
    globals
        private hashtable ht = InitHashtable() // The last hashtable you need
        private integer size = 2 // Index generation for Tables (above 2)
        private integer keys = 0 // Index generation for TableArrays (below 0)
        private integer array list
        private integer lpos = 0 // These two are used to recycle Tables
    endglobals
       
    //! textmacro NewTable takes SUPER, FUNC, TYPE, LOAD
    private struct $TYPE$s extends array
        static if ($LOAD$) then
            method operator [] takes integer key returns $TYPE$
                return Load$FUNC$(ht, this, key)
            endmethod
        endif
        method operator []= takes integer key, $TYPE$ value returns nothing
            call Save$FUNC$(ht, this, key, value)
        endmethod
        method has takes integer key returns boolean
            return HaveSaved$SUPER$(ht, this, key)
        endmethod
        method remove takes integer key returns nothing
            call RemoveSaved$SUPER$(ht, this, key)
        endmethod
    endstruct
    private module $TYPE$m
        method operator $TYPE$ takes nothing returns $TYPE$s
            return this
        endmethod
    endmodule
    //! endtextmacro
       
    //! runtextmacro NewTable("Real", "Real", "real", "true")
    //! runtextmacro NewTable("Boolean", "Boolean", "boolean", "true")
    //! runtextmacro NewTable("String", "Str", "string", "true")
    //! runtextmacro NewTable("Handle", "PlayerHandle", "player", "true")
    //! runtextmacro NewTable("Handle", "WidgetHandle", "widget", "true")
    //! runtextmacro NewTable("Handle", "DestructableHandle", "destructable", "true")
    //! runtextmacro NewTable("Handle", "ItemHandle", "item", "true")
    //! runtextmacro NewTable("Handle", "UnitHandle", "unit", "true")
    //! runtextmacro NewTable("Handle", "AbilityHandle", "ability", "true")
    //! runtextmacro NewTable("Handle", "TimerHandle", "timer", "true")
    //! runtextmacro NewTable("Handle", "TriggerHandle", "trigger", "true")
    //! runtextmacro NewTable("Handle", "TriggerConditionHandle", "triggercondition", "true")
    //! runtextmacro NewTable("Handle", "TriggerActionHandle", "triggeraction", "true")
    //! runtextmacro NewTable("Handle", "TriggerEventHandle", "event", "true")
    //! runtextmacro NewTable("Handle", "ForceHandle", "force", "true")
    //! runtextmacro NewTable("Handle", "GroupHandle", "group", "true")
    //! runtextmacro NewTable("Handle", "LocationHandle", "location", "true")
    //! runtextmacro NewTable("Handle", "RectHandle", "rect", "true")
    //! runtextmacro NewTable("Handle", "BooleanExprHandle", "boolexpr", "true")
    //! runtextmacro NewTable("Handle", "SoundHandle", "sound", "true")
    //! runtextmacro NewTable("Handle", "EffectHandle", "effect", "true")
    //! runtextmacro NewTable("Handle", "UnitPoolHandle", "unitpool", "true")
    //! runtextmacro NewTable("Handle", "ItemPoolHandle", "itempool", "true")
    //! runtextmacro NewTable("Handle", "QuestHandle", "quest", "true")
    //! runtextmacro NewTable("Handle", "QuestItemHandle", "questitem", "true")
    //! runtextmacro NewTable("Handle", "DefeatConditionHandle", "defeatcondition", "true")
    //! runtextmacro NewTable("Handle", "TimerDialogHandle", "timerdialog", "true")
    //! runtextmacro NewTable("Handle", "LeaderboardHandle", "leaderboard", "true")
    //! runtextmacro NewTable("Handle", "MultiboardHandle", "multiboard", "true")
    //! runtextmacro NewTable("Handle", "MultiboardItemHandle", "multiboarditem", "true")
    //! runtextmacro NewTable("Handle", "TrackableHandle", "trackable", "true")
    //! runtextmacro NewTable("Handle", "DialogHandle", "dialog", "true")
    //! runtextmacro NewTable("Handle", "ButtonHandle", "button", "true")
    //! runtextmacro NewTable("Handle", "TextTagHandle", "texttag", "true")
    //! runtextmacro NewTable("Handle", "LightningHandle", "lightning", "true")
    //! runtextmacro NewTable("Handle", "ImageHandle", "image", "true")
    //! runtextmacro NewTable("Handle", "UbersplatHandle", "ubersplat", "true")
    //! runtextmacro NewTable("Handle", "RegionHandle", "region", "true")
    //! runtextmacro NewTable("Handle", "FogStateHandle", "fogstate", "true")
    //! runtextmacro NewTable("Handle", "FogModifierHandle", "fogmodifier", "true")
    //! runtextmacro NewTable("Handle", "AgentHandle", "agent", "false")
    //! runtextmacro NewTable("Handle", "HashtableHandle", "hashtable", "true")

    struct Table extends array
       
        // Implement modules for intuitive type-syntax
        implement realm
        implement booleanm
        implement stringm
        implement playerm
        implement widgetm
        implement destructablem
        implement itemm
        implement unitm
        implement abilitym
        implement timerm
        implement triggerm
        implement triggerconditionm
        implement triggeractionm
        implement eventm
        implement forcem
        implement groupm
        implement locationm
        implement rectm
        implement boolexprm
        implement soundm
        implement effectm
        implement unitpoolm
        implement itempoolm
        implement questm
        implement questitemm
        implement defeatconditionm
        implement timerdialogm
        implement leaderboardm
        implement multiboardm
        implement multiboarditemm
        implement trackablem
        implement dialogm
        implement buttonm
        implement texttagm
        implement lightningm
        implement imagem
        implement ubersplatm
        implement regionm
        implement fogstatem
        implement fogmodifierm
        implement agentm
        implement hashtablem
       
        debug private static integer debugOverflow = 0
       
        // set this = tb[GetSpellAbilityId()]
        method operator [] takes integer key returns Table
            return LoadInteger(ht, this, key)
        endmethod
       
        // set tb[389034] = 8192
        method operator []= takes integer key, Table tb returns nothing
            call SaveInteger(ht, this, key, tb)
        endmethod
       
        // set b = tb.has(2493223)
        method has takes integer key returns boolean
            return HaveSavedInteger(ht, this, key)
        endmethod
       
        // call tb.remove(294080)
        method remove takes integer key returns nothing
            call RemoveSavedInteger(ht, this, key)
        endmethod
       
        // Remove all data from a Table instance
        method flush takes nothing returns nothing
            call FlushChildHashtable(ht, this)
        endmethod
       
        // local Table tb = Table.create()
        static method create takes nothing returns Table
            if (lpos == 0) then
                set size = size + 1
                return size
            endif
            set lpos = lpos - 1
            debug call Table(2).boolean.remove(list[lpos])
            return list[lpos]
        endmethod
       
        // Removes all data from a Table instance and recycles its index.
        //
        //     call tb.destroy()
        //
        method destroy takes nothing returns nothing
            call this.flush()
            static if (DEBUG_MODE) then
                if (integer(this) < 3) then
                    call BJDebugMsg("Table Error: Tried to destroy an invalid Table instance: " + I2S(this))
                    return
                elseif (Table(2).boolean[this]) then
                    call BJDebugMsg("Table Error: Tried to double-free instance: " + I2S(this))
                    return
                endif
                // The reserved Table(2) index detects double-free of instances
                // if running in debug mode.
                set Table(2).boolean[this] = true
            endif
            if (lpos < 8191) then
                set list[lpos] = this
                set lpos = lpos + 1
            debug else
                debug set debugOverflow = debugOverflow + 1
                debug call BJDebugMsg("Table Error: Instance" + I2S(this) + " could not fit in the recycle stack. Overflows: " + I2S(debugOverflow))
            endif
        endmethod
       
    endstruct
       
    struct TableArray extends array
       
        // Returns a new TableArray to do your bidding. Simply use:
        //
        //     local TableArray ta = TableArray[arraySize]
        //
        static method operator [] takes integer arraySize returns TableArray
            local Table tb = Table(1)[arraySize] // Table(1) indexes arraySizes
            local TableArray ta                  // and instances.
            local integer i
            debug if (arraySize <= 0) then
                debug call BJDebugMsg("TableArray Error: Invalid specified array size: " + I2S(arraySize))
                debug return 0
            debug endif
            if (tb == 0 or tb[0] == 0) then
                set keys = keys - arraySize    // Negative values are reserved...
                set Table(1)[keys] = arraySize // This remembers the array size
                set ta = keys                  // All TableArray IDs are below 0
            else
                set i = tb[0]     // Get the last-destroyed TableArray's index
                call tb.remove(0) // Clear data as we go along
                set tb[0] = i - 1 // Decrease and save the recycle count
                set ta = tb[i]    // Retrieve the old TableArray's instance
                call tb.remove(i) // Remove the old TableArray's node
                debug call Table(2).boolean.remove(ta)
            endif
            return ta
        endmethod
       
        // Returns the size of the TableArray (arraySize)
        method operator size takes nothing returns integer
            return Table(1)[this]
        endmethod
       
        // ta[integer a].unit[integer b] = unit u
        // ta[integer a][integer c] = integer d
        //
        // Inline-friendly when not running in debug mode
        //
        method operator [] takes integer key returns Table
            static if (DEBUG_MODE) then
                if (this >= 0) then
                    call BJDebugMsg("TableArray Error: " + I2S(this) + " is not a valid TableArray instance")
                    return 0
                endif
                if (key < 0 or key >= this.size) then
                    call BJDebugMsg("TableArray Error: Tried to lookup key [" + I2S(key) + "] which is outside array bounds [" + I2S(this.size) + "]")
                    return 0
                endif
            endif
            return this + key
        endmethod
       
        // Destroys a TableArray without flushing it; assumed you'd call .flush()
        // if you want it flushed too. This is public so that if you are flushing
        // instances the whole time you don't waste efficiency when disposing the
        // TableArray.
        //
        method destroy takes nothing returns nothing
            local integer i
            local Table tb = Table(1)[this.size]
            static if (DEBUG_MODE) then
                if (this >= 0 or this.size <= 0) then
                    call BJDebugMsg("TableArray Error: Tried to destroy an invalid instance (" + I2S(this) + ")")
                    return
                elseif (Table(2).boolean[this]) then
                    call BJDebugMsg("TableArray Error: Tried to double-free instance: " + I2S(this))
                    return
                endif
                set Table(2).boolean[this] = true
            endif
            if (tb == 0) then
                set tb = Table.create()      // A Table to remember old indexes
                set Table(1)[this.size] = tb // Save it in the reserved key (1)
                set i = 1                    // The recycle count is initially 1
            else
                set i = tb[0] + 1 // Increase recycle count
                call tb.remove(0) // Remove the "recycle count" node
            endif
            set tb[0] = i    // Save recycle count
            set tb[i] = this // Save this under recycle count's index
        endmethod
       
        // Flushes the TableArray and also destroys it. Doesn't get any more
        // similar to the FlushParentHashtable native than this.
        //
        method flush takes nothing returns nothing
            local integer end = this.size + this
            if (integer(this) < end) then
                call TableArray.clean(this, end)
                call this.destroy()
            debug else
                debug call BJDebugMsg("TableArray Error: Tried to flush an invalid instance (" + I2S(this) + ")")
            endif
        endmethod
       
        // All you need to know about this one is that it won't hit the op limit.
        private static method clean takes Table tb, integer end returns nothing
            local integer i = tb + 4096
            if (i < end) then
                call clean.evaluate(i, end)
                set end = i
            endif
            loop
                call tb.flush()
                set tb = tb + 1
                exitwhen tb == end
            endloop
        endmethod
       
    endstruct
       
    endlibrary
     


    I was about to use this script, but the syntax errors made me stop cuz that tells me it's 100% untested ^_^.


    When you make sure that all of its features work (no leaks on destroy, etc), I'll be sure to use it ;D. Was going to update Base script to use it.
     
  20. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,478
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Corrected for the syntax errors. Like I said, can't get NewGen to install on this computer, so how can I test?

    The logic is there and works fine. The syntax errors were easy to make, you just corrected a couple of typos in the macros and wrapped "this" in integer format.