Jump to content

Recommended Posts

Posted
Global $g_iCount
Exit test()
Func test()
    Local $iCount = 0 ; should always be 0
    For $n = 0 To 99
        Main()
        If $iCount <> $g_iCount Then
            ConsoleWrite(' ' & $n & '(' & $g_iCount & ')')
            $iCount = $g_iCount
        EndIf
    Next
    ConsoleWrite(@CRLF)
EndFunc   ;==>test

Func Magic()
    $g_iCount = 0
    Local $this[]
    $this['c'] = 11
    If $this[8] Then $g_iCount += 1 ; ConsoleWrite('lol $this[8] = ' & $this[8] & @CRLF)
    Return $this
EndFunc   ;==>Magic

Func Main()
    Local $a
    For $i = 0 To 99 Step 1
        Magic()
        Dim $a = [0, 0, 0, 0, 0, 0, 0, 0, 0]
        $a = CopyToMap($a)
    Next
EndFunc   ;==>Main

Func CopyToMap($x)
    Local $m[], $1 = UBound($x, 1)
    For $j = 0 To $1 - 1 Step 1
;~      $m[$j] = $x[$j] ; bug
        $m[String($j)] = $x[$j] ; no bug
    Next
    Return $m
EndFunc   ;==>CopyToMap

The help file states this in regards to the map functions:
Warning: This feature is experimental. It may not work, may contain bugs or may be changed or removed without notice.
DO NOT REPORT BUGS OR REQUEST NEW FEATURES FOR THIS FEATURE.

Therefore, all we can do is avoid them. The code above shows such bug. The solution: name your maps with strings.

Ok, good chat :)

Note:  @Mars shared this in a chat at https://autoit.de/ . And I did not know of it. And probably neither did you. Now you do. 

 

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Posted

First, I pared down the script to the essentials:

For $i = 1 To 20
    ; create new(!) map, add one element to the string index "c" (which calculated position in the internal list is index 8)
    Global $m1[]
    $m1["c"] = 1

    ; check if there`s a value at the index 8 (shouldn`t)
    If $m1[8] Then ConsoleWrite($m1[8] & @CRLF)

    ; create another map and add value at the same integer index as the one $m1 checked
    Global $m2[]
    $m2[8] = 1
Next

I made the following observations:

1. It does not occur with all string indexes, but only with some. It is noticeable that each string index has a fixed associated integer index. If you use a different integer index, the phenomenon does not occur. So far, I have found the following combinations in which the phenomenon occurs:

| String-Index | Integer-Index | 
| ------------ | ------------- |
| "["          | 0             |
| "<"          | 1             |
| ""           | 5             |
| "c"          | 8             |
| "D"          | 9             |
| "%"          | 10            |
| "k"          | 16            |
| "L"          | 17            |
| "-"          | 18            |
| "s"          | 24            |
| "T"          | 25            |
| "{"          | 32            |
| "\"          | 33            |
| "="          | 34            |

These relationships can be explained well using hash index collisions.
AutoIt uses the DJB2 hash function in its maps. If you use this for the letters and then scale it to the table size using modulo, you get exactly the corresponding integer indices.
Only the table size is obviously not fixed, as the index sometimes fits for 32 elements and sometimes for 64.
For example: “c” has a DJB2 hash value of 177672. If we assume that the map in AutoIt has 32 elements at that point, the index for “c” is calculated as 177672 % 32 = 8.
This also works for the other string indexes. E.g., “D” --> 177641 % 32 = 9 or “” --> 5381 % 32 = 5.
Only the size of the hash table is obviously variable, as some fit for a size of 32 elements and some for 64.
However, thanks to the bug, we can learn a lot about the internal structure of AutoIt maps. They are DJB2-based hash tables that start with 32 or 64 elements.

2. This behavior only occurs when the map $m2 is created and an element is created with the same integer index as in $m1. If only this index is set, a match is reported very often. If other indexes are also set, the match does not occur as frequently.

One possible explanation for this behavior is as follows:

A hash table is essentially an array with a specific size. This size is fundamentally independent of the number of elements it contains (resizing makes sense but is not mandatory). In this case, a map is obviously initially populated with either 32 or 64 elements. When a value is added to the hash table, the hash value of the key is calculated and this calculated value is mapped to the array size using modulo. If the key is “c”, for example, the DJB2 hash (the hash algorithm used in AutoIt) is 177672.
To obtain an index for our array in the map, we have to reduce this hash value to the range 32 or 64. This is easy to do using the modulo operator. And with 177672 % 32, we get 8 as the result.
The value for the key “c” is therefore stored in the internal array at index 8.
However, since several keys could end up at index 8, we need to create a way to hold multiple elements there.
For each index, a list is created (a “bucket”) that contains all elements whose key matches this index.
The key and value of these elements are then simply stored one after the other in the bucket itself.
When a value is queried, the index is first calculated and then this bucket is traversed linearly to see if the specific key is contained in it.

In AutoIt, these buckets do not necessarily seem to be used only by one map instance.
At some point, two maps share a bucket, resulting in a phenomenon like the one we see here.
So you would have to take a closer look at the initialization of the map to see exactly where the problem lies.

 

Posted
9 hours ago, AspirinJunkie said:

... The value for the key “c” is therefore stored in the internal array at index 8. ...

Nice dive into it @AspirinJunkie.

Since the help file states clearly that is "experimental" and basically "take it or leave it", would string only names have collisions ?, because if they don't, just avoiding integers ( that could be matching the index ) would solve the problem/bug, in using maps.

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...