AHK v2: converting/optimizing scripts Topic is solved

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Problems with objects and legacy syntax

13 Dec 2018, 20:15

oif2003 wrote:
13 Dec 2018, 15:21
I finally broke down and adjusted it to use loop parse. I also reindented the code so hopefully I didn't break anything doing that
This version runs as fast as 1-line looper. Absolutely the same timing. Just a side-note: so there is no point in stuffing things in super-long lines for performance increase. :D Keeping each line shorter than 80-100-120 symbols just makes code more easily maintainable.
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Problems with objects and legacy syntax

13 Dec 2018, 20:34

oif2003 wrote:
13 Dec 2018, 12:01
and this in json.get(): combined conditionals on (a) and removed StrLen(v)

Code: Select all

				: ((c==q) ? (_n:=InStr(_s,q,, n+1),v:=SubStr(_s, n+1, _n-n-1)
						, n:=_n, (kf) && (k:=v, z:=":", u:=1)) ;string literals
					: (_n:=SubStr(_s, n) ~= "[\]\},\s]|$",v:=SubStr(_s, n, _n-1)
						, n+=_n-2, (v is "Number") ? v+=0:E(c, n)) ;number
				, (u) || (a ? (k:=b.Push(v), z:=",]"):(b[k]:=v, z:=",}")))
This patch I liked. =) 828 vs 844. +1.9% (with compact json. I think we should focus on compact json optimizations) and readability increase. before it was:
Spoiler
Forgive me, but I just replaced _n local var used just inside these 2 branches with d (literal is not used yet). _n is already used as an argument of closure right in the same method and I use names starting with _ underscore as function arguments for consistency (so _ marks them as if they r "Read Only" or for special ByRef treatment). :D

Code: Select all

				: ((c==q) ? (d:=InStr(_s,q,, n+1),v:=SubStr(_s, n+1, d-n-1)
					, n:=d, (kf) && (k:=v, z:=":", u:=1)) ;string literals
				: (d:=SubStr(_s, n) ~= "[\]\},\s]|$",v:=SubStr(_s, n, d-1)
					, n+=d-2, (v is "Number") ? v+=0:E(c, n)) ;number
				, (u) || (a ? (k:=b.Push(v), z:=",]"):(b[k]:=v, z:=",}")))
Last edited by vvhitevvizard on 13 Dec 2018, 20:50, edited 2 times in total.
oif2003
Posts: 214
Joined: 17 Oct 2018, 11:43
Contact:

Re: Problems with objects and legacy syntax

13 Dec 2018, 20:38

vvhitevvizard wrote:
13 Dec 2018, 20:34
Nice! it's one step closer to perfection! :bravo:
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Problems with objects and legacy syntax

13 Dec 2018, 20:51

oif2003 wrote:
13 Dec 2018, 20:38
Nice! it's one step closer to perfection! :bravo:
No doubt. whopping +30% performance increase since 1170 score. :thumbup: And I mind u, first version I posted was already optimized to a great degree.
And code we made together is much much shorter and somewhat even more readable.
Last edited by vvhitevvizard on 13 Dec 2018, 22:50, edited 2 times in total.
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Problems with objects and legacy syntax

13 Dec 2018, 21:31

To proceed with dense lines further, I believe we need a bit higher level of commentary over json.Get() locals:

Code: Select all

;a=flag: simple arr [], n=cur. parsing pos in the json str, c=cur chr, z=next chr(s) expected,
;	v=cur val, k=cur key name, kf=flag: literal key name is allowed as next token
;	d=len of the right val (literal string/number), u=flag: we've got key (not val) at this step
;	s:{} =stack, b:{} =sub-objects/keys for cur level of nesting, p=prototype obj for simple arr []
2.
haha it lacks The Final error handling for enormous level of nestedness. :D
There is no sanity check for that matter yet and just 10KB json with nested pattern like j:='{"k":[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]}' might crash AHK or even worse.
Small and valid (by json standard, e.g. every open bracket has its closing counterpart) data chunk with ~15 nesting levels like sample above makes noticeable parsing slowdown for output by json.Set(): 1600 vs 375.
Its like x4 as slow. So recursion is baaad for AHK, no exception to the rule here. :)
But its not the slowdown that bothers me. Some malicious input might break down afk-functioning of scripts. We miss some setting (class key) for nestedness level limit. e.g. json.Nesting:=10
Beautified/Compact output:
ImageImage
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Problems with objects and legacy syntax

14 Dec 2018, 00:11

concept proof with bits holder (just 1 integer using bit shifting and logical OR, AND, XOR) instead of multiple .__Arr keys.
compact input: 844 vs 828. Somewhat slower.
beautified input: 1360 vs 1375 Somewhat faster.
It appears to be faster for non-regular j:='{"k":[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]}' data. 781 vs 828. +5.6% So pure logic (w.o keys inside brackets) is definitely faster. So, for regular data, it should actually win over prev. method by a tiny margin theoretically.
It consumes less memory theoretically. -1 static local. created Array [] objects DONT HAVE .__Arr key! Thats important I think. Should we decide to enumerate and update these imported objects.
New nesting level adding is boiled down to simple s.InsertAt(1,v:={}), no more funny arrays.
But I'm not sure would it supersede the previous routine. Ah, and it added funny stuff like y:=y<<1&~1, y:=y<<1|1, y>>=1 ;)

Im pretty sure it would be the fastest and of the lowest memory consumption procedure should I rewrite this logic with assembler using CPU registers

Code: Select all

;parse json string. obj:=json.get(str)
;JSON keys are always "strings", expected values r: number|"string"|{object}|[array]
	Get(ByRef _s){ 
		static q:=Chr(34), x:=q "{[0123456789-" ;json: allowed chars outside strings
		n:=0, k:="", kf:=0, s:=[b:=root:={}], z:="{", y:=0
;a=flag: simple arr [], n=cur. parsing pos in the json str, c=cur chr, z=next chr(s) expected,
;	v=cur val, k=cur key name, kf=flag: literal key name is allowed as next token
;	d=len of the val/literal right string, u=flag: we've got key (not val) at this step
;	s:{} =stack, b:{} =sub-objects/keys for cur level of nesting, p=prototype obj for simple arr []
		while(c:=SubStr(_s, ++n, 1))!==""
			InStr(" `t`n`r", c) || (
				InStr(z, c) || E(c, n), u:=0
				, InStr("}]", c) ? ((s.1=root) && E(c, n)
					, s.RemoveAt(1), b:=s.1, y>>=1, z:=(a:=y&1) ? ",]" : ",}")
				: InStr(",:", c) ? (z:=(kf:=!a && c==",") ? q:x)
				: InStr("{[", c) ? ((c=="{") ? (kf:=1, y:=y<<1&~1, z:=q "}") : (y:=y<<1|1, z:=x "]")
						, s.InsertAt(1,v:={}), a ? k:=b.Push(v):b[k]:=v
						, b:=s.1, a:=y&1)
				: ((c==q) ? (d:=InStr(_s,q,, n+1),v:=SubStr(_s, n+1, d-n-1)
						, n:=d, (kf) && (k:=v, z:=":", u:=1)) ;string literals
					: (d:=SubStr(_s, n) ~= "[\]\},\s]|$",v:=SubStr(_s, n, d-1)
						, n+=d-2, (v is "Number") ? v+=0:E(c, n)) ;number
					, (u) || (a ? (k:=b.Push(v), z:=",]"):(b[k]:=v, z:=",}")))
			)
		return(s.1=root || E(c, n), s[1,""]) ;s.Count()!=1 ;unpaired {}, []
		E(_c,_n)=>this._T("Unexpected char <" _c "> in pos " _n)
	}
Last edited by vvhitevvizard on 14 Dec 2018, 02:03, edited 14 times in total.
oif2003
Posts: 214
Joined: 17 Oct 2018, 11:43
Contact:

Re: Problems with objects and legacy syntax

14 Dec 2018, 00:33

That looks interesting, are you planning on limiting it to say 64 levels? I suppose you can always extend it for more. I messed around with using an array push/pop to track object type, but it was too slow, slower than your implementation.

Also just fooling around with older version of the code...this worked (377 [] pairs), but adding one more pair will fail

Code: Select all

j:='{"k":[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]}'
:lol:
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Problems with objects and legacy syntax

14 Dec 2018, 00:38

oif2003 wrote:
14 Dec 2018, 00:33
That looks interesting, but are you sure you want to limit it to say 64 levels?
I think 3-4 levels max is regular use. Try to recall when u used 4-dimentional a[x,y,z,w] arrays last time :)
Last edited by vvhitevvizard on 14 Dec 2018, 01:43, edited 1 time in total.
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Problems with objects and legacy syntax

14 Dec 2018, 00:39

oif2003 wrote:
14 Dec 2018, 00:33
I messed around with using an array push/pop to track object type, but it was too slow, slower than your implementation.
I updated the prev. post. So what do u think: should it supersede the previous routine? Can u optimize something in it? :D
oif2003
Posts: 214
Joined: 17 Oct 2018, 11:43
Contact:

Re: Problems with objects and legacy syntax

14 Dec 2018, 00:50

One time I used json to store files inside ahk script's block comment section preserving file directory structure. In that situation, being able to go 4 deep is kind of nice. Also I might use json to autoload objects into memory on script load and autosave on exit to give the script a persistent feel without using ini files. Not being limited to a shallow depth like 4 levels would be a nice feature. I think we should stick with the current implementation for now unless we find something that is at least twice as fast. But that is not to say we can't keep trying!
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Problems with objects and legacy syntax

14 Dec 2018, 00:55

oif2003 wrote:
14 Dec 2018, 00:50
One time I used json to store files inside ahk script's block comment section preserving file directory structure. In that situation, being able to go 4 deep is kind of nice. Also I might use json to autoload objects into memory on script load and autosave on exit to give the script a persistent feel without using ini files. Not being limited to a shallow depth like 4 levels would be a nice feature. I think we should stick with the current implementation for now unless we find something that is at least twice as fast. But that is not to say we can't keep trying!
well, yeah. But I think something like 10-15 levels is a sane limit. And cap is 63 (cuz first level bit is wasted with opening bracket - and its always {} type of Array).

btw, with new method, created Array [] objects DONT HAVE .__Arr key any longer! Thats good should we re-use input data for enumerating/etc. these garbage .__Arr keys would require some additional treatment consuming CPU time and memory.

So new version has no severe disadvantages, json.Get() code structure remains clean, and it leaves no garbage in objects. I wish I could optimize it a bit just to be on par with prev method for the test string.
Last edited by vvhitevvizard on 14 Dec 2018, 02:06, edited 13 times in total.
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Problems with objects and legacy syntax

14 Dec 2018, 01:08

oif2003 wrote:
14 Dec 2018, 00:33
Also just fooling around with older version of the code...this worked (377 [] pairs), but adding one more pair will fail
This one just hangs. btw new version throws an exception normally. w/o any additional checkups its hardcoded 64 levels limit. it just overflows bitdata and meets an un-paired bracket eventually. Sanity check is still required.

I think ur 377 "magic number" is due to AHK's stack capacity for nesting objects. :D
Ive read it before:
The recursive calls will crash AHK (stack overflow) for strings longer than 373 (U64), 587 (U32), and 758 (A32) characters (at least on my system).
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Problems with objects and legacy syntax

14 Dec 2018, 02:50

v1.2.3 with "bitholder" variable and nesting level sanity check for incoming json data. json.Nesting setting.
scores 860..875 vs 828..840 of v1.1. But I think less garbage and complete check is more important for json data than 3-4% of performance for compact json string test. For beautified string test its +1% perf. boost still.

Code: Select all

#SingleInstance force

class json{ ;json.get by coco and heavily modified by vvhitevvizard and oif2003
;json.set by vvhitevvizard and heavily modified by oif2003
;ver 1.2.3

;1) 0=enable indentation and line feed, 1=the most compact representation:
;2) spaces per level, similar to JavaScript's JSON.stringify() 'space' parameter
;	JSON array elements and object members will be pretty-printed with the indent level
;3) Sanity check of input json data for level of nesting. capped at 63
;4) 0=suppress error on built-in object types, just output a class name, e.g.: "File Object"
;5) 0=supress error if the root is not an object:
	static Compact:=0, Space:=4, Nesting:=10, ErrObjType:=0, ErrObj:=0

;"stringify": format [associative] array (object) as JSON string
;str:=json.set(obj). returns empty-line if non-object passed
	Set(_o, _d:="") { ;_d:cur indent
		static q:=Chr(34), t, p
		if !IsObject(_o) ;"string" | number
			return this.ErrObj && this._T("Not an object.")
		(p=this.Space) || t:=indent(p:=this.Space)
			, (this.Compact) ? n:=d:="" : (n:="`n", d:=_d t), VarSetCapacity(s,1024*8)
		try a:=_o._ ;trying to get a unique key
		catch ;"Unknown property" exception means its a built-in inenumerable obj
			return(s:=Type(_o) " Object"
				, (this.ErrObjType) && this._T(s " type is not supported."), "{" q s q "}")
		if(a:=_o.HasKey(_o.Count())){ ;=0: not a linear array
			for i in _o ;loop check for tricky arr, e.g. {"key":0,"2":0}
				if !(a:=i=A_Index)
					break
		} ;flag a=0:associative or sparse (e.g. [1,,3]) array -> to be treated as obj {}
		for k,v in _o ;recursive
			s.=d (a ? "" : q k q ":") (isObject(v) ? this.Set(v, d) 
				: (Type(v)=="String") ? q v q : (Type(v)=="Float") ? Format("{:g}", v) : v) "," n
		return(a ? "[" : "{") (s ? (n RTrim(s, "," n) n _d) : "") (a ? "]" : "}") ;wrap in brackets
		indent(n)=>--n ? indent(n) " ":""
	}

;parse json string. obj:=json.get(str)
;JSON keys are always "strings", expected values r: number|"string"|{object}|[array]
	Get(ByRef _s){ 
		static q:=Chr(34), x:=q "{[0123456789-" ;json: allowed chars outside strings
		n:=0, k:="", kf:=0, s:=[b:=root:={}], z:="{", y:=0
;a=flag: 1:array [], 0:object {}, y=bits for 63 nested levels for a flag
;	n=cur. parsing pos in the json str, c=cur chr, z=next chr(s) expected
;	v=cur val, k=cur key name, kf=flag: literal key name is allowed as next token
;	d=len of the val/literal right string, u=flag: we've got key (not val) at this step
;	s:{} =stack, b:{} =sub-objects/keys for cur level of nesting
		while(c:=SubStr(_s, ++n, 1))!==""
			InStr(" `t`n`r", c,1) || ( ;1=CaseSensitive
				InStr(z,c,1) || E(c, n), u:=0
				, InStr("}]", c) ? ((s.1=root) && E(c, n)
					, s.RemoveAt(1), y>>=1, b:=s.1, z:=(a:=y&1) ? ",]" : ",}")
				: InStr(",:",c,1) ? (z:=(kf:=!a && c==",") ? q:x)
				: InStr("{[",c,1) ? (s.Count()>this.Nesting && E(c, n)
						, (c=="{") ? (kf:=1, y:=y<<1&~1, z:=q "}") : (y:=y<<1|1, z:=x "]")
						, s.InsertAt(1,v:={}), a ? k:=b.Push(v):b[k]:=v, b:=s.1, a:=y&1)
				: ((c==q) ? (d:=InStr(_s,q,1, n+1),v:=SubStr(_s, n+1, d-n-1)
						, n:=d, (kf) && (k:=v, z:=":", u:=1)) ;string literals
					: (d:=SubStr(_s, n) ~= "[\]\},\s]|$",v:=SubStr(_s, n, d-1)
						, n+=d-2, (v is "Number") ? v+=0:E(c, n)) ;number
					, (u) || (a ? (k:=b.Push(v), z:=",]"):(b[k]:=v, z:=",}")))
			)
		return(s.1=root || E(c, n), s[1,""]) ;s.Count()!=1 ;unpaired {}, []
		E(_c,_n)=>this._T("Unexpected char <" _c "> in pos " _n)
	}
	_T(_s){
		throw(_s)
	}
}

; Test runs

j:="
(
{
    "a":[
        111,
        "aa"
    ],
    "amount":101,
    "price":104.2,
    "r1":{
        "misc":12,
        "r2":{
            "type1":11,
            "type2":11
        }
    },
    "type":"ask"
}

)"

j:='{"r1":{"r2":{"type1":11,"type2":11},"misc":12}, "type":"ask","price":104.2,"amount":101, "a":[111,"aa"]}'

n:=20000
t:=A_TickCount
loop(n)
	b:=json.Get(j)
a1:=A_TickCount-t

t:=A_TickCount
loop(n)
	s:=json.Set(b)
a2:=A_TickCount-t

msgbox(clipboard:=a1 "|" a2 "`n`n" s)
Last edited by vvhitevvizard on 14 Dec 2018, 20:07, edited 10 times in total.
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Problems with objects and legacy syntax

14 Dec 2018, 03:25

Another must-have thingie to be optimized (but also converted before that) for AHK v2 is anchor routine. http://www.autohotkey.com/forum/topic4348.html
Interested? :)
its very old, some files fail to run even with AHK v 1.1. But the concept is truly brilliant.

here is anchor 3.2 of the earliest versions that works for me. For concept sake. I added some commentary from other versions of the script.
anchor test1.ahk
anchor v3.2.ahk:

Code: Select all

Anchor(_c, _a, d:=0){
;controls are moved/resized by a fraction of the amount the gui changes size
;e.g.     New pX := orig. pX  + factor * ( current guiW - orig. guiW )
        static pos
        sig=`n%A_Gui%:%_c%= ;unique prefix to store pos/size

        if(d=1)
		draw:="Draw", d:=1,1,1,1
        else if (!d)
                d:=1,1,1,1
        StringSplit q, d, `,

;on first call for this control, remember original position, size and factor:
        if !InStr(pos, sig){
 ;get pos/size of control
		GuiControlGet p, Pos, %_c%
;store (orig. control pos/size - orig. gui width/height * factor) and factor in one var

		pos:=pos . sig . px-A_GuiWidth*q1 . "/" . pw-A_GuiWidth*q2 . "/"
			. py-A_GuiHeight*q3 . "/" . ph-A_GuiHeight*q4 . "/"
        }
    ;get pos/size string and split into array
        StringTrimLeft p, pos, InStr(pos, sig)-1+StrLen(sig)
        StringSplit p, p, /

        c:="xwyh"
    ;calculate new position and size
        loop parse, c
        if InStr(_a, A_LoopField){
                if(A_Index<3) ;for x/w use current gui width
                        e:=p%A_Index%+A_GuiWidth*q%A_Index%
                else
                        e:=p%A_Index%+A_GuiHeight*q%A_Index% ;otherwise use current gui height
;combine "x/w/y/h"
;with (orig. control pos/size - orig. gui width/height*factor + current gui width/height*factor)
                m = %m%%A_LoopField%%e%
        }

 ;move/resize control to new pos/size
        GuiControl %A_Gui%:Move%draw%, %_c%, %m%
}
and here goes the last, optimized for speed version, that stores its internal data using Numput()/NumGet() :)
anchor test1.ahk
anchor v4.6.ahk


Ive just started to convert old 3.2 ver to work with AHK v2. here goes test env. (gui with some gui controls) . the same that I used for AHK 1.1 Anchor versions
anchor test1.ahk2:

Code: Select all

#SingleInstance force
global oo:={}
;#Include %A_ScriptDir%\anchor v3.2.ahk

o:=oo
g:=o[1]:=GuiCreate(" +Resize","test")
g.marginX:=g.marginY:=3

o[4]:=g.Add("button", "section vBtnAddGroup" , "+")
o[5]:=g.Add("edit", "x+1 ys+1 w200 vEdtGroupName")
o[6]:=g.Add("button", "x+1 ys vBtnRemoveGroup" , "-")
o[7]:=g.Add("button", "x+40 ys vBtnAddScriptlet" , "+")
o[8]:=g.Add("edit", "x+1 ys+1 w150 vEdtScriptletName")
o[9]:=g.Add("button", "x+1 ys vBtnRemoveScriptlet", "-")
o[10]:=g.Add("ddl", "x+40 ys w150 vDdbScriptletInGroup Sort")
o[11]:=g.Add("button", "ys vBtnCopyToClipboard", "Copy to &Clipboard")
o[12]:=g.Add("treeview", "xs section w250 h500 vTrvScriptlets")
o[13]:=g.Add("edit", "ys w500 h500 vEdtScriptletData")

g.OnEvent("Close", ()=>ExitApp())
g.OnEvent("Escape", ()=>ExitApp())
g.OnEvent("Size", "Size")
g.Show("AutoSize")
exit

Size(_g, _e, _w, _h){
;	if(_e<0)
;		msgbox(_g.title ":Minimized")
}
Last edited by vvhitevvizard on 14 Dec 2018, 19:00, edited 2 times in total.
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Problems with objects and legacy syntax

14 Dec 2018, 04:58

BTW, I figured out a quick sub-test (to skip even 1st iteration of for-loop test) for non-linear (sparse or associative) arrays:

Code: Select all

a:=[1,2,3,4]
msgbox(a.HasKey(a.Count())) ;=1

a:=[1,2,,4]
msgbox(a.HasKey(a.Count())) ;=0 (not a linear array)
so the next snippet from json.Set() can be optimized for perf.:

Code: Select all

		if(a:=_o.HasKey(_o.Count())){ ;=0: not a linear array
			for i in _o
				if !(a:=i=A_Index)
					break
		} ;flag a=0:associative or sparse (e.g. [1,,3]) array -> to be treated as obj {}
for-loop check is still required for tricky arrays such as

Code: Select all

a:={"key":0,"2":0}
msgbox(a.HasKey(a.Count())) ;=1, but it CANT be exported as [] array, it has a named key!
:D

2.
At some point we might want support for escape characters (at least for escaping double quote itself) and/or non-decimal numbers e.g. with "\u" literals to be back.
but if we DO have a task of storing regex strings to json, I would prefer code/decode them via uuencoding or something and store as a plain literal string.
Last edited by vvhitevvizard on 14 Dec 2018, 17:19, edited 4 times in total.
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Problems with objects and legacy syntax

14 Dec 2018, 16:17

to moderators: Could u pls rename the topic to "AHK v2: converting/optimizing scripts"
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: AHK v2: converting/optimizing scripts

14 Dec 2018, 18:11

quest for perfection is not over, my friend. =) And I was dead set on gaining back perf. which was somewhat lost after the additional checkup and bits-manipulation logic in v1.2.2.
InStr():
3rd parameter: CaseSensitive: Integer
If the parameter CaseSensitive is omitted or 0 (false), the search is not case sensitive (the method of insensitivity depends on StringCaseSense); otherwise, the case must match exactly.


That means we have to add 1 as 3rd parameter for every instance of the function (there r 6 of them), e.g.:
InStr(z,c) -> InStr(z,c,1)
for compact json: 860 vs 875 of v1.2.2. +1.75% perf boost
I wish default of InStr() would be "case-sensitive search" tho. Cuz case-insensitive means conversion of both strings
And I modified/added some commentary to the script.
Updated post: https://www.autohotkey.com/boards/searc ... 9&sr=posts to v1.2.3

2.
Actually, a global patameter A_StringCaseSense (Read/write, default is 0) can be set to optimize all string comparisons =, != and InsStr.
oh, nvm, its overcomplicated:
•Expression comparison operators (except ==). However, since the equal-sign operator (=) is always case-insensitive, it uses the Locale mode when StringCaseSense is On, as does the case-insensitive mode of InStr.
•InStr. However, it is not affected when its CaseSensitive parameter is true.

On or 1 (true): String comparisons are case sensitive. This setting also makes the expression equal sign operator (=) and the case-insensitive mode of InStr use the locale method described below.

Off or 0 (false): The letters A-Z are considered identical to their lowercase counterparts. This is the starting default for all scripts due to backward compatibility and performance (Locale is 1 to 8 times slower than Off depending on the nature of the strings being compared).

Locale String comparisons are case insensitive according to the rules of the current user's locale. For example, most English and Western European locales treat not only the letters A-Z as identical to their lowercase counterparts, but also ANSI letters like Ä and Ü as identical to theirs.


so A_StringCaseSense:=0 doesnt make =, != as case-sensitive (disabling under-the-hood hidden case conversion). :(
Last edited by vvhitevvizard on 14 Dec 2018, 20:08, edited 3 times in total.
oif2003
Posts: 214
Joined: 17 Oct 2018, 11:43
Contact:

Re: AHK v2: converting/optimizing scripts

14 Dec 2018, 18:49

Hey, I've been a little busy and haven't had the chance to read all your posts in detail yet. Before I get busy again I want to show you this quick script I threw together. It parses the script file for C code and uses TCC (https://bellard.org/tcc/) to generate the object file, then it reads it into memory like mcode. Perhaps it can give you a quick way to prototype mcode functions. BTW, I couldn't figure out how to use #includes with this... maybe you can?

Write your C function inside a block like this:

Code: Select all

/*_C func
int main(int a) {
	return a+1;
}
*/
Full code is here:

Code: Select all

/*
	Needs TCC: https://download.savannah.gnu.org/releases/tinycc/tcc-0.9.27-win64-bin.zip
	place script in the same folder as libtcc.dll
*/
#singleinstance force

load_cObject(make_cObject("otest.o"), code)

myfunc(x) => DllCall(&code, "Int", x)
/*_C func
int main(int a) {
	return a+1;
}
*/

t := A_TickCount
x := 0
loop 1000000
	x := myfunc(x)
msgbox(A_TickCount - t " : " x)
	
load_cObject(oName, ByRef code) {
	o := FileRead(A_ScriptDir "\" oName, "RAW")
	,tableAdd := NumGet(o, 0x28, "Ptr")
	,toffset := 0x40
	,padd := NumGet(o, tableAdd + toffset +0x18, "UInt")
	,plen := NumGet(o, tableAdd + toffset +0x20, "UInt")
	,VarSetCapacity(code, plen)
	Loop plen//4
		NumPut(NumGet(o, padd + (A_Index - 1)*4, "UInt"), code, (A_Index-1)*4, "UInt")
	return DllCall("VirtualProtect", "Ptr", &code, "UInt", plen, "UInt", 0x40, "Ptr*" , 0)
}	
	
make_cObject(cObjectName) {
	directory := A_ScriptDir
	
	;read c code
	,startLabel := "/*_C" " func"			
	,endLabel   := "*/"
	,script := fileRead(A_ScriptFullPath)
	,cStart := InStr(script, startLabel) + StrLen(startLabel) + 2
	,cEnd   := InStr(script, endLabel, , cStart + 1)
	,cStr   := SubStr(script, cStart, cEnd - cStart)
	
	;LIBTCC calls
	htcclib := DllCall("LoadLibrary", "Str", directory "\libtcc.dll", "Ptr") 
	,Context := DllCall("libtcc\tcc_new", "Ptr")

	;set path to our include/lib folders
	;~ StrPutVar(directory "\lib", libraryPath)
	;~ StrPutVar(directory "\include", includePath)
	;~ StrPutVar(directory "\include", sysincludePath)
	;~ StrPutVar(directory, tcclibPath)
	;~ DllCall("libtcc\tcc_add_library_path", "Ptr", Context, "Str", libraryPath, "Int")
	;~ DllCall("libtcc\tcc_add_include_path", "Ptr", Context, "Str", includePath, "Int")
	;~ DllCall("libtcc\tcc_add_sysinclude_path", "Ptr", Context, "Str", sysincludePath, "Int")
	;~ DllCall("libtcc\tcc_set_lib_path", "Ptr", Context, "Str", tcclibPath)
	
	;StrPutVar("", options)
	;DllCall("libtcc\tcc_set_options", "Ptr", Context, "Str", options)

	;TCC_OUTPUT_MEMORY       := 1 ; output will be run in memory (default)
	;TCC_OUTPUT_EXE          := 2 ; executable file
	;TCC_OUTPUT_DLL          := 3 ; dynamic library
	,TCC_OUTPUT_OBJ          := 4 ; object file
	;TCC_OUTPUT_PREPROCESS   := 5 ; only preprocess (used internally)
	,DllCall("libtcc\tcc_set_output_type", "Ptr", Context, "UInt", TCC_OUTPUT_OBJ, "Int")
	
	,StrPutVar(cStr, _cStr)
	,DllCall("libtcc\tcc_compile_string", "Ptr", Context, "Str", _cStr, "Int")
	
	,StrPutVar(directory "\" cObjectName, filename)
	,DllCall("libtcc\tcc_output_file", "Ptr", Context, "Str", filename, "Int")
	
	;clean up
	,DllCall("libtcc\tcc_delete", "Ptr", Context)
	,DllCall("FreeLibrary", "Ptr", htcclib)
	
	return cObjectName
	StrPutVar(s, ByRef v) => (VarSetCapacity(v, StrPut(s, "cp0")), StrPut(s, &v, "cp0"))
}

Keep up the good work!
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: AHK v2: converting/optimizing scripts

14 Dec 2018, 19:12

oif2003 wrote:
14 Dec 2018, 18:49
Hey, I've been a little busy and haven't had the chance to read all your posts in detail yet.
welcome back :)
Tiny C Compiler
Very pertinent find u come up with! the reason I havent touched C builders for years is gigabytes of bloatware. AHK is a mere godsend here with a sole 500Kb (after UPX compression) file (and w/o any dependancies) required to run scripts for everyday tasks.

too bad, TCC is x86 ONLY

I used some kind of de-bloated VC2010 (85..200Mb) but it would take time to find it back after so many years passed.
I found it. 109MB .7Z
EDIT: link is deleted. not actual any longer - GCC is better
This one can generate optimized x64 code.
Its portable. Kind of. No installation needed.
And its size can be reduced by deleting huge amount of header (windows.h, etc) files. Cuz true m-code should NOT rely on DLL calls. We can regularly call DLLs via AHK, and m-code idea is to process data (searching, replacing for binary buffers and strings).

There was mcode gen.ahk also and a topic https://autohotkey.com/board/topic/5516 ... ode/page-3,
I messed with it before and tried to get it to work. Here is my edited file I was at (sry some russian comments inside). Alas, Its AHK 1.1 :( , would be great to convert it for v2
Spoiler
Ill come back to m-coding later.
Last edited by vvhitevvizard on 24 Dec 2018, 03:47, edited 3 times in total.
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: AHK v2: converting/optimizing scripts

14 Dec 2018, 20:44

back to pretty-printing procedures. U would be surprised:
task: for stringified float: leading/trailing zeros r truncated, and the decimal point appears only if one or more digits follow it

Code: Select all

SmartT(_a){ ;for float numbers, get rid of leading/trailing 0 and .
	a:="" _a
	if(InStr(a,".")){ ;if decimals
		while(SubStr(a,-1,1)="0")
			a:=SubStr(a,1,-1)
		if(SubStr(a,-1,1)=".")
			a:=SubStr(a,1,-1)
	}
	return a
}
;g:	Leading/trailing zeros are truncated, and the decimal point appears only
;	if one or more digits follow it
SmartT_(_a)=>Format("{:g}", _a)

a:="-0000001.55" ; -> -1.55

n:=200*1000
t:=A_TickCount
loop(n)
	s:=SmartT(a)
a1:=A_TickCount-t

t:=A_TickCount
loop(n)
	b:=SmartT_(a)
a2:=A_TickCount-t

msgbox(clipboard:=a1 "|" a2 "`n`n" s)
150 vs 328. My absolutely unoptimized (at first look) procedure is x2 times as fast compared to Format("{:g}", v)
I knew that Format should be slow. But I didn't expect that scratchy function would be faster. Now I found a bug in it - it fails to remove leading zeroes. But thats another story. And motivation to optimize it :D
Last edited by vvhitevvizard on 14 Dec 2018, 21:36, edited 3 times in total.

Return to “Ask for Help (v2)”

Who is online

Users browsing this forum: Draken, Google [Bot] and 39 guests