• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

HandleIndexing + Recycling.

Level 11
Joined
Nov 4, 2007
Messages
337
What is this?
This is a System that created unique integers between 1 and 8190 for
handles. These values can be used as Index for globals.
Thats, why this is called 'HandleIndexing'.

This has the same functionality as PUI, but there are some important differences:
[*] - HandleIndexing works for each Handle type.
[*] - HandleIndexing does not use UnitUserData. That makes the whole thing slower, but it is not that slow that it causes laggs. Actually, this system is simple.
[*] - This is slower than TimerUtils, PUI, DUI, UIU, HSAS ==> whatever.
But the good things about this is, that you do not have to run textmacros.
HSAS for example created for each Usage ID/8190 global variables. I think, that is ... Not good. You HAVE to create a tetxmacro for each HSAS usage (static!), because otherwise you overwrite the stored value.


This is supposed to be combinated with the Recycling textmacro.
If they are used both, they will cover all your needs for handles.



And here is the code:
JASS:
//********************************************************************
// textmacro Recycling takes NAME, TYPE, IDENTIFIER, AMOUNT, ERASELIKE
// NAME = Enter the name you want to Recycle with. Example: Enter Bottle to create NewBottle() and ReleaseBottle().
// TYPE = Enter the handle type you want to recycle. timer will create timers to recycle.
// IDENTIFIER = How is that handle created? Preloaded? If TYPE is timer, this has to be NewTimer().
// AMOUNT    = How big shall the stack be? The bigger the safer. But this preloads AMOUNT types of the Handle
//             You want at the map Init, so it lacks all the time some memory.
// ERASELIKE = How is the handle simulated to be dead? For Timers Write PauseTimer.
// Dont add the parameters here. They are added automatically. If you don't want simulate death, or it is impossible,
// set this to Foo.

// ======= Examples =======
// Are found below

// ======= Functions =======
// New$NAME$()                   : Recycles an old timer, to keep the HandleID size small.
// Release$NAME$($TYPE$)         : Timers created with NewTimer() must be deleten like this.

library Plugin
    function Foo takes handle h returns nothing
        set h = null
    endfunction
    
    function P_NewUnitA takes nothing returns unit
        local unit u = CreateUnit(Player(15),'e003',0.,0.,0.)
        call ShowUnit(u, false)
        return u
    endfunction
    
    function P_ReleaseUnit takes unit u returns nothing
        call SetUnitAnimation(u,"death")
        call ShowUnit(u, false)
        //call DestroyEffect(AddSpecialEffect("Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl",GetUnitX(u),GetUnitY(u)))
    endfunction
endlibrary

//! textmacro Recycling takes NAME, TYPE, IDENTIFIER, ERASELIKE

library $NAME$Recycling initializer $NAME$Preload requires Plugin

    globals
        integer $NAME$Wanted = 5
        $TYPE$ array $NAME$Handle
    endglobals
    
function New$NAME$ takes nothing returns $TYPE$
    
     if ( $NAME$Wanted<1 ) then
[I]        set $NAME$Wanted = $NAME$Wanted + 1
        set $NAME$Handle[$NAME$Wanted] = $IDENTIFIER$[/I] ==> [U]CUT THIS OUT![/U]
        return $IDENTIFIER$
    endif
    
        set $NAME$Wanted = $NAME$Wanted - 1
        return $NAME$Handle[$NAME$Wanted]
endfunction

function Release$NAME$ takes $TYPE$ which returns nothing
    set $NAME$Handle[$NAME$Wanted] = which
    set $NAME$Wanted = $NAME$Wanted + 1
    call $ERASELIKE$(which)
endfunction

private function $NAME$Preload takes nothing returns nothing
    local integer i = 0
    
    loop
        set i = i + 1
        exitwhen i > $NAME$Wanted
        set $NAME$Handle[i] = $IDENTIFIER$
    endloop
    
endfunction

endlibrary

//! endtextmacro
//! runtextmacro Recycling( "Group", "group", "CreateGroup()" , "GroupClear")
// runtextmacro Recycling( "Rect", "rect", "Rect(0.,0.,0.,0.)" , "Foo")
// runtextmacro Recycling( "Location", "location", "Location(0.,0.)", "Foo")
// runtextmacro Recycling( "Force", "force", "CreateForce()", "ForceClear")
// runtextmacro Recycling( "Timer", "timer", "CreateTimer()", "PauseTimer")
// runtextmacro Recycling( "Dummy", "unit", "P_NewUnitA()", "P_ReleaseUnit)

You might wonder, what Plugin is used for. The Identifier 'Eraselike' has just one parameter: The handle.
So if you want for example that the system nulls Locations automatically, you can write this function into 'Plugin' and give the function as parameter.

JASS:
function ClearLocation takes location l returns nothing
        call SetLocationX(l,0.)
        call SetLocationY(l,0.)
endfunction

Warning: This requires careful handling, because it has not really protections or Debug messages.

Or as peq code:
peq - jass-Code #1488 //********************************************************************

And the two flavours of HandleIndexing.

JASS:
// ***************************************
//! S T A R T   O F   H A N D L E   I N D E X I N G
// ***************************************
//*********************************************************************
//* HandleIndexing
//* ----------
//*
//*  To implement it , create a custom text trigger called HandleIndexing
//* and paste the contents of this script there.
//*
//*  To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass)   More scripts: htt://www.wc3campaigns.net
//*
//* For your needs:
//*  * Get unique integers between 1 and 8190 for handles.
//*
//* set index = Index(handle)    : Gets a unique number that fits into arrays for a handle.
//* FlushIndex(handle)           : Tells the recycler that this Index can be recycles. Call this, when youre don with the Index.
//* FlushIndexSafe(handle)       : Flushs an Index using the system itself;
//* PassIndex(handle,handle)     : Passes ALL the data attached with this system from a handle to another handle.

//* Features    
//*             - Doesn't use UnitUserData
//*             - You Don't have to run textmacros
//*             - Attaching 15 Data to a unit is almost as fast as attaching 1 Date to a unit.
//*             - This is safe! If you Release an Index from a unit, that isn't create, the index will instantly be moved to the RecycleBin
//*             - This is fast! HSAS for example or PUI is faster, but; HSAS needs to run a textmacro for each usage. And it uses extended arrays. That are about 30 globals per macro. PUI uses UnitUserData and I don't like PUIs way to recycle Indexes.
//*             - If you combinate this with Recycling textmacros, it is perfect to recycle indexes and add Data to them.
//*
//********************************************************************
//! I M P O R T A N T
//* If it is possible, that your handle is used in more than one system (like units), Flush the Handle
//* Only, when the Handle is destroyed!
//================================================================

library HandleIndexing
globals
    private constant integer MAX_HANDLE_ID = 100000
    private constant integer tosub = 0x100000
    private integer array StoredIDs[MAX_HANDLE_ID]
    private integer IDs = 0
    private integer gaps = 0
    private integer array Gaps
endglobals

private function h2i takes handle h returns integer
    return h
    return 0
endfunction

function ReleaseIndex takes integer index returns nothing
    //! Tell the recycler which gap can be recycled. This will not remove the fitting Index from a unit. ==> lacks memory
    set gaps = gaps + 1
    set Gaps[gaps] = index
endfunction

function FlushIndex takes handle h returns integer
    local integer num = h2i(h)-tosub
    local integer re = 0
    
        if ( StoredIDs[num] != 0 ) then
            call ReleaseIndex(StoredIDs[num])
            set re = StoredIDs[num]
            set StoredIDs[num] = 0
        endif
    return re
endfunction

function CreateIndex takes integer num returns integer
    //! Recycle Indexes
    if ( gaps > 0 ) then
        set StoredIDs[num] = Gaps[gaps]
        set gaps = gaps - 1
        return StoredIDs[num]
    else
    //! Create new Index
        set IDs = IDs + 1
        set StoredIDs[num] = IDs
        return IDs
    endif
endfunction

function Index takes handle h returns integer
    local integer hashnum = h2i(h)-tosub
    if ( StoredIDs[hashnum] == 0 ) then
        return CreateIndex(hashnum)
    else
        return StoredIDs[hashnum]
    endif
endfunction

function PassIndex takes handle a, handle b returns nothing
    local integer index = Index(a)
    local integer num = h2i(b)-tosub
    set StoredIDs[num] = index
    call FlushIndex(a)
endfunction

endlibrary


And the Steel Flavour:
I created this, to add more comfort to HandleIndexing.
Why is this more comfort?
Ok. Let's imagine following situation:

I have 2 systems, both use HandleIndexing. I want to create an Index for a unit in system 2,
but it was previously created in system 1. (Were talking about the same handle).
So if you release the Index from system 2 now, the Index from system 1 is recycled, too.
===> This is only possible, if the handle is used in more than one system, if it's reachable from each trigger. So if you use for example a Recycling System, this is not problem.
But it's always a problem, if you have for example units.
If you use HandleIndexing correctly, you will not experience bugs.
But remember: If you have units as Handle, there are 2 possibilites to prevent that bug:

A: Use HandleIndexing Steel
B: Flush the Index of a unit, when it decays.

I prefer solution B.

Because then you have other comfort functions from A.
However, heres the steel code:
JASS:
//| --------------------------------------------- |
//|                 | ########################### |
//| HandleIndexing  | #### [ Steel Flavour ] #### |
//|                 | ########################### |
//| --------------------------------------------- |
//
//            Actually, normal HandleIndexing is the better choice.
//            But this is suited for noobs and safer.
//            Differences:
//  ________________
// | Differences    |
// |________________|

// - You dont have to be careful when you Flush an Index with this Flavour.
// - Instead of calling Index(handle) to create AND get an Index, you have to
//   call GetIndex(handle) to return the Index of the unit and
//   call NewIndex(handle) to create an Index for the unit.
// - This is slower than the Normal Handle Indexing.
// - If you use HandleIndexing Steel Flavour, it's veeeeery important that you call ReleaseIndex()!!!!
// - If you created an Index, you have to release one.

//  ________________________
// | How does this work?    |
// |________________________|

// - If you call NewIndex(handle), an integer for the Index is increased by 1.
//   If you call ReleaseIndex(handle) then, the integer is decreased by 1.
//   If the Integer is 0, the Index is released.


library SystemAffector
    globals
        integer array Affeted
    endglobals
    
    function Affect takes integer index returns nothing
        set Affeted[index] = Affeted[index] + 1
    endfunction
    
    function DeAffect takes integer index returns boolean
        set Affeted[index] = Affeted[index] - 1
        
        if (Affeted[index] <= 0 ) then
            return true
        endif
        return false
        
    endfunction

endlibrary


library HandleIndexingSteel requires SystemAffector
globals
    private constant integer MAX_HANDLE_ID = 150000
    private constant integer tosub = 0x100000
    private integer array StoredIDs[MAX_HANDLE_ID]
    private integer IDs = 0
    private integer gaps = 0
    private integer array Gaps
endglobals

private function h2i takes handle h returns integer
    return h
    return 0
endfunction

function ReleaseIndex takes integer index returns nothing
    set gaps = gaps + 1
    set Gaps[gaps] = index
endfunction

function CreateIndex takes integer num returns integer
    //! Recycle Indexes
    if ( gaps > 0 ) then
        set StoredIDs[num] = Gaps[gaps]
        set gaps = gaps - 1
        call Affect(StoredIDs[num])
        return StoredIDs[num]
    else
    //! Create new Index
        set IDs = IDs + 1
        set StoredIDs[num] = IDs
            
                if IDs > 8190 then
                debug call BJDebugMsg("Handle Indexing; Created too many Indexes! Don't forget to call ReleaseIndex(handle). Trying to fix Issue. May cause Errors or bugs!")
                return GetRandomInt(1,8190)
                endif
        call Affect(IDs)
        return IDs
    endif
endfunction

function GetIndex takes handle h returns integer 
    return StoredIDs[h2i(h)-tosub]
endfunction

function NewIndex takes handle h returns integer
    local integer hashnum = h2i(h)-tosub
    if ( StoredIDs[hashnum] == 0 ) then
        return CreateIndex(hashnum)
    else
        call Affect(StoredIDs[hashnum])
        return StoredIDs[hashnum]
    endif
endfunction

function FlushIndex takes handle h returns integer
    local integer num = h2i(h)-tosub
    local integer re = 0
    
    if ( DeAffect(StoredIDs[num]) ) then

            call ReleaseIndex(StoredIDs[num])
            set re = StoredIDs[num]
            set StoredIDs[num] = 0
            
    endif
    
    return re
endfunction

endlibrary







Ok. However.
I said, that HandleIndexing is slower. Well. I did some tests with 500000 executions:
TEST with StopWatchMark
=== TESTED WITH WAR3ERR DISABLED===


With 'Safety':
HandleIndexing: 90% [Called 500k times Index and FlushIndex]
PUI: 100% [Called 500k times PeriodicRecycler and GetUnitIndex]

Without 'Safety':
HandleIndexing: 100% [Called 500k times Index and FlushIndex]
PUI: 94% [Called 500k times PeriodicRecycler and GetUnitIndex]
==> This is, what the systems actually do. A bit code was removed (when Indexes got higher than 8190).
So actually, this can be faster than PUI.
PUI is faster, when you call FlushIndex() more than three times a second. However, if you use this for units,
This can be faster, too. If you Flush the Index when a unit decays:

EDIT: New results. Updated. HandleIndexing is as fast as PUI. I think they're both almost identically fast.

http://peeeq.de/gui.php?id=773

Thats dynamic. Thats faster than PUI. Ok.
This system is faster than PUI in most usages, but you can't do anything wrong with PUI,
because it recycles automatically. You have to be more careful with HandleIndexing.

===> I was suprised, that the debug messages slower my system by ~35%!
However, if you use the map finally, you should remove the debug messages.

Note, that PeriodicRecycler isn't called as often as GetUnitIndex(), but if you play for very long, the Test might fit. Maybe PUI uses even more performance.

I didn't test UnitIndexingUtils, because I think, that it is the same as PUI, but PUI is safer, because it recycles Indexes automatically (Do you remember the possible bug I explained?)




And the testcode:

JASS:
library Bench requires PUI, HandleIndexing
globals
    unit Temp = null
endglobals
function IND takes nothing returns nothing
    local integer i = 0
    call RemoveUnit(Temp)
    set Temp = CreateUnit(Player(0),'hpea',0.,0.,0.)
        loop
            set i = i + 1
            exitwhen i > 200
            //CODE FOR THE SYSTEM. IF A SYSTEM IS TESTED ==> RESTART MAP.
        endloop
endfunction

function Benchmark takes nothing returns nothing
local integer sw = StopWatchCreate()
local real settime1 = StopWatchMark(sw)
local real settime2 = StopWatchMark(sw)
local real t1
local real t0 = StopWatchMark(sw)
local integer i = 0
        loop
            set i = i + 1
            exitwhen i > 2500
            call TimerStart(CreateTimer(),0.,false,function IND)
        endloop
// Insert code to be benchmarked here
set t1 = StopWatchMark(sw)
call BJDebugMsg("HandleIndexing: "+R2S(t1-t0-(settime2-settime1)))

endfunction
endlibrary





And a manual, how to improve coding with HandleIndexing:

JASS:
How do I use this perfectly?

    If You want attach Data to many handles at the same time,
    dont create a new Index for each handle. Create one index
    for a handle and use this one for the others, too.
    
    Use this in combination with Recycling. Then you can attach
    the data to a recycled handle.
    
    If you have private globals, you should better use CCComfort.
    CCComfort converts a string into a unique integer.
    You can use that int as index.
    
    Note, that FlusIndex(handle) returns the index of the handle.
    So, if you want perfect performance when you Flush Data, you
    should do this:
    
        local integer index = FlushIndex(handle)
        set YourData[index] = 0
        set YourString[index] = ""
        set YourUnit[index] = null
        
        ===> This is for a better performance, because if you dont
             clear globals you use with HandleIndexing, they suck
             memory all the time.
             
             
    I dont know why, but this: 
        
        debug if ( hashnum > MAX_HANDLE_ID_COUNT ) then
        debug call BJDebugMsg("Handle Indexes; Index of handle too big. Increae 'MAX_HANDLE_ID_COUNT'!")
        debug endif
    
    costs a lot of performance. If you know, that HandleIndexing works, remove this.
 
Last edited:
Level 14
Joined
Nov 18, 2007
Messages
816
So, this system is actually slower than PUI? If yes, then why do you submit it anyway? Especially since theres CSData already.
Also: return GetRandomInt(1,8190) is NOTHING you should EVER do! This is fucking insane.
And:
JASS:
if IDs > 8190 then
                debug call BJDebugMsg("Handle Indexing; Created too many Indexes! Don't forget to call ReleaseIndex(handle). Trying to fix Issue. May cause Errors or bugs!"
                return GetRandomInt(1,8190)
                endif
the debug line errors.

the Preload function in your textmacro thingy should be correctly scoped (private).
 
Level 11
Joined
Nov 4, 2007
Messages
337
First of all,
Thank you for posting.
After the Debug line, I forgot the brackets.
Ill change it, but I got to see the next Uri Geller now :)D What a crap!)
Ill also change the return GetRandomInt(1,8190).

This system is not slower than PUI, it is as fast as PUI.
When you remove the if Indexes > 8190 'Safety lines', this is slightly faster than PUI. But thats almost no difference. If you keep it, PUI is slightly faster.
(Test Results: HandleIndexing: 0.011 PUI: 0.012)
The reason why I made it, is that it works for each Handle Type and returns Indexes, you don't have to run textmacros (like with HSAS).

The Index Remover for units of this is dynamic, so it can be faster than PUI, but not always.

Ill scope the Preloader. Thanks for the tip.
 
Level 11
Joined
Nov 4, 2007
Messages
337
Just think:
I create Indexes for handles!
You can use these Indexes without running textmacros, because the system is supposed to have that feature!
The Indexes are in a range between 1 and 8190.. So I attach the Indexes to the Handle, instead of direct Data.
==> Doesn't make it much slower, but you don't have to run textmacros and create a lot of globals.
 
Level 11
Joined
Nov 4, 2007
Messages
337
That is not a unique ID for a unit. The ID has to be between 1 and 8190.
Just read the code.
You don't seem to understand.
If you use this way of getting unique Indexes, the filled arrays need a size of whatever, maxbe 300000.
And that creates a lot of other globals, because that's a feature of the NewGen.
And with higher IDs, it gets a looot slower.
If the H2I value - 0x100000 of the unit is for example 185555, you have to run thorugh
22 Ifs.

The Method I use, converts a high Index, into an index bewtween 1 and 8190 ==> No use for extended Arrays with size e.g 100000.

Extended arrays are _Non-real-arrays_!
The size of a real array is 8190!







Edit: OK. You don't seem to understand!
Let me expalin again.

The Handle IDs of handles is 0x0000000 + counter.
Every time you create a handle, the counter is increased by 1.
So if the counter is higher than 8190, it fits no longer into an array, because
the biggest Index for an array is 8190.
So if you use the HANDLEID-0x000000 method, you need bigger arrays than 8190.
And there is the solution: The JassHelper has a feature called extended arrays.
Extended arrays create arrays that have more space than 8190 IDs.
But they aren't real arrays.
If I write

globals
integer array Data [300000] ==> For an array with a space of 300000
endglobals

the JassHelper makes the following code from it:
JASS:
//JASSHelper struct globals:
integer array s__Data
integer array s__2Data
integer array s__3Data
integer array s__4Data
integer array s__5Data
integer array s__6Data
integer array s__7Data
integer array s__8Data
integer array s__9Data
integer array s__10Data
integer array s__11Data
integer array s__12Data
integer array s__13Data
integer array s__14Data
integer array s__15Data
integer array s__16Data
integer array s__17Data
integer array s__18Data
integer array s__19Data
integer array s__20Data
integer array s__21Data
integer array s__22Data
integer array s__23Data
integer array s__24Data
integer array s__25Data
integer array s__26Data
integer array s__27Data
integer array s__28Data
integer array s__29Data
integer array s__30Data
integer array s__31Data
integer array s__32Data
integer array s__33Data
integer array s__34Data
integer array s__35Data
integer array s__36Data
integer array s__37Data

endglobals

function sg__Data_get takes integer i returns integer
    if(i<8191) then
        return s__Data[i]
    elseif(i<16382) then
        return s__2Data[i-8191]
    elseif(i<24573) then
        return s__3Data[i-16382]
    elseif(i<163820) then
        if(i<32764) then
            return s__4Data[i-24573]
        elseif(i<40955) then
            return s__5Data[i-32764]
        elseif(i<49146) then
            return s__6Data[i-40955]
        elseif(i<106483) then
            if(i<57337) then
                return s__7Data[i-49146]
            elseif(i<65528) then
                return s__8Data[i-57337]
            elseif(i<73719) then
                return s__9Data[i-65528]
            elseif(i<90101) then
                if(i<81910) then
                    return s__10Data[i-73719]
                else
                    return s__11Data[i-81910]
                endif
            else
                if(i<98292) then
                    return s__12Data[i-90101]
                else
                    return s__13Data[i-98292]
                endif
            endif
        else
            if(i<114674) then
                return s__14Data[i-106483]
            elseif(i<122865) then
                return s__15Data[i-114674]
            elseif(i<131056) then
                return s__16Data[i-122865]
            elseif(i<147438) then
                if(i<139247) then
                    return s__17Data[i-131056]
                else
                    return s__18Data[i-139247]
                endif
            else
                if(i<155629) then
                    return s__19Data[i-147438]
                else
                    return s__20Data[i-155629]
                endif
            endif
        endif
    else
        if(i<172011) then
            return s__21Data[i-163820]
        elseif(i<180202) then
            return s__22Data[i-172011]
        elseif(i<188393) then
            return s__23Data[i-180202]
        elseif(i<245730) then
            if(i<196584) then
                return s__24Data[i-188393]
            elseif(i<204775) then
                return s__25Data[i-196584]
            elseif(i<212966) then
                return s__26Data[i-204775]
            elseif(i<229348) then
                if(i<221157) then
                    return s__27Data[i-212966]
                else
                    return s__28Data[i-221157]
                endif
            else
                if(i<237539) then
                    return s__29Data[i-229348]
                else
                    return s__30Data[i-237539]
                endif
            endif
        else
            if(i<253921) then
                return s__31Data[i-245730]
            elseif(i<262112) then
                return s__32Data[i-253921]
            elseif(i<270303) then
                return s__33Data[i-262112]
            elseif(i<286685) then
                if(i<278494) then
                    return s__34Data[i-270303]
                else
                    return s__35Data[i-278494]
                endif
            else
                if(i<294876) then
                    return s__36Data[i-286685]
                else
                    return s__37Data[i-294876]
                endif
            endif
        endif
    endif
endfunction

function sg__Data_set takes integer i,integer v returns nothing
    if(i<8191) then
        set s__Data[i]=v
    elseif(i<16382) then
        set s__2Data[i-8191]=v
    elseif(i<24573) then
        set s__3Data[i-16382]=v
    elseif(i<163820) then
        if(i<32764) then
            set s__4Data[i-24573]=v
        elseif(i<40955) then
            set s__5Data[i-32764]=v
        elseif(i<49146) then
            set s__6Data[i-40955]=v
        elseif(i<106483) then
            if(i<57337) then
                set s__7Data[i-49146]=v
            elseif(i<65528) then
                set s__8Data[i-57337]=v
            elseif(i<73719) then
                set s__9Data[i-65528]=v
            elseif(i<90101) then
                if(i<81910) then
                    set s__10Data[i-73719]=v
                else
                    set s__11Data[i-81910]=v
                endif
            else
                if(i<98292) then
                    set s__12Data[i-90101]=v
                else
                    set s__13Data[i-98292]=v
                endif
            endif
        else
            if(i<114674) then
                set s__14Data[i-106483]=v
            elseif(i<122865) then
                set s__15Data[i-114674]=v
            elseif(i<131056) then
                set s__16Data[i-122865]=v
            elseif(i<147438) then
                if(i<139247) then
                    set s__17Data[i-131056]=v
                else
                    set s__18Data[i-139247]=v
                endif
            else
                if(i<155629) then
                    set s__19Data[i-147438]=v
                else
                    set s__20Data[i-155629]=v
                endif
            endif
        endif
    else
        if(i<172011) then
            set s__21Data[i-163820]=v
        elseif(i<180202) then
            set s__22Data[i-172011]=v
        elseif(i<188393) then
            set s__23Data[i-180202]=v
        elseif(i<245730) then
            if(i<196584) then
                set s__24Data[i-188393]=v
            elseif(i<204775) then
                set s__25Data[i-196584]=v
            elseif(i<212966) then
                set s__26Data[i-204775]=v
            elseif(i<229348) then
                if(i<221157) then
                    set s__27Data[i-212966]=v
                else
                    set s__28Data[i-221157]=v
                endif
            else
                if(i<237539) then
                    set s__29Data[i-229348]=v
                else
                    set s__30Data[i-237539]=v
                endif
            endif
        else
            if(i<253921) then
                set s__31Data[i-245730]=v
            elseif(i<262112) then
                set s__32Data[i-253921]=v
            elseif(i<270303) then
                set s__33Data[i-262112]=v
            elseif(i<286685) then
                if(i<278494) then
                    set s__34Data[i-270303]=v
                else
                    set s__35Data[i-278494]=v
                endif
            else
                if(i<294876) then
                    set s__36Data[i-286685]=v
                else
                    set s__37Data[i-294876]=v
                endif
            endif
        endif
    endif
endfunction

If you use HandleIndexing, you only have to create one extended array.
And the system attaches unique integers between 1 and 8190 to the unit.
And these attached integers fit into any arrays, not just extended arrays.

And this system is almost as fast as extended arrays.
 
Actually, extended arrays can only go up to 60000, and real arrays go up to index 8191, which is the 8192th slot (since you can have slot 0). And fine, you want a value between 0 and 8191? I'll give you one:

JASS:
function H2I takes handle h returns integer
    return 0
    return h
endfunction
function GetUniqueIndex takes handle h returns integer
    local integer i = H2I(h)
    return i - (i/8191) * 8191
endfunction
 
Level 11
Joined
Nov 4, 2007
Messages
337
Element!
Please go and learn from tutorials!

Just calcualte it, or test the function or somewhat.
If it was so easy to create unique Indexes between 1 and 8190,
why would anyone need extended arrays then?

i - (i/8191) * 8191

I tested it. Actually it returns 0, but it seems like a warcraft bug.
However; It doesn't create unitque Indexes, it just makes the Indexes smaller.
 
Last edited:
Level 11
Joined
Nov 4, 2007
Messages
337
Ok.
Your function is a HASH function I think.
But Indexes are overwrited!
So this is useful for example for timers,
but not for units, since all systems have access to units.

Edit: If this is working so well, why doesn't Vexorian use this in TimerUtils for example instead
of H2I-0x100000 ?
 
Level 11
Joined
Nov 4, 2007
Messages
337
Ok. I tested it correctly.
But artificle:
JASS:
function GetUniqueIndex takes nothing returns integer
    local integer i = 0x100000+50000+Count
    return i - (i/8191) * 8191
endfunction

globals
    integer Count = 0
    boolean Start = false
endglobals

function LeckHandleIDs takes integer amount returns nothing
    local integer i = 0
    local unit t
    set amount = amount*5
    loop
        set i = i + 1
        exitwhen i > amount
        set Count = Count + 1
            call Debug("For Handle: Created Index "+I2S(GetUniqueIndex()))
            call TriggerSleepAction(0.25)
    endloop
endfunction
And every 5 seconds, increase counter by 1000.
Results: The ID's are overwrited, but the Indexes are in a range between 0 and 8190.
However, this is useful for timers, but not for units.
And: I ask myself, why Vexorian doesn't use this for TimerUtils?
 
Ok then...
JASS:
globals
    integer array indexes
endglobals

function AssignUniqueIndex takes handle h returns integer
    local integer i = H2I(h)
    local integer j = i
    set i = i - (i/8191) * 8191
    if indexes[i]==0 then
        set indexes[i] = i
        return i
    endif
    loop
        exitwhen j == i or indexes[j] == 0
        set j = j + 1
        if j > 8191 then
            set j = 0
        endif
    endloop
    if j == i then
        return 8192 //too many indexes already assigned, return a bad number
    endif
    set indexes[j] = i
    return j
endfunction

function GetUniqueIndex takes handle h returns integer
    local integer i = H2I(h)
    local integer j = i
    if indexes[i] == i then
        return i
    endif
    loop
        exitwhen indexes[j] == i or j == i or indexes[j] == 0
        set j = j + 1
        if j > 8191 then
            set j = 0
        endif
    endloop
    if indexes[j] == 0 or j == i then
        return 8192//return bad number, no index found
    endif
    return indexes[j]
endfunction
 
Level 11
Joined
Nov 4, 2007
Messages
337
I tested both systems:
Your system is as fast as mine is as fast as PUI :p

Testcode:
JASS:
library Bench requires HandleIndexing, GUI, PUI
globals
    unit Temp = null
endglobals
function IND takes nothing returns nothing
    local integer i = 0
    local integer index = 0
    //call KillUnit(Temp)
    set Temp = CreateUnit(Player(0),'hpea',0.,0.,0.)
        loop
            set i = i + 1
            exitwhen i > 500
            
            //! PUI [ Perfect Unit Indexing ]
            
            //call GetUnitIndex(Temp)
            //call PeriodicRecycler()
            
            //! HandleIndexing!
            
            call Index(Temp)
            call FlushIndex(Temp)
            
            //! EOW GUI [Element of Water get unique Index]
            //call AssignUniqueIndex(Temp)
            //CODE FOR THE SYSTEM. IF A SYSTEM IS TESTED ==> RESTART MAP.
        endloop
    call RemoveUnit(Temp)
endfunction

function Benchmark takes nothing returns nothing
local integer sw = StopWatchCreate()
local real settime1 = StopWatchMark(sw)
local real settime2 = StopWatchMark(sw)
local real t1
local real t0 = StopWatchMark(sw)
local integer i = 0
        loop
            set i = i + 1
            exitwhen i > 2000
            call TimerStart(CreateTimer(),0.00,false,function IND)
        endloop
// Insert code to be benchmarked here
set t1 = StopWatchMark(sw)
call BJDebugMsg("HandleIndexing: "+R2S(t1-t0-(settime2-settime1)))

endfunction
endlibrary

After A system was tested, restart game :O


Edit: Sorry! My system is twice as much fast.
HandleIndexing: 0.010
Your System: 0.021
 
Mine was just an example of how to avoid bugs... it wasn't a system. I bet it's possible to optimize it a lot... besides, it doesn't even need that safety thing really - it's not like there's much chance of a collision is it? And without the safety thing mine would be super-efficient + only needs one function + doesn't have to store data in an array...
 
Level 11
Joined
Nov 4, 2007
Messages
337
Your code didn't need any safety, because I didn't create more than 8190 handles in my test.
The reason, why your system is slower, is that the math you use needs a lot more time to calculate.

Your code seems to bust the maximal size of an integer, turn negative, and then positive again.
I don't know exactly.
But here is the test without safety: (Tested 3000*40 times)

Your code: 0.014 seconds
My System: 0.014 seconds.
==> My system is safe, yours is not, but my system is as fast as yours :D
 
In actual fact, mine is safer than yours. What happens if the handle id is greater than 0x100000+8191 with yours? It goes over the array limit? Ooh, guess what? Crash. Unless, of course, you make MAX_HANDLE_ID greater than 8191, in which case it will be slower than mine. And there is the advantage of mine not using any globals at all, and only 2 functions.
 
Level 11
Joined
Nov 4, 2007
Messages
337
I have only more comfort functions.
In fact, you need only FlushIndex and Index.
Yours is __NOT__ Safer than mine.
With the safety, mine is 2 times faster.
Else as fast as yours.

It goes over the array limit? Ooh, guess what? Crash
==> Wrong

Unless, of course, you make MAX_HANDLE_ID greater than 8191, in which case it will be slower than mine
==> -.- Ok. Another test.
Guess what: Mine is still as fast as yours.
I increased the Handle ID by 16380.
I did the test without safety from your system.
Even if I increase the Handle ID by 81900, my system is as fast as yours xD.

Also: Your function has to calculate the Index each time you use the system.
Mine only once.
So if you get an index 5 times, your's will need the quintuple amount of time, mine maybe the twice.

===> For Set and Get with your system, you have to calculate the complicated math twice. If the Index is already created with my system, I only need H2I - 0x1000000

Lets summarize:
My system is as fast as yours,
My System is safer.
 
It goes over the array limit? Ooh, guess what? Crash
==> Wrong
Please explain what's wrong about that?

Unless, of course, you make MAX_HANDLE_ID greater than 8191, in which case it will be slower than mine
==> -.- Ok. Another test.
Guess what: Mine is still as fast as yours.
I increased the Handle ID by 16380.
I did the test without safety from your system.
Even if I increase the Handle ID by 81900, my system is as fast as yours xD.
Ok, I concede that point.

Also: Your function has to calculate the Index each time you use the system.
Mine only once.
So if you get an index 5 times, your's will need the quintuple amount of time, mine maybe the twice.
That may be true, but when would you ever want to get a unit's index more than twice?

===> For Set and Get with your system, you have to calculate the complicated math twice. If the Index is already created with my system, I only need H2I - 0x1000000
It's not like the complicated math will completely halt your system is it? And besides, yours still has to work out the if statement, mine has no such thing.

Lets summarize:
My system is as fast as yours,
My System is safer.
Your system is no safer. With yours it is possible to go over the array limit, with mine it is not.
 
Level 11
Joined
Nov 4, 2007
Messages
337
Its the users decision, how big the array Limit is.
Ok.
I have an offer:
I will credit you (if you invented the Hash function on your own) and check if the Handle ID of the suggested handle is bigger than MAX_HANDLE_ID.
If this is true, the system will swap to your system with safety.
==> If it swaps, it is 2 times slower (because of Safety), but the System won't ever cause bugs.

What do you think of that?
 
Level 11
Joined
Nov 4, 2007
Messages
337
@ Element of Water: I wont use the simple Modulo Hash!
It's unsafe and slow.
Unsafe: You can't clear Indexes. After you called the function 8191 times, there are no more unused Indexes.
Slow: If the Indexes go over 8191, you have to loop through a lot of Indexes. Maybe 6000?
==> Veeeeeeeery slow and unefficient.
Bleive me: My method is a lot better.

@Deaod: How can I test this proberly?
I don'tknow, how often FlushIndex is called.
If it's called too often, PUI might be faster.
But actually you don't have to call it manually.
You can Flush the Index, when a unit dies.
===> HandleIndexing is faster, when indexed units die rarer than ~3 times a second.
So HandleIndexing should be faster.



Edit: I am just looking over the PUI code.
Maybe I can find a better way to test it.



ModuloInteger(i,8191)
 
Unsafe: You can't clear Indexes. After you called the function 8191 times, there are no more unused Indexes.
Of course you can clear indexes. Why not?

Slow: If the Indexes go over 8191, you have to loop through a lot of Indexes. Maybe 6000?
You obviously don't know much about hashing.

==> Veeeeeeeery slow and unefficient.
Bleive me: My method is a lot better.
And what makes yours so good?
 
Level 11
Joined
Nov 4, 2007
Messages
337
Slow: If the Indexes go over 8191, you have to loop through a lot of Indexes. Maybe 6000?
===> You obviously don't know much about hashing.

loop
exitwhen j == i or indexes[j] == 0
set j = j + 1
if j > 8191 then
set j = 0
endif
endloop

And what makes yours so good?
If you don't like rit, you needn't use it.
==> Fast
==> Safe
==> Doesn't create a Bunch of Extended arrays.
==> For all Handle Types.
 
Level 11
Joined
Nov 4, 2007
Messages
337
JASS:
function GetUniqueIndex takes handle h returns integer
    local integer i = H2I(h)
    local integer j = i
    if indexes[i] == i then
        return i
    endif
    loop
        exitwhen indexes[j] == i or j == i or indexes[j] == 0
        set j = j + 1
        if j > 8191 then
            set j = 0
        endif
    endloop
    if indexes[j] == 0 or j == i then
        return 8192//return bad number, no index found
    endif
    return indexes[j]
endfunction
===> This will not work. H2I Number is too big for an array.

JASS:
function AssignUniqueIndex takes handle h returns integer
    local integer i = H2I(h)
    local integer j = i
    set i = i - (i/8191) * 8191
    if indexes[i]==0 then
        set indexes[i] = i
        return i
    endif
    loop
        exitwhen j == i or indexes[j] == 0
set j = j + 1
        if j > 8191 then
            set j = 0
        endif
    endloop
    if j == i then
        return 8192 //too many indexes already assigned, return a bad number
    endif
    set indexes[j] = i
    return j
endfunction
===> Same problem.
Why don't you want to accept, that your code is bad?

Ok. But at least the function wont loop 6000 times.
 
Top