1. The poll for our 11th Music Contest is up! Help us choose the most awesome cinematic tracks by casting a vote!
    Dismiss Notice
  2. Melee Mapping contest #3 - Poll is up! Vote for the best 4v4 melee maps!
    Dismiss Notice
  3. The 30th edition of the Modeling Contest is finally up! The Portable Buildings need your attention, so come along and have a blast!
    Dismiss Notice
  4. The Aftermath has been revealed for the 19th Terraining Contest! Be sure to check out the Results and see what came out of it.
    Dismiss Notice

[vJASS] ArrayList

Discussion in 'Submissions' started by Pinzu, Feb 7, 2019.

  1. Pinzu

    Pinzu

    Joined:
    Nov 30, 2007
    Messages:
    1,148
    Resources:
    3
    Spells:
    2
    Tutorials:
    1
    Resources:
    3
    This is a Array List container using a hashtable for storage to allow for a virtually endless amount of lists. It contains normal list interface, stack interface and a few utility methods such as sort, contains, containsAll, indexOf, setRange, addRange, swap, etc.

    The principle behind this library is that the API should be as accomodating as possible, and not minimalistic, so if you have any ideas feel free to post suggested changes and argue your case for a new feature.

    Code (vJASS):

    library ArrayList requires optional HashRecyler
    /*
        Made by: Pinzu
     
        Version: 1.2
     
        This is a array list container utilizing a hashtable for storage, making it possible to allocate a virtually unlimited amount of lists.
        It contains regular list interface, stack interface, and a few utility methods such as sort, addMatching, removeMatching, copy, swap, etc.

        Credits:
            Bannar for his ListT library which inspired this.
                https://www.hiveworkshop.com/threads/containers-list-t.249011/
       
            Bribe for his Table library which the HashRecyler is based on.
                https://www.hiveworkshop.com/threads/snippet-new-table.188084/
           
            Overfrost for suggesting improvements.

    */


        // ******************************************************************************************************** \\
        //    API
        // ******************************************************************************************************** \\
     
        //! novjass
     
        struct $NAME$ArrayList extends array
     
            readonly static $TYPE$ pivot     // Pivot element during sort or comparison  
            readonly static $TYPE$ other     // Other element during sort
       
            // Allocates the list.
            static method create takes nothing returns thistype
     
            // Deallocates the list.
            method destroy takes nothing returns nothing
     
            //    Removes all elements from the list.
            method clear takes nothing returns nothing
       
            // Returns the number of elements in the list.
            method operator length takes nothing returns integer
       
            // Returns the element at the specified position.
            method operator[] takes integer index returns $TYPE$
       
            // Replaces the element at the specified position in the list with the specified element.
            method operator[]= takes integer index, $TYPE$ element returns nothing
       
            // Returns the value at the first position of the list
            method first takes nothing returns $TYPE$
       
            // Returns the value at the last position of the list
            method last takes nothing returns $TYPE$
     
            // Inserts the specified element at the specified position in the list.
            method add takes integer index, $TYPE$ element returns nothing

            // Removes the element at the specified position in the the list, returning the value.
            method remove takes integer index returns $TYPE$
       
            // Inserts the element to the last position of the list (stack interface).
            method push takes $TYPE$ value returns thistype
       
            // Removes the element at the last position of the list, returning the value (stack interface).
            method pop takes $TYPE$ value returns $TYPE$
       
            // Returns the index position of the first occurrence of the specified element in the list, or -1 if the list does not contain the element.
            method indexOf takes $TYPE$ value returns integer

            // Returns true if the index is storing any value (table interface).
            method has takes integer index returns boolean
       
            // Returns true if the list contains the specified element.
            method contains takes $TYPE$ value returns boolean
       
            // Returns true if the calling list contains all elements in the specified list.
            method containsAll takes thistype list returns boolean
       
            //Allocates a new list containing shallow copies of all elements held currently.
            method clone takes nothing returns thistype
       
            // Returns true if the list contains no elements.
            method isEmpty takes nothing returns boolean
       
            // Swaps position of 2 elements at the specified positions.
            method swap takes integer indexA, integer indexB returns nothing

            // Returns true if two lists are equal, same length and same structure.
            method equals takes thistype list returns boolean
       
            // Sorts the list after the provided condition (c). Note that the comparison must use the variables 'pivot' and 'other'.
            // Furthermore, only '>' or '<' are valid operators for the sorting to work. Example:
       
                private function SortUnitsByPlayer takes nothing returns boolean
                    return GetPlayerId(GetOwningPlayer(UnitArrayList.pivot)) < GetPlayerId(GetOwningPlayer(UnitArrayList.other))
                endfunction
           
            method sort takes code c returns nothing
       
            // Reverses the order of all elements inside the list.
            method reverse takes nothing returns nothing
       
            // Changes the values of all assigned elements from the start position to the end of the range to the specified new value.
            method setRange takes integer start, integer range, $TYPE$ value returns nothing
       
            // Will perform a shallow copy of all elements from the specified source list to the calling list, with the given range. Starting
            // from the specified source position, inserting to the specified destination position.
            method addRange takes integer destPos, thistype src, integer srcPos, integer range returns thistype
       
        //! endnovjass

        // ******************************************************************************************************** \\
        //    Shared Resources
        // ******************************************************************************************************** \\
     
        globals
            private constant integer SIZE_INDEX = -2 // Should be less than -1 to not risk collision with anything else
            private trigger t = CreateTrigger()
            private integer up
            private integer down
        endglobals

    static if not LIBRARY_HashRecyler then

        struct HashRecyler extends array
    debug    readonly static integer counter    = 0
            readonly static hashtable ht = InitHashtable()

            static method operator[] takes integer k returns integer
                return LoadInteger(ht, -1, k)
            endmethod

            static method operator []= takes integer k, integer tb returns nothing
                call SaveInteger(ht, -1, k, tb)
            endmethod
       
            private static method onInit takes nothing returns nothing
                set thistype[0] = 1
            endmethod
       
            static method alloc takes nothing returns integer
                local integer k =  thistype[0]
                if (thistype[k] == 0) then
                    set thistype[0] = k + 1
                else
                    set thistype[0] = thistype[k]
                endif
    debug         set counter = counter + 1
                return k
            endmethod
       
            static method free takes integer k returns nothing
                set thistype[k] = thistype[0]
                set thistype[0] = k
                call FlushChildHashtable(ht, k)
    debug         set counter = counter - 1
            endmethod
        endstruct
    endif

        // ******************************************************************************************************** \\
        //    Add any textmacro below of the data types you wish to support.
        //
        // Notice: The last boolean is for a bug fix related to nulling hashtable handles, it's not a necessity.
        // But it allows for nulling list storing handles like this:
        //
        // set units[0] = null or players[0] = null
        //
        // If you set it to false the above snippet will not work but set will be slightly more efficent.
        //
        // ******************************************************************************************************** \\
     
        //! runtextmacro ARRAY_LIST_DEFINE("Int",        "integer",    "0",        "Integer",        "SavedInteger")
        //! runtextmacro ARRAY_LIST_DEFINE("Real",        "real",        "0.",        "Real",            "SavedReal")
        //! runtextmacro ARRAY_LIST_DEFINE("Bool",        "boolean",    "false",    "Boolean",        "SavedBoolean")
        //! runtextmacro ARRAY_LIST_DEFINE("Str",        "string",    "null",        "Str",            "SavedString")
        //! runtextmacro ARRAY_LIST_DEFINE("Unit",        "unit",        "null",        "UnitHandle",    "SavedHandle")
        //! runtextmacro ARRAY_LIST_DEFINE("Player",    "player",    "null",        "PlayerHandle",    "SavedHandle")

        // ******************************************************************************************************** \\
        //    Array List
        // ******************************************************************************************************** \\
     
        //! textmacro_once ARRAY_LIST_DEFINE takes NAME, TYPE, NONE, VAR, HANDLE
     
        struct $NAME$ArrayList extends array
     
            readonly static $TYPE$ pivot         // Pivot element during sort or comparison  
            readonly static $TYPE$ other         // Other element during sort

            static method create takes nothing returns thistype
                return HashRecyler.alloc()
            endmethod
       
            method destroy takes nothing returns nothing
                call HashRecyler.free(this)
            endmethod
       
            private method operator length= takes integer index returns nothing
                call SaveInteger(HashRecyler.ht , this, SIZE_INDEX, index)
            endmethod

            method operator length takes nothing returns integer
                return LoadInteger(HashRecyler.ht , this, SIZE_INDEX)
            endmethod
       
            method clear takes nothing returns nothing
                call FlushChildHashtable(HashRecyler.ht , this)
            endmethod
       
            method first takes nothing returns $TYPE$
                return Load$VAR$(HashRecyler.ht , this, 0)
            endmethod
       
            method last takes nothing returns $TYPE$
                return Load$VAR$(HashRecyler.ht , this, .length - 1)
            endmethod
       
            method operator[] takes integer index returns $TYPE$
                return Load$VAR$(HashRecyler.ht , this, index)
            endmethod
       
            method operator[]= takes integer index, $TYPE$ element returns nothing
                call Save$VAR$(HashRecyler.ht , this, index, element)
            endmethod

            method add takes integer index, $TYPE$ element returns nothing
                local integer i = .length
                if index < 0 or index > i then
    debug             call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList(" + I2S(this) + ").add:IndexOutOfBounds (" + I2S(index) + ")")
                    return
                endif
                set .length = i + 1
                loop
                    exitwhen i == index
                    call Save$VAR$(HashRecyler.ht , this, i, Load$VAR$(HashRecyler.ht , this, i - 1))
                    set i = i - 1
                endloop
                call Save$VAR$(HashRecyler.ht , this, index, element)
            endmethod
       
            method remove takes integer index returns $TYPE$
                local integer len = .length - 1
                if index < 0 or index > len then
    debug             call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList(" + I2S(this) + ").remove:IndexOutOfBounds (" + I2S(index) + ")")
                    return $NONE$
                endif
                set .length = len
                set other = Load$VAR$(HashRecyler.ht , this, index)
                loop
                    exitwhen index == len
                    call Save$VAR$(HashRecyler.ht , this, index, Load$VAR$(HashRecyler.ht , this, index + 1))
                    set index = index + 1
                endloop
                call Remove$HANDLE$(HashRecyler.ht , this, len)
                return other
            endmethod
       
            method push takes $TYPE$ value returns thistype
                local integer len = .length
                call Save$VAR$(HashRecyler.ht , this, len, value)
                set .length = len + 1
                return this
            endmethod
       
            method pop takes $TYPE$ value returns $TYPE$
                local integer len = .length - 1
                if len < 0 then
                    return $NONE$
                endif
                set .length = len
                set other = Load$VAR$(HashRecyler.ht , this, len)
                call Remove$HANDLE$(HashRecyler.ht , this, len)
                return other
            endmethod
       
            method indexOf takes $TYPE$ value returns integer
                local integer i = 0
                local integer len = .length
                loop
                    exitwhen i == len
                    if Load$VAR$(HashRecyler.ht , this, i) == value then
                        return i
                    endif
                    set i = i + 1
                endloop
                return -1
            endmethod
       
            method has takes integer index returns boolean
                return index >= 0 and index < .length
            endmethod
       
            method contains takes $TYPE$ value returns boolean
                return .indexOf(value) != -1
            endmethod
       
            method containsAll takes thistype list returns boolean
                local integer i = 0
                local integer n = list.length
                local integer j
                local integer m = .length
                loop
                    exitwhen i == n
                    set other = Load$VAR$(HashRecyler.ht , list, i)
                    set j = 0
                    loop
                        if j == m then
                            return false
                        endif
                        exitwhen other == Load$VAR$(HashRecyler.ht , this, j)
                        set j = j + 1
                    endloop
                    set i = i + 1
                endloop
                return true
            endmethod
       
            method isEmpty takes nothing returns boolean
                return .length == 0
            endmethod

            method swap takes integer indexA, integer indexB returns nothing
    debug       if indexA < 0 or indexA >= .length or indexB < 0 or indexB >= .length then
    debug             call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList(" + I2S(this) + ").swap:IndexOutOfBounds")
    debug           return
    debug         endif
                set other = Load$VAR$(HashRecyler.ht , this, indexA)
                call Save$VAR$(HashRecyler.ht , this, indexA, Load$VAR$(HashRecyler.ht , this, indexB))
                call Save$VAR$(HashRecyler.ht , this, indexB, other)
            endmethod
       
            method clone takes nothing returns thistype
                return create().addRange(0, this, 0, .length)
            endmethod
       
            method equals takes thistype list returns boolean
                local integer i = .length
                if i == list.length then
                    set i = i - 1
                    loop
                        exitwhen i < 0
                        if Load$VAR$(HashRecyler.ht , this, i) != Load$VAR$(HashRecyler.ht , list, i) then
                            return false
                        endif
                        set i = i - 1
                    endloop
                    return true
                endif
                return false
            endmethod
       
            private method setupTrigger takes code c returns nothing
                call TriggerClearConditions(t)
                call TriggerAddCondition(t, Condition(c))
            endmethod
       
            private method quicksort takes integer first, integer last returns nothing
                if first < last then
                    set up = first
                    set down = last
                    set pivot = Load$VAR$(HashRecyler.ht , this, first)
                    loop
                        loop
                            set other = Load$VAR$(HashRecyler.ht , this, up)
                            exitwhen up >= last or TriggerEvaluate(t)
                            set up = up + 1
                        endloop
                        loop
                            set other = Load$VAR$(HashRecyler.ht , this, down)
                            exitwhen  not TriggerEvaluate(t)
                            set down = down - 1
                        endloop
                        exitwhen up >= down
                        set other = Load$VAR$(HashRecyler.ht , this, up)
                        call Save$VAR$(HashRecyler.ht , this, up, Load$VAR$(HashRecyler.ht , this, down))
                        call Save$VAR$(HashRecyler.ht , this, down, other)
                    endloop
                    set other = Load$VAR$(HashRecyler.ht, this, first)
                    call Save$VAR$(HashRecyler.ht , this, first, Load$VAR$(HashRecyler.ht , this, down))
                    call Save$VAR$(HashRecyler.ht , this, down, other)
                    call .quicksort(first, down - 1)
                    call .quicksort(down + 1, last)
                endif
            endmethod

            method sort takes code c returns nothing
    debug        if .length > 1400 then
    debug             call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "WARNING: $NAME$ArrayList.sort: Attempting to sort " + I2S(.length) + " elements.")
    debug        endif
                call .setupTrigger(c)
                call .quicksort(0, .length - 1)
            endmethod
       
            method reverseInner takes integer first, integer last returns nothing
                if first < last then
                    set other = Load$VAR$(HashRecyler.ht , this, first)
                    call Save$VAR$(HashRecyler.ht , this, first, Load$VAR$(HashRecyler.ht , this, last))
                    call Save$VAR$(HashRecyler.ht , this, last, other)
                    call .reverseInner(first + 1, last - 1)
                endif
            endmethod
       
            method reverse takes nothing returns nothing
                //call reverseInner(0, .length - 1)
                set up = 0
                set down = .length - 1
                loop
                    exitwhen down <= up
                    set other = Load$VAR$(HashRecyler.ht , this, up)
                    call Save$VAR$(HashRecyler.ht , this, up, Load$VAR$(HashRecyler.ht , this, down))
                    call Save$VAR$(HashRecyler.ht , this, down, other)
                    set up = up + 1
                    set down = down - 1
                endloop  
            endmethod
       
            method addRange takes integer destPos, thistype src, integer srcPos, integer range returns thistype
                local integer srcSize = src.length
                local integer len = .length
                local integer i = destPos
                if srcPos < 0 or srcPos == srcSize or destPos < 0 or destPos > len then
    debug            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList.copy:IndexOutOfBounds")
                    return this
                endif
                if srcPos + range > srcSize then
                    set range = srcSize - srcPos
                endif
                loop
                    exitwhen i == len
                    call Save$VAR$(HashRecyler.ht , this, i + range, Load$VAR$(HashRecyler.ht , this, i))    // make room
                    set i = i + 1
                endloop
                set i = 0
                loop
                    exitwhen i == range
                    call Save$VAR$(HashRecyler.ht , this, destPos + i, Load$VAR$(HashRecyler.ht , src, srcPos + i))    // fill room with copies
                    set i = i + 1
                endloop
                set .length =  len + range
                return this
            endmethod
       
            method setRange takes integer index, integer range, $TYPE$ value returns nothing
                local integer len = .length
    debug        if index >= len then
    debug             call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList(" + I2S(this) + ").setRange:IndexOutOfBounds (" + I2S(index) + ")")
    debug        endif
                set range = index + range
                loop
                    exitwhen index == range or index >= len
                    call Save$VAR$(HashRecyler.ht , this, index, value)
                    set index = index + 1
                endloop
            endmethod
        endstruct
        //! endtextmacro
    endlibrary
     


    Example Usage


    Sorting Units by unit id
    upload_2019-2-7_15-44-4.png

    Sorting players by score
    upload_2019-2-7_15-50-41.png

    How to:
    Code (vJASS):

    private function SortUnitsByPlayer takes nothing returns boolean
            return GetPlayerId(GetOwningPlayer(UnitArrayList.pivot)) < GetPlayerId(GetOwningPlayer(UnitArrayList.other))
        endfunction
     
        local UnitArrayList list = UnitArrayList.create()
        // ...
        call list.sort(function SortUnitsByPlayer())  // limit is around 1400 - 2000 depending on complexity, the bigger problem is the lagg that might be inccured when sorting larger sets.
     


    Code (vJASS):

    scope Test initializer Init


        globals
            private integer start
            private integer i
            private integer j = 0
            private integer MAX = 500
            private IntArrayList printable
            private IntArrayList largeList
            private UnitArrayList units
        endglobals
     
        private function PrintJob takes nothing returns nothing
            local string s = ""
            local integer size = printable.size()
            set start = i
            set j = 0
            loop
                exitwhen i == size or j == 400
                set s = s + I2S(printable[i]) + " "
                set j = j + 1
                set i = i + 1
            endloop
            call BJDebugMsg("Start: " + I2S(start) + "\n" + s)
            if i < size then
                call ExecuteFunc("Test__PrintJob")
            endif
        endfunction
     
        private function PrintList takes IntArrayList list returns nothing
            set i = 0
            set printable = list
            call ExecuteFunc("Test__PrintJob")
        endfunction
     

        private function ascendingOrder takes nothing returns boolean
            return IntArrayList.pivot < IntArrayList.other
        endfunction
     
        private function descendingOrder takes nothing returns boolean
            return IntArrayList.pivot > IntArrayList.other
        endfunction
     
        private function SortUnitsByPlayer takes nothing returns boolean
            return GetPlayerId(GetOwningPlayer(UnitArrayList.pivot)) < GetPlayerId(GetOwningPlayer(UnitArrayList.other))
        endfunction
     
        globals
            private integer array score
        endglobals
     
        private function SortPlayersByScore takes nothing returns boolean
            return score[GetPlayerId(PlayerArrayList.pivot)] > score[GetPlayerId(PlayerArrayList.other)]
        endfunction
     
        private function PlayerList takes nothing returns nothing
            local PlayerArrayList list = PlayerArrayList.create()
            local integer i = 0
            local integer length
            local player p
            loop
                exitwhen i == 12
                set score[i] = GetRandomInt(0, 100)    // just a random score
                call list.add(i, Player(i))
                set i = i + 1
            endloop
            call list.sort(function SortPlayersByScore)
            set i = 0
            set length = list.size()
            loop
                exitwhen i == length
                set p = list[i]
                call BJDebugMsg(GetPlayerName(p) + " has a score of " + I2S(score[GetPlayerId(p)]))
                set i = i + 1
            endloop
        endfunction
     
        function FormatUnits takes nothing returns nothing
            local integer i = 0
            local integer length = units.size()
            local unit u
            loop
                exitwhen i == length
                set u = units[i]
                call SetUnitX(u, i*10 - 3000)
                call SetUnitY(u, 0)
                set i = i + 1
            endloop
        endfunction
     
        function SortUnits takes nothing returns nothing
            call units.sort(function SortUnitsByPlayer)
            call BJDebugMsg("Units sorted")
            call ExecuteFunc("FormatUnits")
        endfunction
     
        private function UnitList takes nothing returns nothing
            local integer i = 0
            local integer length
            set units = UnitArrayList.create()
            loop
                exitwhen i == 1800
                call units.add(i, CreateUnit(Player(GetRandomInt(0, 24)), 'hpea', 0, 0, 0))
                set i = i + 1
            endloop
            call BJDebugMsg("Created " + I2S(units.size()) + " number of units.")
            call ExecuteFunc("SortUnits")
        endfunction

        private function RemoveMatchingFilter takes nothing returns boolean
            return IntArrayList.pivot >= 30 and IntArrayList.pivot <= 60
        endfunction
     
     
        private function CreateList takes integer size returns IntArrayList
            local integer k = 0
            local IntArrayList list = IntArrayList.create()
            loop
                exitwhen k == size
                call list.add(k, GetRandomInt(0, 200))
                set k = k + 1
            endloop
            return list
        endfunction
     
        function SortLargeList takes nothing returns nothing
            call largeList.sort(function ascendingOrder)
            call PrintList(largeList)
        endfunction
     
        private function Main takes nothing returns nothing
        /*
            local integer i = 0
            local IntArrayList list = IntArrayList.create()
            local IntArrayList list2 = IntArrayList.create()
            local IntArrayList tempList
            loop
                exitwhen i == 100
                //call list.add(i, GetRandomInt(0, 100))
                call list.add(i, i)
                set i = i + 1
            endloop
            //call list.sort(function descendingOrder)
            call PrintList(list)
     
            // This will copy the first 50 elements from list to list2 insertion starting at 0
            // The old list will however retain it's elements.
            call IntArrayList.copy(list, 0, list2, 0, list.size())
            call PrintList(list2)
            call BJDebugMsg("size: " + I2S(list2.size()))
     
            call BJDebugMsg("size: " + I2S(list.size()))
     
            call list2.removeMatching(function RemoveMatchingFilter)
            call PrintList(list2)
     
            set tempList = IntArrayList.tempList
            set i = 0
            loop
                exitwhen i == tempList.size()
                call BJDebugMsg(I2S(i) + ": " + I2S(tempList[i]) + " was removed")
                set i = i + 1
            endloop
     
            //call PlayerList()
     
            */

            call UnitList()
            //set largeList = CreateList(3200)
            //call PrintList(largeList)
            //call ExecuteFunc("SortLargeList")
        endfunction
     
        private function DeleteUnit takes nothing returns nothing
            local integer rdm
            if units.size() > 0 then
                set rdm = GetRandomInt(0, units.size() - 1)
                call RemoveUnit(units.remove(rdm))
                call BJDebugMsg("Remaining: " + I2S(units.size()) + " -- " + I2S(rdm))
            else
                call PauseTimer(GetExpiredTimer())
                call BJDebugMsg("Complete removal")
            endif
        endfunction
     
        private function Init takes nothing returns nothing
            call TimerStart(CreateTimer(), 0.2, false, function Main)
            call TimerStart(CreateTimer(), 0.01, true, function DeleteUnit)
        endfunction
    endscope


    It's also used here: Spell Menu System 1.2c


    I've now included
    has(index)
    as a method which makes it technically possible to replace a table with this provided that the data is ordered.[/HIDDEN]


    Removed Features
    Code (vJASS):

    method drop takes thistype evicted, code c returns nothing
                local integer i = .length - 1
                local integer j
                local integer len
                call .setupTrigger(c)
                loop
                    exitwhen i < 0
                    set pivot = Load$VAR$(ht, this, i)
                    if TriggerEvaluate(t) then
                        if evicted != 0 then
                            call evicted.push(pivot)
                        endif
                        set j = i
                        set len = .length - 1
                        set .length = len
                        loop
                            exitwhen j == len
                            call Save$VAR$(ht, this, j, Load$VAR$(ht, this, j + 1))
                            set j = j + 1
                        endloop
                        call Remove$HANDLE$(ht, this, len)
                    endif
                    set i = i - 1
                endloop
            endmethod
     
            method unique takes thistype evicted returns nothing
                local thistype temp
                local integer i
                set temp = $NAME$ArrayList.create()
                set i = .length - 1
                loop
                    exitwhen i < 0
                    if temp.contains(this[i]) then
                        if evicted != 0 then
                            call evicted.push(this[i])
                        endif
                        call .remove(i)
                    else
                        call temp.push(this[i])
                    endif
                    set i = i - 1
                endloop
                call temp.destroy()
            endmethod

    private static method onAddMatching takes nothing returns nothing
                call tempList.push(pivot)
            endmethod
     
            private static method onSetMatching takes nothing returns nothing
                call Save$VAR$(ht, list, i, other)
            endmethod
     
            private static method onCountMatching takes nothing returns nothing
                set up = up + 1
            endmethod
     
            private static method onRemoveMatching takes nothing returns nothing
                local thistype this = list
                if tempList != 0 then
                    call tempList.push(this.remove(i))
                else
                    call this.remove(i)
                endif
            endmethod
     
            method forEach takes integer start, integer end, integer change, code c, code a returns nothing
    debug         if change == 0 or (start > end and change > 0) or (start < end and change < 0) then
    debug            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList(" + I2S(this) + ").forEach:InfiniteLoopDetected")
    debug            return
    debug        endif
                call .setupTrigger(c, a)
                set i = start
                loop
                    exitwhen i == end
                    set pivot = Load$VAR$(ht, this, i)
                    if TriggerEvaluate(t) then
                        call TriggerExecute(t)
                    endif
                    set i = i + change
                endloop
            endmethod
     
            method removeMatching takes thistype dest, code c returns thistype
                set tempList = dest
                call .forEach(.length - 1, -1, -1, c, function thistype.onRemoveMatching)
                return dest
            endmethod
     
            method addMatching takes thistype srcs, code c returns nothing
                set tempList = this
                call srcs.forEach(0, srcs.length, 1, c, function thistype.onAddMatching)
            endmethod
     
            method setMatching takes $TYPE$ value, code c returns nothing
                set other = value
                call .forEach(0, .length, 1, c, function thistype.onSetMatching)
            endmethod
     
            method countMatching takes code c returns integer
                set up = 0
                call .forEach(0, .length, 1, c, function thistype.onCountMatching)
                return up
            endmethod

     


    Table Version

    This is a table version of the same library, I've decided to include it should any one prefer that. Note that my tests on the latest patch apperently doesn't inline methods properly so Table severly impeds the performance. And I'm not sure exactly why this is happening. Maybe it's a a problem on my end, and if that is the case this version is perfectly fine.
    Code (vJASS):

    library ArrayList requires Table
    /*
        Made by: Pinzu
     
        Version: 1.2
     
        This is a array list container utilizing a hashtable for storage, making it possible to allocate a virtually unlimited amount of lists.
        It contains regular list interface, stack interface, and a few utility methods such as sort, addMatching, removeMatching, copy, swap, etc.

        Credits:
            Bannar for his ListT library which inspired this.
                https://www.hiveworkshop.com/threads/containers-list-t.249011/
       
            Bribe for his Table library.
                https://www.hiveworkshop.com/threads/snippet-new-table.188084/
           
            Overfrost for suggesting improvements.

    */


        // ******************************************************************************************************** \\
        //    API
        // ******************************************************************************************************** \\
     
        //! novjass
     
        struct $NAME$ArrayList extends array
     
            readonly static $TYPE$ pivot     // Pivot element during sort or comparison    
            readonly static $TYPE$ other     // Other element during sort
       
            // Allocates the list.
            static method create takes nothing returns thistype
     
            // Deallocates the list.
            method destroy takes nothing returns nothing
     
            //    Removes all elements from the list.
            method clear takes nothing returns nothing
       
            // Returns the number of elements in the list.
            method operator length takes nothing returns integer
       
            // Returns the element at the specified position.
            method operator[] takes integer index returns $TYPE$
       
            // Replaces the element at the specified position in the list with the specified element.
            method operator[]= takes integer index, $TYPE$ element returns nothing
       
            // Returns the value at the first position of the list
            method first takes nothing returns $TYPE$
       
            // Returns the value at the last position of the list
            method last takes nothing returns $TYPE$
     
            // Inserts the specified element at the specified position in the list.
            method add takes integer index, $TYPE$ element returns nothing

            // Removes the element at the specified position in the the list, returning the value.
            method remove takes integer index returns $TYPE$
       
            // Inserts the element to the last position of the list (stack interface).
            method push takes $TYPE$ value returns thistype
       
            // Removes the element at the last position of the list, returning the value (stack interface).
            method pop takes $TYPE$ value returns $TYPE$
       
            // Returns the index position of the first occurrence of the specified element in the list, or -1 if the list does not contain the element.
            method indexOf takes $TYPE$ value returns integer

            // Returns true if the index is storing any value (table interface).
            method has takes integer index returns boolean
       
            // Returns true if the list contains the specified element.
            method contains takes $TYPE$ value returns boolean
       
            // Returns true if the calling list contains all elements in the specified list.
            method containsAll takes thistype list returns boolean
       
            //Allocates a new list containing shallow copies of all elements held currently.
            method clone takes nothing returns thistype
       
            // Returns true if the list contains no elements.
            method isEmpty takes nothing returns boolean
       
            // Swaps position of 2 elements at the specified positions.
            method swap takes integer indexA, integer indexB returns nothing

            // Returns true if two lists are equal, same length and same structure.
            method equals takes thistype list returns boolean
       
            // Sorts the list after the provided condition (c). Note that the comparison must use the variables 'pivot' and 'other'.
            // Furthermore, only '>' or '<' are valid operators for the sorting to work. Example:
       
                private function SortUnitsByPlayer takes nothing returns boolean
                    return GetPlayerId(GetOwningPlayer(UnitArrayList.pivot)) < GetPlayerId(GetOwningPlayer(UnitArrayList.other))
                endfunction
           
            method sort takes code c returns nothing
       
            // Reverses the order of all elements inside the list.
            method reverse takes nothing returns nothing
       
            // Changes the values of all assigned elements from the start position to the end of the range to the specified new value.
            method setRange takes integer start, integer range, $TYPE$ value returns nothing
       
            // Will perform a shallow copy of all elements from the specified source list to the calling list, with the given range. Starting
            // from the specified source position, inserting to the specified destination position.
            method addRange takes integer destPos, thistype src, integer srcPos, integer range returns thistype
       
        //! endnovjass

        // ******************************************************************************************************** \\
        //    Shared Resources
        // ******************************************************************************************************** \\
     
        globals
            private constant integer SIZE_INDEX = -2 // Should be less than -1 to not risk collision with anything else
            private trigger t = CreateTrigger()
            private integer up
            private integer down
        endglobals

        // ******************************************************************************************************** \\
        //    Add any textmacro below of the data types you wish to support
        //
        // ******************************************************************************************************** \\
     
        //! runtextmacro ARRAY_LIST_DEFINE("Int",        "integer",    "0",        "Integer",        "SavedInteger")
        //! runtextmacro ARRAY_LIST_DEFINE("Real",        "real",        "0.",        "Real",            "SavedReal")
        //! runtextmacro ARRAY_LIST_DEFINE("Bool",        "boolean",    "false",    "Boolean",        "SavedBoolean")
        //! runtextmacro ARRAY_LIST_DEFINE("Str",        "string",    "null",        "Str",            "SavedString")
        //! runtextmacro ARRAY_LIST_DEFINE("Unit",        "unit",        "null",        "UnitHandle",    "SavedHandle")
        //! runtextmacro ARRAY_LIST_DEFINE("Player",    "player",    "null",        "PlayerHandle",    "SavedHandle")

        // ******************************************************************************************************** \\
        //    Array List
        // ******************************************************************************************************** \\
     
        //! textmacro_once ARRAY_LIST_DEFINE takes NAME, TYPE, NONE, VAR, HANDLE
     
        struct $NAME$ArrayList extends array
     
            readonly static $TYPE$ pivot         // Pivot element during sort or comparison    
            readonly static $TYPE$ other         // Other element during sort

            static method create takes nothing returns thistype
                return Table.create()
            endmethod
       
            method destroy takes nothing returns nothing
                call Table(this).destroy()
            endmethod
       
            private method operator length= takes integer index returns nothing
                set Table(this).integer[SIZE_INDEX] = index
            endmethod

            method operator length takes nothing returns integer
                return Table(this).integer[SIZE_INDEX]
            endmethod
       
            method clear takes nothing returns nothing
                call Table(this).flush()
            endmethod
       
            method first takes nothing returns $TYPE$
                return Table(this).$TYPE$[0]
            endmethod
       
            method last takes nothing returns $TYPE$
                return Table(this).$TYPE$[.length - 1]
            endmethod
       
            method operator[] takes integer index returns $TYPE$
                return Table(this).$TYPE$[index]
            endmethod
       
            method operator[]= takes integer index, $TYPE$ element returns nothing
                set Table(this).$TYPE$[index] = element
            endmethod

            method add takes integer index, $TYPE$ element returns nothing
                local integer i = .length
                if index < 0 or index > i then
    debug             call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList(" + I2S(this) + ").add:IndexOutOfBounds (" + I2S(index) + ")")
                    return
                endif
                set .length = i + 1
                loop
                    exitwhen i == index
                    set Table(this).$TYPE$[i] = Table(this).$TYPE$[i - 1]
                    set i = i - 1
                endloop
                set Table(this).$TYPE$[index] = element
            endmethod
       
            method remove takes integer index returns $TYPE$
                local integer len = .length - 1
                if index < 0 or index > len then
    debug             call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList(" + I2S(this) + ").remove:IndexOutOfBounds (" + I2S(index) + ")")
                    return $NONE$
                endif
                set .length = len
                set other = Table(this).$TYPE$[index]
                loop
                    exitwhen index == len
                    set Table(this).$TYPE$[index] = Table(this).$TYPE$[index + 1]
                    set index = index + 1
                endloop
                call Table(this).$TYPE$.remove(len)
                return other
            endmethod
       
            method push takes $TYPE$ value returns thistype
                local integer len = .length
                set Table(this).$TYPE$[len] = value
                set .length = len + 1
                return this
            endmethod
       
            method pop takes $TYPE$ value returns $TYPE$
                local integer len = .length - 1
                if len < 0 then
                    return $NONE$
                endif
                set .length = len
                set other = Table(this).$TYPE$[len]
                call Table(this).$TYPE$.remove(len)
                return other
            endmethod
       
            method indexOf takes $TYPE$ value returns integer
                local integer i = 0
                local integer len = .length
                loop
                    exitwhen i == len
                    if Table(this).$TYPE$[i] == value then
                        return i
                    endif
                    set i = i + 1
                endloop
                return -1
            endmethod
       
            method has takes integer index returns boolean
                return index >= 0 and index < .length
            endmethod
       
            method contains takes $TYPE$ value returns boolean
                return .indexOf(value) != -1
            endmethod
       
            method containsAll takes thistype list returns boolean
                local integer i = 0
                local integer n = list.length
                local integer j
                local integer m = .length
                loop
                    exitwhen i == n
                    set other = Table(this).$TYPE$[i]
                    set j = 0
                    loop
                        if j == m then
                            return false
                        endif
                        exitwhen other == Table(this).$TYPE$[j]
                        set j = j + 1
                    endloop
                    set i = i + 1
                endloop
                return true
            endmethod
       
            method isEmpty takes nothing returns boolean
                return .length == 0
            endmethod

            method swap takes integer indexA, integer indexB returns nothing
    debug       if indexA < 0 or indexA >= .length or indexB < 0 or indexB >= .length then
    debug             call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList(" + I2S(this) + ").swap:IndexOutOfBounds")
    debug           return
    debug         endif
                set other = Table(this).$TYPE$[indexA]
                set Table(this).$TYPE$[indexA] = Table(this).$TYPE$[indexB]
                set Table(this).$TYPE$[indexB] = other
            endmethod
       
            method clone takes nothing returns thistype
                return create().addRange(0, this, 0, .length)
            endmethod
       
            method equals takes thistype list returns boolean
                local integer i = .length
                if i == list.length then
                    set i = i - 1
                    loop
                        exitwhen i < 0
                        if Table(this).$TYPE$[i] != Table(list).$TYPE$[i] then
                            return false
                        endif
                        set i = i - 1
                    endloop
                    return true
                endif
                return false
            endmethod
       
            private method setupTrigger takes code c returns nothing
                call TriggerClearConditions(t)
                call TriggerAddCondition(t, Condition(c))
            endmethod
       
            private method quicksort takes integer first, integer last returns nothing
                if first < last then
                    set up = first
                    set down = last
                    set pivot = this[first]
                    loop
                        loop
                            set other = this[up]
                            exitwhen up >= last or TriggerEvaluate(t)
                            set up = up + 1
                        endloop
                        loop
                            set other = this[down]
                            exitwhen  not TriggerEvaluate(t)
                            set down = down - 1
                        endloop
                        exitwhen up >= down
                        set other = this[up]
                        set this[up] = this[down]
                        set this[down] = other
                    endloop
                    set other = this[first]
                    set this[first] = this[down]
                    set this[down] = other
                    call .quicksort(first, down - 1)
                    call .quicksort(down + 1, last)  
                endif
            endmethod

            method sort takes code c returns nothing
    debug        if .length > 1400 then
    debug             call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "WARNING: $NAME$ArrayList.sort: Attempting to sort " + I2S(.length) + " elements.")
    debug        endif
                call .setupTrigger(c)
                call .quicksort(0, .length - 1)
            endmethod
       
            private method reverseInner takes integer first, integer last returns nothing
                if first < last then
                    set other = this[first]
                    set this[first] = this[last]
                    set this[last] = other
                    call .reverseInner(first + 1, last - 1)
                endif
            endmethod
       
            method reverse takes nothing returns nothing
                //call reverseInner(0, .length)
                set up = 0
                set down = .length - 1
                loop
                    exitwhen down <= up
                    set other = this[up]
                    set this[up] = this[down]
                    set this[down] = other
                    set up = up + 1
                    set down = down - 1
                endloop
            endmethod
       
            method addRange takes integer destPos, thistype src, integer srcPos, integer range returns thistype
                local integer srcSize = src.length
                local integer len = .length
                local integer i = destPos
                if srcPos < 0 or srcPos == srcSize or destPos < 0 or destPos > len then
    debug            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList.copy:IndexOutOfBounds")
                    return this
                endif
                if srcPos + range > srcSize then
                    set range = srcSize - srcPos
                endif
                loop
                    exitwhen i == len
                    set Table(this).$TYPE$[i + range] = Table(this).$TYPE$[i]    // Make room
                    set i = i + 1
                endloop
                set i = 0
                loop
                    exitwhen i == range
                    set Table(this).$TYPE$[destPos + i] = Table(this).$TYPE$[srcPos + i]    // fill room with copies
                    set i = i + 1
                endloop
                set .length =  len + range
                return this
            endmethod
       
            method setRange takes integer index, integer range, $TYPE$ value returns nothing
                local integer len = .length
    debug        if index >= len then
    debug             call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList(" + I2S(this) + ").setRange:IndexOutOfBounds (" + I2S(index) + ")")
    debug        endif
                set range = index + range
                loop
                    exitwhen index == range or index >= len
                    set Table(this).$TYPE$[index] = value
                    set index = index + 1
                endloop
            endmethod
        endstruct
        //! endtextmacro
    endlibrary
     
    Last edited: Feb 14, 2019 at 2:34 AM
  2. Pinzu

    Pinzu

    Joined:
    Nov 30, 2007
    Messages:
    1,148
    Resources:
    3
    Spells:
    2
    Tutorials:
    1
    Resources:
    3
    The sort is somewhat slow and limited as it performs 2 trigger evaluations. I'm not sure if I can optimize it further.

    One idea would be to allow the user to create a rank value for the elements in the table, but I'm not sure that this would work that well, so perhaps the trade off between being able to sort on any condition is worth the loss in performance.


    The only other thing impeding performance is the use of Table instead of having its own hashtable.

    Edit: I have recoded it using hashtable instead of Table, I think it makes sense given that its a data structure and performance should be factored in. I created a HashRecyler library so the hashtable could be reused in other places if the method I've implemented is logically sound.

    In the end i managed to sort 3000 integers and 1800 units by player id before hitting the OP limit, which I'll consider as a success (it was only a fraction of this before I optimized it).
     
    Last edited: Feb 9, 2019
  3. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,538
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    What advantages does this have over Bannar's?
     
  4. Pinzu

    Pinzu

    Joined:
    Nov 30, 2007
    Messages:
    1,148
    Resources:
    3
    Spells:
    2
    Tutorials:
    1
    Resources:
    3
    More API and doesn't use Table method wrappers so should have a slight performance edge. I suggested changes to Bannar but they were not approved so this could be seen as the alternative with more features out of the box. I'm gonna stick with it not using Table for now.

    The philosophy I'm adopting for this is that it should do as much as could possibly be desired out of the box and to that end I'm open for suggestions on what to include. Bannar's version has a different design approach and is more light weight (though has slightly more execution overhead).

    I don't see why these two libraries are in conflict, it's simply a matter or preference.

    Updated the code to 1.1.

    The most noteworthy features of this is that you can provide conditions to sort anything, which should work well for smaller lists, larger lists should perhaps have their own dedicated sorting algorithm to reduce the overhead derived from executing TriggerEvaluate.

    There are also other utility methods in the API which I haven't mentioned here.
     
    Last edited: Feb 9, 2019
  5. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,538
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    If you could show a couple use cases where your library does something better that would help.

    I also feel it could have a better library name than
    PArrayList
    . Maybe
    ListInterface
    or
    ArrayList
    .

    And I noticed you are checking if the trigger member is null before creating it in
    setupTrigger
    . Couldn't you just create the trigger in the .create() method? I don't see anywhere in the code where the trigger would become null after initializing it.
     
  6. Pinzu

    Pinzu

    Joined:
    Nov 30, 2007
    Messages:
    1,148
    Resources:
    3
    Spells:
    2
    Tutorials:
    1
    Resources:
    3
    - ArrayList is fine for me if it's not taken.
    - The reason for the check for null is for the cases when it's never used, I figured the added if is not so bad. Remeber that a user might have multiple list types and some may never use any of these features: sort, addMatching or removeMatching (currently).

    Here are some examples which I haven't checked if it compiles, tested examples can be found in the opening post.

    Code (vJASS):

    function StackUsage takes nothing returns nothing
        local IntArrayList stack = IntArrayList.create()
        local integer returnValue
        call stack.push(5).push(3).push(2)            // faster than add(i, 5)
        set returnValue = stack.pop()                // == 2
        set returnValue = stack.peek()                 // == 3
        set returnValue = stack.pop()                 // == 3
    endfunction

    function SortUnit takes nothing returns boolean        // valid operators are "<" and ">"
        return GetPlayerId(GetOwningPlayer(UnitArrayList.pivot)) < GetPlayerId(GetOwningPlayer(UnitArrayList.other))
    endfunction

    function SortingUnits takes nothing returns nothing
        local UnitArrayList units  = UnitArrayList.create()
        local integer i = 0
        local unit u
        loop
            exitwhen i == 100
            call units.add(i, CreateUnit(Player(GetRandomInt(0, 23)), 'hpea' 0, 0, 0) // Creates a unit and grants it to a random player
            set i = i + 1
        endloop
        call units.sort(function SortUnit)    // Units will now be sorted by player id in ascending order
        set i = 0
        loop
            exitwhen i == units.length
            set u = units[i]
            call SetUnitX(u, 20*i - 1000)
            call SetUnitY(u, 0)
            set i = i + 1
        endloop
    endfunction

    function RemoveCondition takes nothing returns boolean
        return IntArrayList.pivot > 30 and IntArrayList.pivot < 60
    endfunction

    function AddCondition takes nothing returns nothing
        return IntArrayList.pivot > 50
    endfunction

    function RemoveMatching takes nothing returns nothing
        local IntArrayList list = IntArrayList.create()
        local IntArrayList removed                            // Will hold the removed elements
        local integer i = 0
        loop
            exitwhen i == 100
            call list.push(i)
            set i = i + 1
        endloop
        set removed = list.removeMatching(IntArrayList.create(), function RemoveCondition)    // remove all elements between 30 and 60 in this case
        call list.addMatching(removed, function AddCondition) // we append the removed elements that meet the condition to the list.
        call removed.destroy()
    endfunction


    oh and a more basic case:
    list.indexOf(value)
    every list should have this and
    list.contains(value)
    in my oppinion.

    Is completely hashtable based so should be able to exceed array limitations that regular struct allocation uses, the one I reference uses a table but has it as a member variable which removes this advantage.

    I'm sure there are more things to commet on (I looked at this: [vJASS] - [Containers] Vector<T>)

    Note that I'm not comparing this to ListT as that is a LinkedList and doesn't have random access which makes it a different beast entirely though I think there is room for API improvement on that library as well as I have indicated.

    _______________________________________________________

    Version 1.2
    - Added:
    method setMatching takes $TYPE$ value, code c returns nothing
    Changes value of all elements matching the provided condition.
    - Added:
    method countMatching takes code c returns integer
    Returns the number matching occurances within the list, can be used to count number of instances of a given value or more complex conditions.
    - Added:
    method clone takes nothing returns thistype 
    Returns a new list containing all elements of the calling list.
    - Added:
    method reverse takes nothing returns nothing 
    Reverses the order of the list.
    - Added:
    method containsAll takes thistype list returns boolean
    - Changed: copy now also returns the destination list.
    - Changed: Removed some of the static variables from the textmacro so that they are shared across all list types, removing the need for multiple triggers.
    - Changed: Added a forEach method which was used internally to I think reduce boiler plate code, though slowing it down instead.

    I'd appreciate input on the aggregate methods: setMatching, addMatching and removeMatching, Should I make them more flexible by allowing the user to specify which index or leave it as is?
     
    Last edited: Feb 10, 2019 at 3:52 PM
  7. Overfrost

    Overfrost

    Joined:
    Jan 9, 2019
    Messages:
    82
    Resources:
    0
    Resources:
    0
    Just go through the comments patiently mate!
    Code (vJASS):


            //-------------
            // API Design
            //---------

            // - this is fine, but since Table loves to use [] operator, make this use that too
            method indexOf takes $TYPE$ value returns integer
            /* -> */ method operator index[] takes $TYPE$ value returns integer
           
            // - this should not be a method, for a method its name should be getSize
            method size takes nothing returns integer
            /* -> */  method operator size takes nothing returns integer

            // - this needs better explanation
            // - make it takes boolexpr as argument instead of code, so that the user can opt to use 1 stored boolexpr instead of repeated boolexpr conversion
            // - line number 2 is incorrect, the user can pass anything as long as it's a boolean
            // - explains what happens when the filter returns true/false
            //
            // Sorts the list after the provided condition (c). Note that the comparison must use the variables 'pivot' and 'other' in the comparison.
            // Furthermore, only '>' or '<' are valid operators.
            method sort takes code c returns nothing

            // - this has to be non-static
            // - my suggestion makes (this) as the destination
            static method copy takes thistype src, integer srcPos, thistype dest, integer destPos, integer range returns thistype
            /* -> */ method addCopy takes integer index, thistype source, integer sourceStartIndex, integer length returns thistype(this)

            // - see my review on its code for this
            method clone takes nothing returns thistype

            //-------------------
            // Unnecessary APIs
            //---------------
           
            // - manual is enough, these are unneeded
            method removeMatching takes thistype dest, code c returns thistype
            method addMatching takes thistype srcs, code c returns nothing
            method setMatching takes $TYPE$ value, code c returns nothing
            method countMatching takes code c returns integer

            // - will anyone else ever need this?
            method containsAll takes thistype list returns boolean


    //---------
    // Script
    //-----

    // - just don't make the required library optional
    // - there's no harm in making a lib requires another, it's a feature meant to be used
    // - however, if you want to keep this, make the HashRecycler you provide here private
    //   because this in public means that you're "advertising" or "force-including" another lib (to me at least)
    static if not LIBRARY_HashRecyler then
        struct HashRecyler extends array
            readonly static hashtable ht = InitHashtable()
    debug    readonly static integer counter = 0
           
            private static method operator[] takes integer k returns integer
                return LoadInteger(ht, -1, k)
            endmethod

            private static method operator []= takes integer k, integer tb returns nothing
                call SaveInteger(ht, -1, k, tb)
            endmethod

            private static method onInit takes nothing returns nothing
                set thistype[0] = 1
            endmethod

            static method alloc takes nothing returns integer
                local integer k =  thistype[0]
                if (thistype[k] == 0) then
                    set thistype[0] = k + 1
                else
                    set thistype[0] = thistype[k]
                endif
    debug         set counter = counter + 1
                return k
            endmethod

            static method free takes integer k returns nothing
                set thistype[k] = thistype[0]
                set thistype[0] = k
                call FlushChildHashtable(ht, k)
    debug         set counter = counter - 1
            endmethod
        endstruct
    endif

        // - use globals instead, that's it, won't make unnecessary extra variables nor make any difference at all
        private struct v extends array
            static integer i
            static hashtable ht = HashRecyler.ht  // - this one line is a lazy line mate, highly unneeded
            static integer up
            static integer down
            static integer len
            static trigger t = CreateTrigger()
        endstruct

        // - move this waaaay up, close to the documentation, and state this there as well
        //! runtextmacro DEFINE_ARRAY_LIST("Int",        "integer",    "0",        "Integer",        "SavedInteger")
        //! runtextmacro DEFINE_ARRAY_LIST("Real",        "real",        "0.",        "Real",            "SavedReal")
        //! runtextmacro DEFINE_ARRAY_LIST("Bool",        "boolean",    "false",    "Boolean",        "SavedBoolean")
        //! runtextmacro DEFINE_ARRAY_LIST("Str",        "string",    "null",        "Str",            "SavedString")
        //! runtextmacro DEFINE_ARRAY_LIST("Unit",        "unit",        "null",        "UnitHandle",    "SavedHandle")
        //! runtextmacro DEFINE_ARRAY_LIST("Player",    "player",    "null",        "PlayerHandle",    "SavedHandle")

        // - forgot to make this private eh?
        // - or is this meant to be a ghost API?
        function GetCurArrayListIndex takes nothing returns integer
            return v.i
        endfunction

        // - this naming is improper, more proper naming system to use is [SCOPE]_[MEMBER]
        // - name -> ARRAY_LIST_DEFINE
        // - this API is much better in conjunction with Table, and like I said somewhere else,
        //! textmacro_once DEFINE_ARRAY_LIST takes NAME, TYPE, NONE, VAR, REMOVE
       
        struct $NAME$ArrayList extends array

            // - personally, I prefer removing the ifs to make these inline, though it's fine either way
            method operator[] takes integer index returns $TYPE$
                if (index < 0 or index >= .length) then
    debug             call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList.get:IndexOutOfBounds")
                    return $NONE$
                endif
                return Load$VAR$(v.ht, this, index)
            endmethod
            method operator[]= takes integer index, $TYPE$ element returns nothing
                if (index < 0 or index >= .length) then
    debug             call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList.set:IndexOutOfBounds")
                    return
                endif
                call Save$VAR$(v.ht, this, index, element)
            endmethod

            // - marked lines are redundant
            method add takes integer index, $TYPE$ element returns nothing
                local integer i = .length
                if (index < 0 or index > i) then
    debug             call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList.add:IndexOutOfBounds")
                    return
                endif
            //--set v.len = i
                loop
                    exitwhen i == index
                    call Save$VAR$(v.ht, this, i, Load$VAR$(v.ht, this, i - 1))
                    set i = i - 1
                endloop
                call Save$VAR$(v.ht, this, index, element)
            //--set .length = v.len + 1
            endmethod
           
            // - marked lines are redundant
            method push takes $TYPE$ value returns thistype
            //--set v.len = .length
            //--call Save$VAR$(v.ht, this, v.len, value)
            //--set v.len = v.len + 1
            //--set .length = v.len
                return this
            endmethod
           
            method pop takes $TYPE$ value returns $TYPE$
                set v.len = .length
                if .length == 0 then  // - what is this? .length is called again despite the fact that it was called into a variable
                    return $NONE$
                endif
                set v.len = v.len - 1
                set .length = v.len
                set other = Load$VAR$(v.ht, this, v.len)
                call Remove$REMOVE$(v.ht, this, v.len)
                return other
            endmethod
           
            // - merge this into .sort directly
    /*      private method setupTrigger takes code c returns nothing
                call TriggerClearConditions(v.t)  
                call TriggerAddCondition(v.t, Condition(c))
            endmethod */


            // - deemed unnecessary, see review on docs
    /*      method removeMatching takes thistype dest, code c returns thistype
                set v.i = .length - 1
                if v.i >= 0 then
                    call .setupTrigger(c)
                    loop
                        exitwhen v. < 0
                        set pivot = Load$VAR$(v.ht, this, v.i)
                        if TriggerEvaluate(v.t) then
                            if dest != 0 then
                                call dest.push(.remove(v.i))
                            else
                                call .remove(v.i)
                            endif
                        endif
                        set v.i = v.i - 1
                    endloop
                endif
                return dest
            endmethod
            method addMatching takes thistype srcs, code c returns nothing
                set v.i = 0
                set v.len = srcs.length
                if srcs != 0 then
                    call .setupTrigger(c)
                    loop
                        exitwhen v.i == v.len
                        set pivot = Load$VAR$(v.ht, srcs, v.i)
                        if TriggerEvaluate(v.t) then
                            call .push(pivot)
                        endif
                        set v.i = v.i + 1
                    endloop
                endif
            endmethod
            method setMatching takes $TYPE$ value, code c returns nothing
                set v.i = 0
                set v.len = .length
                call .setupTrigger(c)
                loop
                    exitwhen v.i == v.len
                    set pivot = Load$VAR$(v.ht, this, v.i)
                    if TriggerEvaluate(v.t) then
                        call Save$VAR$(v.ht, this, v.i, value)
                    endif
                    set v.i = v.i + 1
                endloop
            endmethod
            method countMatching takes code c returns integer
                local count = 0
                set v.i = 0
                set v.len = .length
                call .setupTrigger(c)
                loop
                    exitwhen v.i == v.len
                    set pivot = Load$VAR$(v.ht, this, v.i)
                    if TriggerEvaluate(v.t) then
                        set count = count + 1
                    endif
                    set v.i = v.i + 1
                endloop
                return count
            endmethod */


            // - use HaveSavedSomething native instead
            // - Table's API uses the term has instead of contains, might be better with has
            method contains takes $TYPE$ value returns boolean
                return .indexOf(value) != -1
            endmethod
           
            // - bye please
    /*      method containsAll takes thistype list returns boolean
                local integer i = 0
                set v.len = list.length
                loop
                    exitwhen i == v.len
                    if .indexOf(list[i]) == -1 then
                        return false
                    endif
                    set i = i + 1
                endloop
                return true
            endmethod */


            // - just change this to operator
            method size takes nothing returns integer
                return .length
            endmethod
           
            static method copy takes thistype src, integer srcPos, thistype dest, integer destPos, integer range returns thistype
                local integer i = 0
                local integer srcSize
                if srcPos < 0 or srcPos == src.length or destPos < 0 or destPos > dest.length then  // - so, srcPos > src.length is fine eh?
    debug            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList.copy:IndexOutOfBounds")
                    return dest
                endif
                set srcSize = src.length  // - move this above on local declaration, also helps inside the if
                loop
                    exitwhen i == range or srcPos >= srcSize  // - it should be safe making it == instead after the if above is fixed
                    call dest.add(destPos, src[srcPos])  // - do this manually, don't be lazy, the manual way removes the need of so many unnecessary operations
                    set destPos = destPos + 1
                    set srcPos = srcPos + 1
                    set i = i + 1
                endloop
                return dest
            endmethod

            // - unneeded because this can be done easily enough with  ArrayList.create().addCopy(0, AL, 0, AL.length)
            // - there are many optimize-able things if this method is done manually
    /*      method clone takes nothing returns thistype
                return thistype.copy(this, 0, thistype.create(), 0, .length)
            endmethod */

           
            // - no comment yet on this one, I'm currently occupied with other things, can't be arsed to bother sorting algorithms
            private method quicksort takes integer first, integer last returns nothing
                if first < last then  
                    set v.up = first
                    set v.down = last
                    set pivot = Load$VAR$(v.ht, this, first)
                    loop
                        loop
                            set other = Load$VAR$(v.ht, this, v.up)
                            exitwhen v.up >= last or TriggerEvaluate(v.t)
                            set v.up = v.up + 1
                        endloop
                        loop
                            set other = Load$VAR$(v.ht, this, v.down)
                            exitwhen  not TriggerEvaluate(v.t)
                            set v.down = v.down - 1
                        endloop
                        exitwhen v.up >= v.down
                        set other = Load$VAR$(v.ht, this, v.up)
                        call Save$VAR$(v.ht, this, v.up, Load$VAR$(v.ht, this, v.down))
                        call Save$VAR$(v.ht, this, v.down, other)
                    endloop
                    set other = Load$VAR$(v.ht, this, first)
                    call Save$VAR$(v.ht, this, first, Load$VAR$(v.ht, this, v.down))
                    call Save$VAR$(v.ht, this, v.down, other)
                    call .quicksort(first, v.down - 1)  
                    call .quicksort(v.down + 1, last)    
                endif
            endmethod

            // - just make quicksort public and rename it to sort please, make sure to do the appropriate changes
    /*      method sort takes code c returns nothing
    debug        if .length > 1400 then
    debug             call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "WARNING: $NAME$ArrayList.sort: Attempting to sort " + I2S(.length) + " elements.")
    debug        endif
                call .setupTrigger(c)
                call .quicksort(0, .length - 1)
            endmethod */


            // - a ghost public method, unseen in docs
            // - it's fine except for its ghost status
            method reverse takes nothing returns nothing
                set v.up = 0
                set v.down = .length - 1
                loop
                    exitwhen v.down <= v.up
                    set other = Load$VAR$(v.ht, this, v.up)
                    call Save$VAR$(v.ht, this, v.up, Load$VAR$(v.ht, this, v.down))
                    call Save$VAR$(v.ht, this, v.down, other)
                    set v.up = v.up + 1
                    set v.down = v.down - 1
                endloop
            endmethod
           
        endstruct
        //! endtextmacro
     
  8. Pinzu

    Pinzu

    Joined:
    Nov 30, 2007
    Messages:
    1,148
    Resources:
    3
    Spells:
    2
    Tutorials:
    1
    Resources:
    3
    Thanks for your feedback. That's quite a extensive list so I won't comment on all but some changes will be made from it.
    • On the sort, I'm not sure how you can make it pass a bool expression, care to show an example of how it would work?
    • With regard to the operators '<' and '>' what I mean is that if you try: <= or >=, == or != the sorting won't work.
    • containsAll is for example used when you wanna check for all items in a recepie system is in existence.
    • On the inline vs safty should be enabled or not
      Code (vJASS):

      [/LIST]
      method opeartor[] takes integer index returns $TYPE$
          static if SAFTY_ENABLED then
              if index < 0 or index >= .length then
                  debug msg
                  return    
              endif
          endif
          return Load$VAR$(ht, this, index)
      endmethod
       
    • You don't like the push method?On Hashtable HaveSaved, I'm not sure if that would work as we don't know which key to look for or is that what HaveSavedValue does? Also the length would have to be temporarily removed as to not conflict with saved integers.
      Code (vJASS):

      native HaveSavedBoolean (hashtable table, integer parentKey, integer childKey) returns boolean
      native HaveSavedHandle (hashtable table, integer parentKey, integer childKey) returns boolean
      native HaveSavedInteger (hashtable table, integer parentKey, integer childKey) returns boolean
      native HaveSavedReal (hashtable table, integer parentKey, integer childKey) returns boolean
      native HaveSavedString (hashtable table, integer parentKey, integer childKey) returns boolean
      function HaveSavedValue (integer key, integer valueType, integer missionKey, hashtable table) returns boolean
       
    • I felt that if the copy method was static it was clear how it would work, where as if it's a method it's not as clear what is going where.

    • I don't think making quicksort public makes any sense unless you want to sort extremly large data sets and want something to sort only part of that data set for you, but I can make it public. But as for removing sort(code c) thats' a no. Furthermore, again, I'm not sure you can pass bool expressions as I don't think you can do the comparisons necessary.
    • I suppose changing contains to has makes sense if you'd want to have compatability with normal table usage. But these methods don't exactly do the same thing. Table.has returns true if there is something stored at a particular index. list.contains return true if the list actually have an element of a given value inside it.
    A boolexpr is a == b right? The thing is that a and b changes depending on where we are on the list.


    I reduced the boiler plate by creating a forEach method instead so that those methods you hated have less boiler plate code generated:
     method forEach takes integer start, integer end, integer change, code c, code a returns nothing
    This part of the library wasn't meant to be fast, simply convenient.

    For isntance countMatching turned into:
    Code (vJASS):

    method countMatching takes code c returns integer
        set up = 0
        call .forEach(0, .length -1, 1, c, function thistype.onCountMatching)
        return up
    endmethod[/ljass]

    I'm not exactl sure if it saved many lines, but perhaps.. ^^
     
    Last edited: Feb 10, 2019 at 10:19 PM
  9. Overfrost

    Overfrost

    Joined:
    Jan 9, 2019
    Messages:
    82
    Resources:
    0
    Resources:
    0
    After accidentally bumping into something, in your case it's actually better to use ForForce. This is the thread.

    Well yes, if it's about comparing the values directly. The thing is, it is not about the operator, it's about whether the filter returns true or false.

    My bad here mate! Don't know why I remembered the function that way.

    Alright, just do this then:
    Code (vJASS):
        // - manual rewrite produces faster result, and is more optimized
        method has takes $TYPE$ value returns boolean
            local integer i = .length
            loop
                exitwhen i == 0
                set i = i - 1
                if Load$VAR$(v.ht, this, i) == value then
                    return true
                endif
            endloop
            return false
        endmethod

        // - please check on this one, might miss something
        method contains takes thistype list returns boolean
            local integer i = list.length
            local integer j
            local $TYPE$ value
            loop
                exitwhen i == 0
                set i = i - 1
                set value = Load$VAR$(v.ht, list, i)
                set j = .length
                loop
                    if j == 0 then
                        return false
                    endif
                    set j = j - 1
                    exitwhen Load$VAR$(v.ht, this, j) == value
                endloop
            endloop
            return true
        endmethod
    Don't use static if + constant for this, literally unneeded configuration for the user. Just enable the safety on debug, and disable it otherwise. A good user knows that it's not supposed to hold keys greater than its size.

    It's fine, and on point.

    I'm sure addCopy explains things better, plus it requires one less argument.

    What I meant was, to merge sort, quicksort, and setup all into one method which is sort alone. Look at recursive calls in this thread. If you really really want to separate them, use textmacros instead.
    ---

    These are new...
    Code (vJASS):
            private method setupTrigger takes code c, code a returns nothing
                call TriggerClearConditions(v.t)  
                call TriggerClearActions(v.t)
                call TriggerAddCondition(v.t, Condition(c))
                if a != null then
                    call TriggerAddAction(v.t, a)
                endif
                set v.list = this
            endmethod

            private method forEach takes integer start, integer change, integer end, code c, code a returns nothing
                call .setupTrigger(c, a)
                set v.i = start
                loop
                    exitwhen v.i == end
                    set pivot = Load$VAR$(v.ht, this, v.i)
                    if TriggerEvaluate(v.t) then
                        call TriggerExecute(v.t)
                    endif
                    set v.i = v.i + change
                endloop
            endmethod
    ... which literally downgraded the script. Nice!
     
  10. Pinzu

    Pinzu

    Joined:
    Nov 30, 2007
    Messages:
    1,148
    Resources:
    3
    Spells:
    2
    Tutorials:
    1
    Resources:
    3
    You can't write an iterative quicksort without a elaborate stack manipulation to replace the recursion. I've tried more primitive solutions before and they perform far worse.

    As for the ForForce I don't know why bring that up, I'm not manipulating a force here? Though I've never been having anything against things like ForGroup or ForForce.

    I don't envision anyone using containsAll in a mission critical way requiring the removal of the function call just to perform the same action. Suppose I could test it. The change in copy makes sense because it was very inefficiently written O(n^2). The changes to contain all does not address the underlying problem of it being O(n^2) and thus will not have any substantial impact.

    As for the forEach wrappers, I see what you're saying. They may be slow but I will have to test them in a real scenario before I lay a verdict on if they have any practical use or are just silly, probably the latter. Though I think addMatching and removeMatching are more valuable than setMatching and countMatching.

    You can't have nested textmacros I'm afraid.

    So you want
    .sort(function SortComparison)
    to be changed to
    list.sort(Filter(function SortComparison))
    ?

    I'll try include more debugs, I'm thinking add and remove should have the if-statement because if that goes wrong the structure goes to shit, and not simply that you will have swapped things to unused memory.
     
    Last edited: Feb 11, 2019 at 2:22 AM
  11. Overfrost

    Overfrost

    Joined:
    Jan 9, 2019
    Messages:
    82
    Resources:
    0
    Resources:
    0
    Forgot that it was inside a textmacro already, just use manual CnP. There must be a way to make the quicksort non-recursive.

    To bloody run a code of course. Here I'll CnP Bribe's post just for you:
    Code (vJASS):
    scope Poop initializer Init
        globals
            private trigger trig = CreateTrigger()
            private force f = CreateForce()
        endglobals
        function TestFunc takes nothing returns nothing
        endfunction
        private function Tester takes nothing returns nothing
            local integer i = 500
            loop
                set i = i - 1
                //call ExecuteFunc("TestFunc")
                //call TriggerExecute(trig)
                //call TriggerEvaluate(trig)
                call ForForce(f, function TestFunc)
                exitwhen i == 0
            endloop
        endfunction
        private function Init takes nothing returns nothing
            local code c = function TestFunc
            //call TriggerAddAction(trig, c)
            //call TriggerAddCondition(trig, Filter(c))
            call ForceAddPlayer(f, Player(0))
            call TimerStart(CreateTimer(), 0.03125, true, function Tester)
        endfunction
    endscope
    But it can't return any value, so a global variable is needed to pass the returned value.

    Good users know what an array is. They can use their own if statement when they know they might pass an invalid key.

    Alright... Did you read this?
    For your convenience, here's what GetHandleIdBJ is:
    Code (vJASS):
    function GetHandleIdBJ takes handle h returns integer
        return GetHandleId(h)
    endfunction
    And you're saying that something like this is just fine:
    Code (vJASS):
    private function A takes integer exp returns integer
        local integer i = 1
        loop
            exitwhen exp <= 0
            set i = i*2
            set int = int - 1
        endloop
        return i
    endfunction
    function B takes integer len returns integer
        local integer i = 0
        loop
            exitwhen len <= 0
            set len = len - 1
            set i = i + A(len)
        endloop
        return i
    endfunction
    // notice that A is private, and if this is the entire lib, it's stupid to have A as a standalone function
    While this one is way better:
    Code (vJASS):
    function B takes integer len returns integer
        local integer i = 0
        local integer j = 1
        loop
            exitwhen len <= 0
            set i = i + j
            set j = j*2
            set len = len - 1
        endloop
        return i
    endfunction
    // while this example has an extreme difference, the fact that it no longer does unnecessary function calls INSIDE A LOOP is a lot already
    ---

    Now I should really stop arsing around with all this.
     
  12. Pinzu

    Pinzu

    Joined:
    Nov 30, 2007
    Messages:
    1,148
    Resources:
    3
    Spells:
    2
    Tutorials:
    1
    Resources:
    3
    Alright I've implemented your suggested changes, in particular I removed the forEach stuff. The only thing I didn't implement was the ForForce as I would have to test the validity of that before changing it). Because even if ForForce is faster than EvalTrigger for callbacks it does not necessarily mean that this is also true:

    Code (vJASS):


    function Compare takes nothing returns nothing
        set result = evaluate something
    endfunction

    loop
        set other = load()
        call ForForce(F. function Compare)
        if result then
             call swap()
        endif
    endloop

    // vs
    function Compare takes nothing returns boolean
        return evaluate something
    endfunction

    loop
        set other = load()
        if EvaluateTrigger(t) then
             call swap()
        endif
    endloop

     


    I also read in one of the treads you linked that EvaluateTrigger(null) is a rather small cost? Which makes me consider if the new methods
    addRange and setRange still could implement a condition argument as optional in case one wants to apply a filter?
     
    Last edited: Feb 11, 2019 at 3:13 PM
  13. Overfrost

    Overfrost

    Joined:
    Jan 9, 2019
    Messages:
    82
    Resources:
    0
    Resources:
    0
    It's not because of the speed. It's because of the place, the situation.
    method sort takes code c returns nothing
    . There's nothing being saved, so directly passing c works.

    Man, I should stop doing this...
     
  14. Pinzu

    Pinzu

    Joined:
    Nov 30, 2007
    Messages:
    1,148
    Resources:
    3
    Spells:
    2
    Tutorials:
    1
    Resources:
    3
    You are free to try writing a better sorting algorithm and post results ^^

    Edit: Added all the removed code to the opening post in case anyone wants to argue for their place in the library.

    Code (vJASS):

            method drop takes thistype evicted, code c returns nothing                 // drop all elements matching condition
            method unique takes thistype evicted returns nothing                     // drops all duplications
            method forEach takes integer start, integer end, integer change, code c, code a returns nothing
            method removeMatching takes thistype dest, code c returns thistype
            method addMatching takes thistype srcs, code c returns nothing
            method setMatching takes $TYPE$ value, code c returns nothing
            method countMatching takes code c returns integer
     
    Last edited: Feb 11, 2019 at 4:33 PM
  15. Pinzu

    Pinzu

    Joined:
    Nov 30, 2007
    Messages:
    1,148
    Resources:
    3
    Spells:
    2
    Tutorials:
    1
    Resources:
    3
    Since Overfrost didn't believe me when I said that Table doesn't inline I now have proof that method operators don't inline, or am I reading this result wrong? (I didn't actually look at Table but the principle is the same).

    Code (vJASS):

            method operator[] takes integer index returns $TYPE$
                return Load$VAR$(HashRecyler.ht , this, index)
            endmethod
     
            method operator[]= takes integer index, $TYPE$ element returns nothing
                call Save$VAR$(HashRecyler.ht , this, index, element)
            endmethod
    private method reverseInner takes integer first, integer last returns nothing
                if first < last then
                    set other = this[first]
                    set this[first] = this[last]
                    set this[last] = other
                    call .reverseInner(first + 1, last - 1)
                endif
            endmethod
     
            method reverse takes nothing returns nothing
                //call reverseInner(0, .length)
                set up = 0
                set down = .length - 1
                loop
                    exitwhen down <= up
                    set other = this[up]
                    set this[up] = this[down]
                    set this[down] = other
                    set up = up + 1
                    set down = down - 1
                endloop
            endmethod

     


    Translates into:

    Code (vJASS):


     // Setter
     function s__IntArrayList__getindex takes integer this,integer index returns integer
                return LoadInteger(s__HashRecyler_ht, this, index)
      endfunction
       
        // Getter
        function s__IntArrayList__setindex takes integer this,integer index,integer element returns nothing
            call SaveInteger(s__HashRecyler_ht, this, index, element)
        endfunction


     function s__IntArrayList_reverseInner takes integer this,integer first,integer last returns nothing
                if first < last then
                    set s__IntArrayList_other=s__IntArrayList__getindex(this,first)
                    call s__IntArrayList__setindex(this,first, s__IntArrayList__getindex(this,last))
                    call s__IntArrayList__setindex(this,last, s__IntArrayList_other)
                    call s__IntArrayList_reverseInner(this,first + 1 , last - 1)
                endif
      endfunction
     
      function s__IntArrayList_reverse takes integer this returns nothing
                //call reverseInner(0, .length)
                set ArrayList__up=0
                set ArrayList__down=s__IntArrayList__get_length(this) - 1
                loop
                    exitwhen ArrayList__down <= ArrayList__up
                    set s__IntArrayList_other=s__IntArrayList__getindex(this,ArrayList__up)
                    call s__IntArrayList__setindex(this,ArrayList__up, s__IntArrayList__getindex(this,ArrayList__down))
                    call s__IntArrayList__setindex(this,ArrayList__down, s__IntArrayList_other)
                    set ArrayList__up=ArrayList__up + 1
                    set ArrayList__down=ArrayList__down - 1
                endloop
      endfunction


     
    This doesn't look like inlining to me.

    Which also explains why using method operators over directly calling the hashtable reaches the OP limit much sooner.

    @Overfrost, I will now continue saying what you told me to stop saying directly into your face. :D
     
    Last edited: Feb 12, 2019 at 7:03 PM
  16. Overfrost

    Overfrost

    Joined:
    Jan 9, 2019
    Messages:
    82
    Resources:
    0
    Resources:
    0
    It's your compiler mate, probably your settings. Here's what happens for me.

    Code (vJASS):
    scope Test initializer Init

    private function Init takes nothing returns nothing
        local IntArrayList list = IntArrayList.create()
        call list.push(0xF)
        call list.push(0xFF)
        set list[0] = list.last()
        set list[list.length - 1] = list.first()
    endfunction

    endscope
    Compiled to:
    Code (vJASS):
    //===========================================================================
    // scope Test begins

    function Test___Init takes nothing returns nothing
        local integer list= (s__HashRecyler_alloc()) // INLINED!!
        call s__IntArrayList_push(list,0xF)
        call s__IntArrayList_push(list,0xFF)
        call SaveInteger(s__HashRecyler_ht, (list), (0), ( s__IntArrayList_last(list))) // INLINED!!
        call SaveInteger(s__HashRecyler_ht, (list), ((LoadInteger(s__HashRecyler_ht, (list), ArrayList___SIZE_INDEX)) - 1), ( (LoadInteger(s__HashRecyler_ht, (list), 0)))) // INLINED!!
    endfunction

    // scope Test ends

    Just to clarify, I used your lib without any changes.
     
  17. Pinzu

    Pinzu

    Joined:
    Nov 30, 2007
    Messages:
    1,148
    Resources:
    3
    Spells:
    2
    Tutorials:
    1
    Resources:
    3
    Could you try this from a scope.
    Code (vJASS):

    library THIS_IS_A_BIG_LIB_NAME uses Table

        struct S
     
            static method create takes nothing returns thistype
                return  Table.create()
            endmethod
     
            method destroy takes nothing returns nothing
                call Table(this).destroy()
            endmethod
     
            method operator[] takes integer index return integer
                return Table(this).integer[index]
            endmethod
     
            method operator[]= takes integer index, integer value return integer
                return Table(this).integer[index] = value
            endmethod

        endstruct
    endlibrary

      local S s = S.create()
      set s[0] = 1
      call s.destroy()
     


    So we can kill this debate once and for all.

    Also, I'm using the latest patch of wc3... Does this mean the new patches have killed inlining? :>

    Can someone confirm that this is the case?

    Also, should I stick with hashtable or use Table regardless of this possible problem?
     
    Last edited: Feb 12, 2019 at 10:07 PM
  18. Overfrost

    Overfrost

    Joined:
    Jan 9, 2019
    Messages:
    82
    Resources:
    0
    Resources:
    0
    Code (vJASS):
    library LIBRARY uses Table

        struct S extends array
     
            static method create takes nothing returns thistype
                return  Table.create()
            endmethod
         
            method destroy takes nothing returns nothing
                call Table(this).destroy()
            endmethod
         
            method operator[] takes integer index returns integer
                return Table(this).integer[index]
            endmethod
         
            method operator[]= takes integer index, integer value returns nothing
                set Table(this).integer[index] = value
            endmethod

        endstruct
    endlibrary
    ->
    Code (vJASS):
    //library LIBRARY:

     
            function s__S_create takes nothing returns integer
                return s__Table_create()
            endfunction
         
            function s__S_destroy takes integer this returns nothing
                call s__Table_destroy((this))
            endfunction
         
            function s__S__getindex takes integer this,integer index returns integer
                return (LoadInteger(Table__ht, ((((this)))), (index))) // INLINED!!
            endfunction
         
            function s__S__setindex takes integer this,integer index,integer value returns nothing
                call SaveInteger(Table__ht, ((((this)))), (index), ( value)) // INLINED!!
            endfunction


    //library LIBRARY ends