- NSIS Discussion
- nsArray plug-in
Archive: nsArray plug-in
Afrow UK
7th May 2011 19:46 UTC
nsArray plug-in
Wrote this light weight array plug-in which will be used in the nsL Assembler to support native language constructs for arrays. I figure people will also use it in their NSIS projects too.
http://nsis.sourceforge.net/File:NsArray.zip
- 6KB DLL (versus 20KB for NSISArray)
- Low memory usage
- Linked lists
- Allocates small blocks of memory rather than one continuous block - Much faster
- Uses memory pointers
- E.g. sorting copies memory pointers, not the data itself - Very simple usage
- Bare minimum set of functions - No limit on the number of arrays
- Designed to be used by NSIS Assembler (http://nslassembler.sf.net)
Stu
zeeh3
8th May 2011 02:17 UTC
Very cool, thank you for the new plugin :)
disruptor108
26th May 2011 20:11 UTC
Thanks for making this.
A couple things I've noticed while using it:
*After deleting an array, the length of the array is still 1.
*Removing items from an array do not shorten the length.
I can provide my code examples if necessary.
Afrow UK
26th May 2011 20:24 UTC
Ah thanks I probably forgot to reduce the counters. Will fix soon thanks. I'm thinking of allowing associative arrays (akin to PHP arrays) rather than indexed ones. Think that would be a good idea?
Stu
disruptor108
26th May 2011 21:01 UTC
Personally, I have no preference one way or the other. I've been doing fine with indexes, but if changing to associative arrays works for others I can change my code.
Afrow UK
26th May 2011 21:07 UTC
Well it would work like PHP arrays so you can have both index-associated and key-associated elements in the same array. Perhaps I'll fix these small bugs first though.
Stu
Afrow UK
2nd June 2011 10:37 UTC
Finally fixed those bugs (and a couple more):
1.0.0.1 - 2nd June 2011
* Fixed decrementing of element and array counters on element and array
deletion.
* Fixed memory leak of array name on array deletion.
* Fixed bad pointers remaining after last element and array deletion.
This will be the last version (hopefully) to only support indexed arrays. The next version I plan to have associative and indexed arrays in one, akin to PHP. The changes I had in mind to allow for this are:
* "New" will no longer initialise the values of an array. One can just use "Insert" (will reduce some of the code size).
* "Insert" will take an optional prefix argument for each element in the format "/key=val" where val can be an index or a string. Omitting /key will result in the element being inserted at its natural index (which is the last highest index + 1).
* "Remove" and "Get" will no longer take index ranges, but instead will take a list of keys/indices followed by /end.
Stu
Afrow UK
2nd July 2011 16:42 UTC
New plug-in version which supports associative arrays (as well as indexed). The plug-in usage and functionality has changed quite a lot since the previous version but the DLL is still the same size.
http://nsis.sourceforge.net/File:NsArray.zip
Stu
MSG
2nd July 2011 17:13 UTC
Nice updating spree you got going there, Stu. :)
singman
26th September 2011 16:45 UTC
Error with ToString
If I follow the example on the DLL archive, I get that ouput :
MyArray = {0}
MyArray[0] is 11
MyArray[1] is 14
MyArray[4] is blah
MyArray[5] is /key=jajaj
MyArray[jajaj] is
Set MyArray[jajaj] = 22
MyArray[jajaj] is
MyArray = {0}
MyArray length: 1
MyArray length: 1
MyArray = {0}
MyArray = {1}
MyArray length: 1
MyArray2 values sorted numerically = {1}
MyArray2 sorted by keys = {1}
MyArray2 = {1}
MyArray2[0] is
MyArray2[1] is 14
MyArray2 = {1}
MyArray2 length: 1
MyArray2Keys = {0}
MyArray2 = {0}
MyArray2 length: 7
Completed
I think every ToString call return something wrong.
Quote from documentation :
nsArray::ToString my_array
Pop $var
Returns a string representation of `my_array` which includes keys and
values in the format key1 => value1, key2 => value2, etc.. The error
flag is set if the array does not exist or if the result cannot
completely fit into the output buffer (NSIS_MAX_STRLEN) although as
much as can fit into the output buffer will still be returned.
Afrow UK
29th September 2011 10:15 UTC
Will take a look sometime today.
Stu
Afrow UK
2nd October 2011 10:55 UTC
I have no problems with the example script. Make sure you are using the correct plug-in (i.e. Unicode or ANSI).
Edit: If there is ever a problem, the error flag is set.
Stu
singman
3rd October 2011 13:22 UTC
Great !
I was using the Unicode version but my NSIS is no longer compiled with Unicode.
Thanks.
Afrow UK
12th October 2011 20:01 UTC
New plug-in version:
1.1.0.2 - 12th October 2011
* Added GetAt function.
* Added addition sort flag (16) to retain keys/indices order when sorting values.
* Removed New function. Set function now creates the array if it doesn't already exist.
Stu
Afrow UK
12th February 2012 17:39 UTC
New plug-in version:
1.1.1.0 - 12th February 2012
* Added Split function.
* Added /noempty switch to Join function.
* Sort function now supports ORing (|) for its sort flags.
* Replaced Delete function with Clear function.
* Replace GetAt function with /at=position switch on Get function.
* Added Get /at=-1 for array element iteration.
* Added /at=position and /val=value switches for Remove function.
* Implemented script header replacements for Copy, CopyKeys and ToString functions.
* Added script header ForEachIn LogicLib loop.
The ForEachIn loop can be used like so:
${ForEachIn} my_array $key $value
DetailPrint "my_array[$key] => $value"
${Next}
Stu
Afrow UK
12th February 2012 20:04 UTC
New plug-in version:
1.1.1.1 - 12th February 2012
* Optimised Get /at=-1 by using an pointer instead of a counter.
* Added Get /at=-2 to iterate in reverse order.
* Added script header ForEachInReverse LogicLib loop.
Stu
Afrow UK
13th February 2012 10:44 UTC
New plug-in version (hopefully the last for a while):
1.1.1.2 - 13th February 2012
* Replaced /at=-1 with /next for Get function.
* Replaced /at=-2 with /prev for Get function.
* Added /reset switch to Get function to reset /next or /prev iteration.
* The /at=position switch for Get and Remove functions now support negative numbers.
* Added call to Get /reset before entering ForEachIn loop.
* Fixed incorrect usage of Split function in the readme.
Stu
vicokoby
6th March 2012 00:09 UTC
When set a array outside of section, this init with some empty elements
For example:
Page custom CreateMyPage LeaveMyPage
Function CreateMyPage
InstallOptions
FunctionEnd
Function LeaveMyPage
FunctionEnd
vicokoby
6th March 2012 00:32 UTC
#When set a array outside any section, this aray have some empty elements,
por example in the function of a custom page.#
Page custom MyPage LeaveMyPage
Page instfiles
Function MyPage
Push $R0
InstallOptions::dialog $PLUGINSDIR\someIni.ini
Pop $R0
FunctionEnd
Function LeaveMyPage
nsArray::Set MyArray 'one'
nsArray::Set MyArray 'two'
nsArray::Set MyArray 'three'
nsArray::Length MyArray
Pop $1 ; $1 = 4 instead 3
NsArray::Get MyArray 0
Pop $0 ; $0 = ´´ ( Empty String)
#This only occur into a custom page function#
Function
Section MySection
Call LeaveMyPage ; When the call is from here, no there problems with the array
SectionEnd
Afrow UK
6th March 2012 10:45 UTC
nsArray::Set takes multiple values so you need to put /end on the end, i.e.
nsArray::Set MyArray one two three /end
If you don't add /end, values left on the NSIS stack will get added.
Edit: Maybe I should add a first argument of /list to add multiple elements in a single call to Set (i.e. /end can be omitted without /list).
Stu
vicokoby
6th March 2012 14:40 UTC
I must /end when I put a single elment?
eg.: nsArray::Set MyArray ´Single´ /end
Afrow UK
6th March 2012 17:53 UTC
At the moment yes because of what I said. NSIS passes arguments to plug-ins using the stack and because Set can have multiple arguments there is no way of knowing if the argument was on the stack already or passed as a plug-in argument in the call. This is why /end is necessary and not optional. Some other plug-ins require the use of /end too.
Stu
Afrow UK
20th March 2012 23:23 UTC
New version uploaded. Set and Remove calls no longer need the /end suffix when adding/removing a single element. If you want to specify multiple elements, start the list with /list and end it with /end. The readme has examples.
http://nsis.sourceforge.net/File:NsArray.zip
Stu
Yathosho
2nd June 2012 13:50 UTC
feature requests:
-unique (removes duplicate values)
-merge (two arrays)
Afrow UK
2nd June 2012 17:06 UTC
You can do both of those with the existing functions plus some loops. Perhaps I will add them to the script header when I have time.
Stu
Yathosho
2nd June 2012 17:36 UTC
just a suggestion, after all the same could be said about sorting
Afrow UK
2nd June 2012 21:52 UTC
Actually you could only do sorting with the existing functions if the array is indexed only (i.e. how would you sort if the array is associative?) Also the internal Sort function simply moves the pointers around rather than the actual data. This is far more efficient and does not require any new memory allocation or deallocation or data copying. Also writing the quick sort algorithm in NSIS code would not be very straight forward (bubble sort would be easier but is less efficient).
Removing duplicates and merging arrays both require memory deallocation and allocation so writing an internal function would serve little benefit over an NSIS script equivalent and would only add additional size to the DLL.
Stu
TVNST
22nd August 2012 14:11 UTC
I checked the plugin code in order to maybe get a hint about what was causing the memory corruption problems I got, and there's some potential issue with the Set command and its key words.
According to my interpretation of the code, there's no accurate distinguishing between a value and a key word. Therefore the function is not able to recognize if a string value accidently has the same text as a known keyword like "/list", "/end" or "/key".
For example, if just one value is added and this value accidently starts with "/key=", it will set the error flag and abort the installation in my case.
If a list of parameters is added and one of the values is accidently "/end", like...
nsArray::Set array /list "Bla" "/end" "BlaBla" /end
... the last two parameters are not interpreted anymore, and might pollute the NSIS stack, because there's not condition for "/end" to be the last available parameter.
It would be better to have different function names for each type since every keyword reduces the set of valid values.
Since I have to add values read from various registry values, there's pretty much any value possible including the key words.
I will now check the values to add for the keywords first and use a workaround it that happens. Fore example, it's possible to add the "/list" value with the following statement:
nsArray::Set array /list "/list" /end
Afrow UK
28th August 2012 19:31 UTC
Yes that is indeed a problem which I was well aware of when I wrote the functions. I suppose a SetList function would not hurt and I am perhaps over-obsessed with making the plug-in as small as possible.
I have run both the ANSI and Unicode builds through the debugger and have had no memory access violations. There could still be some but I would have a look at the other plug-ins you use.
Stu
TVNST
29th August 2012 13:49 UTC
I checked the basic functions of the plugin and the code is rather clean.
I highly doubt the memory corruptions are caused by your plugin as well.
I also validated our own plugin code, but althouh I found memeory leaks but I couldn't find any memory corruptions.
However, I replaced some unsave functions like sprintf with their safer variant with array size limitation.
However, our installer still has some sporadic stability problems which might be caused by another plugin.
We also upgraded to the newest NSIS version but that didn't help either.
Afrow UK
3rd September 2012 22:07 UTC
New version:
1.1.1.4 - 3rd September 2012
* Replaced Set /list with SetList.
* Replaced Remove /list with RemoveList.
Please update your code accordingly if you switch to the new version.
Stu
T.Slappy
12th December 2012 07:23 UTC
What is correct usage of nsArray::Get? I think nsArray corrupts my stack but I cannot find the problem.
This is my code - Is this usage correct?
1)
ClearErrors
nsArray
::Get UrlTree $CompleteGroup
>${If} ${Errors}
${DebugMsg} "UrlTree($CompleteGroup) is not set!"
>${Else}
Pop $CompleteTotal
; There are some files
>${EndIf}
In documentation I saw this usage:
2)
ClearErrors
nsArray
::Get MyArray 2
Pop $R0
${If} ${Errors}
DetailPrint `Error!`
${EndIf}
So in my example (1): I do POP only if ${Errors} is fine.
But the docs say (2): do the POP, then check ${Errors}.
I have many values in stack, so I think POP in (2) will remove element from stack even in case the nsArray does not contain required element.
Is this assumption correct?
Afrow UK
12th December 2012 13:12 UTC
Yes that is an error in the example script. It will not push anything onto the stack if an error occurs (element not found).
Stu
Fretje
28th January 2013 15:33 UTC
Is there a way to sort an array with version numbers using the versioncompare logic? I'm using "numeric" now, and it doesn't give the right result:
* 4.2.6_32
* 5.1.10.2_32
* 5.1.8.2_32
* 5.1.9.0_32
should be:
* 4.2.6_32
* 5.1.8.2_32
* 5.1.9.0_32
* 5.1.10.2_32
fretje
Afrow UK
28th January 2013 19:13 UTC
See attached.
Stu
Fretje
29th January 2013 10:46 UTC
Thank you very much for the very fast reply, Stu.
This is exactly what I needed!
Yathosho
30th January 2013 12:11 UTC
btw, the readme still mentions the old syntax for nsArray_ToString
Marshallx7
9th May 2013 15:23 UTC
Couldn't find this documented anywhere and I think it is worth mentioning:
/at= does not work with the Set command.
Further more /key= only works on keynames, not indexes, as follows:
nsArray::Set "myArray" "0"
nsArray::Set "myArray" "1"
nsArray::Set "myArray" "2"
nsArray::Set "myArray" "3"
;array now = {"0", "1", "2", "3"}
nsArray::Remove "myArray" /at=1
;array now = {"0", "2", "3"}
nsArray::Set "myArray" /key=2 "three"
;array now = {"0", "three", "3"}
Because elements with no specified keyname are presumably given a keyNAME of their initial index.
The above is
correct behaviour as you might have keynames that consist only of numbers.
If you want to have /at= functionality with the Set command then you can add it yourself using the following macro
!macro ArraySetAt arrayname index value
Push $R0
ClearErrors
nsArray::Get "${arrayname}" "/at=${index}"
${IfNot} ${Errors}
Pop $R0 ;key
Exch $R0 ;discard value
Pop $R0 ;discard value
nsArray::Set "${arrayname}" "/key=$R0" "${value}"
${EndIf}
Pop $R0
!macroend
Afrow UK
11th May 2013 17:56 UTC
Originally posted by Marshallx7
/at= does not work with the Set command.
That is correct - the readme does not show any uses of /at= with the Set instruction, but I could add it.
Originally posted by Marshallx7
Further more /key= only works on keynames, not indexes
This is not true. Your array after the Remove call is:
myArray = {0 => 0, 2 => 2, 3 => 3}
After your Set call, it becomes:
myArray = {0 => 0, 2 => three, 3 => 3}
There is no distinction between numeric keys and string keys. It is just that numeric keys are assigned when no key is specified when adding elements.
Edit: You probably assumed that removing element with index 1 would result in the indexes after it being decreased by 1 also. This does not happen in hashed arrays because then the association is lost. I suppose the plug-in needs a reindex function. For example in PHP you'd use array_values().
Stu
Marshallx7
12th May 2013 10:36 UTC
Sorry I wasn't clear before, yes - what you say the arrays would be is what I concur with, I just didn't mention the keynames.
I fully accept that this is intended behaviour, and I did run my own tests to be sure, because I need Set /at functionality.
To make my position clear to anyone reading, there are NO bugs in nsArray (that I can see) - just Set /at functionality is not (presently) a feature (but you can emulate it using my macro above).
A reindex method might be useful, but I imagine it is more resource intensive than a Set /at command. Using Get /at, Remove /at and Set /at would mean a reindex is not necessary, because you can then choose to either use associative keynames (without /at) or positional indexes (with /at)