A little Bug about NumPut

Report problems with documented functionality
feiyue
Posts: 349
Joined: 08 Aug 2014, 04:08

A little Bug about NumPut

03 Oct 2017, 22:54

I ran into a confusing place when I was using "NumPut", the help file said:
If VarOrAddress is a variable such as MyVar, the address of the variable's string buffer is used.
But the following code doesn't work as much as I want.

Code: Select all

a=12345678
MsgBox, % Varsetcapacity(a)  ;-->0 , I want >=4
NumPut(65535<<16, a, "int")  ;  Use &a is ok, But a is wrong
MsgBox, % NumGet(a, "uint")>>16  ;-->50 , I want 65535
just me
Posts: 9425
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: A little Bug about NumPut

04 Oct 2017, 04:55

I think this is caused by the number caching feature:
Variable types: AutoHotkey has no explicitly defined variable types. However, a variable containing only digits (with an optional decimal point) is automatically interpreted as a number when a math operation or comparison requires it. (To improve performance, numbers are cached internally to avoid conversions to/from strings.)
You won't find much information in the docs about this feature, at least I didn't. And I didn't know as yet that it already works this way in AHK 1.1. So the following is a mix of known facts and assumptions:
  • Code: Select all

    a = 12345678
    Fact: This assigns a pure numeric string. The integer value is stored in the number cache of the variable.
  • Code: Select all

    MsgBox, % Varsetcapacity(a)  ;-->0
    Guess: Because the value 12345678 can be cached as a pure number without loss of information, the internal size of the variable's string buffer is set to zero.
    Fact: This does not happen if you preceed the number string with a 0.
  • Code: Select all

    NumPut(65535<<16, a, "int")  ;  Use &a is ok, But a is wrong
    Guess: NumPut() failes if you pass the variable by name, because the size of the variable's string buffer is 0.
    Fact: The failure can be verified by checking the return value. This does not happen if the variable is passed by address (&Var). Though not explicitly documented, the variable's string buffer is rebuilt from the number cache in this case and the size of the string buffer is sufficient to store an integer.
  • Code: Select all

    MsgBox, % NumGet(a, "uint")>>16  ;-->50
    Guess: On assignment, the string 12345678 has been put into the variable's string buffer. It's still there even though the size of the buffer has been set to 0.
    Fact: NumGet() doesn't check the buffer's size, so it reads the first 4 bytes of the buffer. This value is 3276849 respectively 0x00320031 or "12" on AHK Unicode. Shifting this value 16 bits to the right results in 0x32 = 50.
Bug or feature, that is the question.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: A little Bug about NumPut

04 Oct 2017, 07:22

There is no indication in the documentation that what you are trying to do will work. The correct way to put a number at the address of a variable ('s stringbuffer), is to set the capacity accordingly with varsetcapacity, or use varsetcapacity to check if it has sufficient capacity already, and then do it. In your script, you check and see that there isn't enough capacity, and then try anyways, no surprise it doesn't work. just me's comments about number caching explains why the capacity is 0.
Correct way:

Code: Select all

MsgBox % Varsetcapacity(a,4) 
msgbox % NumPut(65535<<16, a, "int")

Additionally,
numput wrote:If VarOrAddress is a variable such as MyVar, the address of the variable's string buffer is used.
numput wrote:If the target address is invalid, an empty string is returned. However, some invalid addresses cannot be detected as such and may cause unpredictable behaviour.
Check the return:

Code: Select all

msgbox % NumPut(65535<<16, a, "int") ; blank
AHK detects the invalid address, and returns blank. v2 will (most likely) crash beacuse in v2 numput will try to put a number at the address 12345678. The corresponding behaviour in v1 would be msgbox % NumPut(65535<<16, a+0, "int")
Though not explicitly documented, the variable's string buffer is rebuilt from the number cache in this case and the size of the string buffer is sufficient to store an integer.
& wrote:&MyVar also disables the caching of binary numbers in that variable,
Hence,

Code: Select all

msgbox % &a
msgbox % NumPut(65535<<16, a, "int")
will work in this case.

Cheers.
just me
Posts: 9425
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: A little Bug about NumPut

04 Oct 2017, 08:20

Helgef wrote:In your script, you check and see that there isn't enough capacity, and then try anyways, no surprise it doesn't work.
I think this check was added after the issue was detected.
Helgef wrote:AHK detects the invalid address, and returns blank.
I don't think that the address is invalid. The same address is passed to NumGet() which is able to read reasonable contents from that address. That's at least inconsistent (though unlike writing reading cannot corrupt memory).

Also, I wonder why the first byte (ANSI) / two bytes (Unicode) is / are not set to zero to mark the string buffer as empty.
just me
Posts: 9425
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: A little Bug about NumPut

04 Oct 2017, 10:25

Something strange is happening:

Code: Select all

#NoEnv
A := True   ; built-in AHK variables are strings
B := False  ; "
C := "1"    ; numeric string
D := "0"    ; "
E := 1      ; number
F := 0      ; "
MsgBox, 0, Capacity (Unicode), % "A:`t" . VarSetCapacity(A) . "`n"
                     . "B:`t" . VarSetCapacity(B) . "`n"
                     . "C:`t" . VarSetCapacity(C) . "`n"
                     . "D:`t" . VarSetCapacity(D) . "`n"
                     . "E:`t" . VarSetCapacity(E) . "`n"
                     . "F:`t" . VarSetCapacity(F)
MsgBox wrote:A: 6
B: 6
C: 6
D: 6
E: 0
F: 6
It doesn't make sense for me.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: A little Bug about NumPut

04 Oct 2017, 11:16

I get 6 for all 6, may be related to SetFormat that is used in my script somewhere.
In a separate otherwise empty script, I also got the one 0 like you did.
In AHK v2 I got 6 for all 6.

With SetFormat, anything unusual always seems to be related to this:
SetFormat
https://autohotkey.com/docs/commands/SetFormat.htm
If the slow mode "Integer" or "Float" is used anywhere in the script, even if that SetFormat line is never executed, the caching of integers or floating point numbers (respectively) is disabled the moment the script launches.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: A little Bug about NumPut

04 Oct 2017, 11:57

Towards me it just seems that 0 is not cached as a number
Recommends AHK Studio
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: A little Bug about NumPut

04 Oct 2017, 12:04

Hello just me.
I think this check was added after the issue was detected.
Probably, and it answers the question, why is NumPut(65535<<16, a, "int") wrong?
The remaining valid questions questions are,
  • Why is varsetcapacity(a) = 0 ? You answered.
  • How to get Varsetcapacity(a) >= 4? I answered.
  • Why is NumPut(65535<<16, &a, "int") ok? I answered.
I don't think that the address is invalid.
It sure is, we can see in script varsetcapacity(a) = 0, surely AHK can detect this internally. I don't know why numget doesn't detect the same issue though, it should I guess. :think:
Also, I wonder why the first byte (ANSI) / two bytes (Unicode) is / are not set to zero to mark the string buffer as empty.
My guess is, there is no allocated string buffer to write 0 to.

This is a case of invalid usage, not a bug, we shouldn't try to either read from, or write to, something which isn't sized properly. v2 is better, since it is more likely to crash than continue running with invalid results.
Something strange is happening:
I get the same results as you just me.
Towards me it just seems that 0 is not cached as a number
Indeed.

Cheers
feiyue
Posts: 349
Joined: 08 Aug 2014, 04:08

Re: A little Bug about NumPut

04 Oct 2017, 14:04

I guess "MyVar=12345678" has placed the string in the variable address,
It seems that for NumGet, the use of MyVar is equivalent to "&MyVar",
But for NumPut, the use of MyVar does not necessarily mean "&MyVar" ( When MyVar is a pure digital ).

I thought I could be lazy and use "a=1234" instead of VarSetCapacity(a, 4),
It seems that we should use VarSetCapacity honestly before using NumPut. :D
Noesis
Posts: 301
Joined: 26 Apr 2014, 07:57

Re: A little Bug about NumPut

05 Oct 2017, 02:06

feiyue wrote:I guess "MyVar=12345678" has placed the string in the variable address,
It may help to think of variables as always being numbers unless they in no way can be numbers and so must be a string. Just saying as it seems you expect the opposite. Personally I can't fathom how MyVar=12345678 could be interpreted as a string. If on the other hand you made it clear it was to be a string and forced ahk to interpret it as a string, by doing MyVar:="12345678", then you would find that you do get your expected result, without using VarSetCapacity, and without requiring &a.
just me
Posts: 9425
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: A little Bug about NumPut

05 Oct 2017, 04:54

After a look into the AHK sources I found the reason for the NumGet() mystery. Different from NumPut() the string buffer is rebuilt in any case if not marked as synchronized with the number cache. That's always true for variables of zero capacity holding a cached number.

By default, all numbers are cached as Int64 or Double. Only pure integers without leading zeroes are not additionally stored as passed in the variable's string buffer. 0 is stored, because the one and only zero is handled as a leading zero.
Noesis wrote:Personally I can't fathom how MyVar=12345678 could be interpreted as a string.
Variable types: AutoHotkey has no explicitly defined variable types. However, a variable containing only digits (with an optional decimal point) is automatically interpreted as a number when a math operation or comparison requires it. (To improve performance, numbers are cached internally to avoid conversions to/from strings.)

Storing values in variables: To store a string or number in a variable, there are two methods: traditional and expression. The traditional method uses the equal sign operator (=) to assign unquoted literal strings or variables enclosed in percent signs.
feiyue
Posts: 349
Joined: 08 Aug 2014, 04:08

Re: A little Bug about NumPut

05 Oct 2017, 06:59

just me wrote: By default, all numbers are cached as Int64 or Double. Only pure integers without leading zeroes are not additionally stored as passed in the variable's string buffer.
@just me Thank you for your explanation. I finally got it.
My guess is wrong, "MyVar=12345678" is not written directly into the memory address of the MyVar,
Instead, the MyVar memory address is written from the number cache until the NumGet or other string operation is performed.

Return to “Bug Reports”

Who is online

Users browsing this forum: No registered users and 31 guests