last update 10/2022
let's go nuts!
12th Oct 2022 - draft version , work in progress
Gonuts is a runtime library, compiler and interpreter utilizing and extending the Squirrel Language, especially for creating client/server applications, IoT or industry data harvesting and processing while offering source code protection, encryption, events, multithreading and lots more.
It is statically linked and not depending on any external library and runs on (almost) any Linux or Windows platform with a very small footprint.
It comes with an optimized internal memory management and has a very small RAM (and disk) footprint. It was successfully used on standard Distributions like Windows 7,10 and 11, Debian Linux, Ubuntu and even on ARM Hardware like Raspberry Pi, BeagleBone or even on commercial ARM based LTE-Router/Gateway hardware.
We tested and used Gonuts on
As it's statically linked, the currently provided AMD64 (compiled on Debian), Win32 (compiled on Win7) and ARM64 (Raspbian) versions probably run on any similar sytems.
Sorry, we didn't come round to fix compilation for Win64 yet, seems to be quite an issue.
Historically all started with a protocol we made, among others, for Teamspeak; and which probably now can be found working behind the scenes of MyTeamspeak on both clients and server side to securely synchronize user settings, bookmarks and identities.
A more updated and generalized protocol was then named PXP (Phobyx eXtended Protocol) and was used internally on quite some projects. From some projects of our former game developer era we knew Squirrel and merged them, adding some more features to get standard tasks done more rapidly. PXLib was born, also known as libPXP (the library to implement PXP using Squirrel).
PXLib grew over the years, and more and more features were added on demand to serve for example in a huge public traffic telemetry project, image/video kiosk systems, an embedded http server simulator and integrated templating engine or a complex chemistry recipe and regulatory affairs software, to name a few. Dependency on C/C+ coding was reduced with each step and more and more control went to Squirrel while we rolled out for more and more hardware platforms and projects, until PXLib finally made it into a standalone application that now would enable us to re-develop all our servers and clients we made so far on a 5 to 50% schedule compared to the original projects. PXLib was finally renamed to “go nuts", regarding the Squirrel source code file extension being "nut".
Today we are happily throwing nuts and pnuts at "gonuts" to feed our squirrels and provide them a den (module) every now and then and one of our largest projects with over 150.000 lines of gonuts code (and exactly 0 lines of c++) is a ERP system, including bookkeeping export interfaces, sales platform (like Amazon, Ebay...) interfaces and lots more. As you read these lines, apparently we published gonuts as a general development and production tool.
At the time of this writing, the main approach is M2M/IoT and we’re currently introducing CAN support among others, but we also occasionally work on OpenGL(ES) and multi-platform GUI bindings and plan for an IDE named “Drey” (a drey is a tree squirrel/flying squirrel nest). Our focus, however, currently is on small embedded systems and the Linux server side. However, gonuts is flexible enough to be considered a somewhat general purpose development platform now.
First and most of all, access to keyboard input unfortunately is quite limited. There is no getch() or something, In Squirrel you only can open use stdin, stdout and stderr as files (see squirrel standard I/O library), which usually are line driven!
On linux, though, you can use the event system to access keyboard (using sockfd class on tty files for example), but on Windows you simply can't do that. There is a den, however, to SIMULATE keyboard strokes programmatically, which in turn works für Windows only. Funny, isn't it?
Well, we'll work on that some day, but as keyboard input is handled so differently on different platforms, it'll take a while.
Another caveat is about Squirrel ENUMs, but this is nothing unusual for Squirrel - enums are handled by the compiler an hence work within the same source file only.
See Script management functions about for a proposed workaround using the preprocessor feature provided by pxlib.pnut
gonuts is really stable and we're using it in production for years now, but it has some features that are explicitly documented as "use at your own risk", like destructor support for example. Sometimes normal program termination will stall when using RealThreads, but it happened on very, very complex and huge applications only - we're still digging into that. Also in combination with realthreads we also observed memory leaks in rare cases, though very few and short blocks or memory. However, it IS possible to produce memory leaks.
Also gonuts CAN crash when some den (or even gonuts native) c-functions require a dynamic stack resize.
Gonuts is designed for Linux. Few functionality is unavailable on Windows, this is explicitly documented. Additionally, Database access is currently limited to sqlite3 on Windows (unless someone creates a proper Den Module for that, we didn't come round yet).
Also, your code is not magically forced to be platform independent. Beginning with line endings (windows: \r\n Linux:\n). Use the ISWINDOWS constant to check what platform you're running on.
Integer and floats may be 32 or 64 bits wide.
Well, UTF-8 is supported, but wide characters and Unicode are not. You have to work around when dealing with those. A simple comparison like if (inputstring=="Unmöglich♠")
- note the Ggerman special character ö and the pike symbol - may or may not work depending on underlying OS, settings and source file character set, but it will fail 100% when the string has a different character set compared to your source file editor or dealing with wide characters. The source files are expected to be UTF-8, iso-8859, ASCII-128, ASCII-256 or WINDOWS-1252 or anything like that bound to standard characters being single bytes.
Debugging is not thread-aware when it comes to RealThreads. Breakpoints do work very well, but you never know which thread you're actually in unless looking at the base of the call history. When stepping into or over, you may suddenly find yourself in a totally different thread and source. Hence we now have the habit of using breakpoints a lot.
Breaking sometimes can take a lot of time when a lot of local and stack data is to be transferred to your debugging client, especially remotely. Breking into a loop processing an SQL-request that returned 50.000 lines with 500 columns each tends to make you go fetch some coffee.
Large tables and arrays get truncated to a few thousands entries (which is indicated), but when their elements again hold large arrays/tables, or some ring references occur, it can be really tedious - or even impossible (network timeout).
Also you can not alter memory/data/variables manually, or use conditional breakpoints.
The call stack is not complete when a function was invoked by the event management.
Also, when an event throws an exception while debugging you get into it, but in production mode (non-debugging) you need to know it won't stop execution of the thread, it will return to the loop.dispatch despite any error. (However, you can see this while debugging, too, it's just a special behaviour to be aware of)
Debugging requires squirrels special debugging interface and protocol, so you need to have an IDE or client supporting that. To our knowledge, there's currently only a VisualStudio 2008 edition and VisualStudio2015 plugin plus a dedicated, but way less featured small opensource project named nutcracker.
There's no JIT compiler available yet for Squirrel, hence also no JIT for Gonuts. Everything is executing bytecode in the end, so you may face performance issues when doing really "heavy things". So it's no good advice to build a render loop for 3d games in Gonuts. However, you can work around using c++ by creating your own Den module. Just note: Gonuts usually is "in control", you can reverse that of course and just load your module, start a function that only returns for application exit, and use sq_compile and sq_call to execute gonuts stuff in reverse.
On linux, very few dependencies exist (glib6):
libpthread.so.0 (libpthread) librt.so.1 (librt) libdl.so.2 (libdl) libstdc++.so.6 (libstdc++6) libm.so.6 (libm6) libgcc_s.so.1 (libgcc6) libc.so.6 (libc6)
These should be present on roughly every linux, as they're simply the very, very basics.
There are none we're aware of
gonuts (or gonuts.exe) is a command line (win: console) application.
All versions except amalgamations support control via command line parameters described below. All command line parameters usually are also passed to the Squirrel app (unless specified otherwise using the -a
option, see below).
gonuts [inputfilename] [options and/or excess parameters]
where inputfilename
is a name of the .nut or .cnut file to load and execute. Note that you can omit the file extension, gonuts will try both .nut and .cnut automatically in all cases (passing the extension only changes the order it will try to load). inputfilename
can optionally have a path (relative or absolute). Unless a namespace name is provided using the -n
option, the namespace the file will be loaded to is the raw filename body, without any extension or prepending path.
When no input filename is provided "main" will be used in the current working directory (--> ./main.nut or ./main.cnut)
Option | Description |
---|---|
-SQDBG |
Optional. Run in remote debugging mode. Wait for a debugger to connect before starting the app. |
-n yourname |
Optional. Set yourname as the namespace table to load the code file to |
-p pnutfile |
Optional. Add a pnut archive to the gonuts include paths. you may repeat this option to add more than one pnut archive. The order of multiple archives is obeyed when searching them for nuts lateron. pnutfile must be the full filename including extension (.pnut) and include any relative or absolute path required to access it. |
-a |
Optional. Argument separator. When not present, all arguments will be passed to the Squirrel app. When present, only arguments from this point on will be passed (including the -a , which accordingly will replace the executable filename usually being the very first argument). |
--namespace yourname |
synonym for -n |
--pnut pnutfile |
synonym for -p |
note that when running an amalgamation (the executable contains script resources) is run, these still apply except the startup script and -namespace.
The exit status will be the value function main()
returns, provided it is integer. Otherwise (main does not return an integer or is not present at all), gonuts will exit with status 0.
If an error occurs preventing execution though, gonuts exit status will be -1
gonuts supports calling exit(intval); to prematurely exit with the given integer exit status. However, note that when using RealThreads that usually is not a good approach because all threads will be immediately stopped and killed
Default filename extensions:
extension | file type |
---|---|
.nut | Text script files, plain text |
.cnut | Pre-compiled (byte code) script files, raw, optionally compressed and/or encrypted |
.pnut | PNUT script archives (collections of scripts), where each file inside the archive can be either plain or byte code and in both cases optionally compressed and/or encrypted |
Wether a script is byte code, compressed or encrypted is detected using Headers within the file.
Note: Though Squirrel supports UTF-8 (just another header), extended gonuts functionality usually does NOT.
When explicitly using a filename extension, you're able to use that, so it's possible to load a script with filename "foo.bar" actually. This comes in handy for example when using the preprocessor that comes with pxplib.pnut, so you can use files extensions like .hnut, .inut or .inc
See http://www.squirrel-lang.org for documentation. Currently gonuts is housing Squirrel 3.1 - but we're moving to 3.2 very soon, which comes with some nice syntactic sugar
The Squirrel standard library is fully supported and already registered in the root table.
gonuts introduces additional compiler keywords for comfort reasons:
Keyword | Effect |
---|---|
var |
synonym for local |
auto |
synonym for local |
nil |
synonym for null |
`___FREEZE___ | will stop compiler to do line counting. See script.compilehook |
`___MELT___ | compiler will continue linecounting |
protected |
synonym for function but makes the function locked against serialization (writeclosure) |
In gonuts, scripts usually are loaded using script.require (see script.require, script.release and script.shrink); users are discouraged to use the Squirrel standard lib functions dofile or loadfile or the “include” pre-compile instruction unless they need to achieve certain special outcomes.
Gonuts uses namespaces. Namespaces are tables (always created in the root table) where the script gets executed upon loading. All script elements (functions, classes, objects) are placed into that table. The table name (its index in the root table) by default is defined by the file name without path or extension. Note: Loading a file “somepath/test.nut” while a file “test.cnut” was loaded before will result in the same namespace, overwriting the script loaded first. Hence all filenames should be unique, disregarding their extensions or subfolders they’re in.
Elements of scripts can always be accessed using double colons like “::namespace.element”.
Note that loading mechanisms implemented by the Squirrel language or standard library (loadfile and dofile) do not follow any rules, they directly load a script. Also note that the “include” directive literally includes plain text sourcefiles, which in turn become part of the compiled script while the include instruction gets REPLACED by the included source text. Hence include is to be considered a “pre-compile instruction” not following any additional rules. It is recommended to name included source texts “.inut”.
Technically spoken, gonuts loads scripts by creating (or replacing!) a namespace table in the root table and immediately executing the script with the namespace table as the “this” object.
After initial script execution(!) ends, depending on the way the script was loaded (normal via e.g. script.require or via RealThread) a function named either “Main” or "threadmain" gets executed if it exists in the namespace table.
Hence, users are advised to NOT create infinite loops or event loops directly on script level – a script MUST return when executed (usually by end of the script file), while its “Main” or "threadmain" is not required to return. (Having a “Main” or “threadmain” function at all in a script also is not mandatory except for real threads).
After loading a script to its namespace and before executing any “Main” or “threadmain” function, additional objects are placed into the namespace table when loaded using commandline or script.require:
Index | Object |
---|---|
filename | string containing the script filename (note: not necessary to actually EXIST!) |
namespace | a string representing the namespace name (for backward access or comparisons) |
scriptregister | The string the loaded script got registered with and to pass to script.release |
A note on multithreading: Real Threads (“real”, because the single term “thread” is used for a different concept in Squirrel) are separated VMS. They do not share a root table or any Squirrel object directly and have their own separated state also regarding loaded scripts. RThreads, however, share the loading rules, like paths and archives to apply while loading.
When loading a file either way supporting paths, these rules are used to load a script.
PX will iterate all registered loading paths, and then try to find either provided,“.nut” or “.cnut” file matching any provided subfolder path by concatenation. When no such file could be loaded, gonuts will try all registered archives before continuing with the next loading path.
Loading paths are iterated in their registered order (while the current working directory is used as a default when no paths were configured using script.Paths), the same applies to archives (registered by script.AddArchive). The outer loop is for loading paths, the inner for archives.
You can change the default priority of "files before archives" globally for all threads using the script.Rule
command. Amalgamations swap the order automatically to "archives over files" and any attempt of reconfiguration will be simply ignored.
In case the provided filename already has a file extension, that very filename will be tried first, then ".nut" and finally ".cnut".
Note pnut archives ignore file extensions and accessing them always strips any file extension.
**Example: **
script.Paths(“”,”subfolder/”,”/home/user/”) ; script.AddArchive(“./my.pnut”,”/var/pxlib.pnut”) ; script.require(“somepath/somescript.myext”) ;
will try to find and load the first script found in the follwing orders, depending on global script.Rule
configuration:
After script.Rule("priopnut",0)
(or unconfigured) and not running an amalgamation:
file ./somepath/somescript.myext
file ./somepath/somescript.nut
file ./somepath/somescript.cnut
somepath/somescript in ./my.pnut
somepath/somescript in /var/pxlib.pnut
file ./subfolder/somepath/somescript.myext
file ./subfolder/somepath/somescript.nut
file ./subfolder/somepath/somescript.cnut
subfolder/somepath/somescript in ./my.pnut
subfolder/somepath/somescript in /var/pxlib.pnut
file /home/user/somepath/somescript.myext
file /home/user/somepath/somescript.nut
file /home/user/somepath/somescript.cnut
/home/user/somepath/somescript in ./my.pnut
/home/user/somepath/somescript in /var/pxlib.pnut
After script.Rule("priopnut",1)
or when running an amalgamation:
somepath/somescript in ./my.pnut
somepath/somescript in /var/pxlib.pnut
file ./somepath/somescript.myext
file ./somepath/somescript.nut
file ./somepath/somescript.cnut
subfolder/somepath/somescript in ./my.pnut
subfolder/somepath/somescript in /var/pxlib.pnut
file ./subfolder/somepath/somescript.myext
file ./subfolder/somepath/somescript.nut
file ./subfolder/somepath/somescript.cnut
/home/user/somepath/somescript in ./my.pnut
/home/user/somepath/somescript in /var/pxlib.pnut
file /home/user/somepath/somescript.myext
file /home/user/somepath/somescript.nut
file /home/user/somepath/somescript.cnut
Note: it does not matter whether a .nut or .cnut actually contains plain source or precompiled code, or if it is compressed and/or encrypted.
Loading Paths can be set using script.Paths(array)
and retrieved by local array=script.Paths() ;
To add a path, for example: { local p=script.Paths. p.append(“somepath/”) ; script.Paths(p) ; }
Archives can be added by using script.AddArchive(string) ;
where string is a full or relative path, filename and extension(mandatory!). Despite the methods name, an array of archives can be retrieved by calling script.AddArchive() ;
Note: Unlike for paths, you can not alter the archive list, but only append single archives!
Where paths are used, they usually can be relative or absolute. When a path without a filename is used, it should (and sometimes must) end with a trailing slash. When it comes to script and pnut archives, path format (slash or backslash) will be converted according to the platform. Users should always use slashes for such paths.
However, using absolute paths always binds your app to the platform, because device names are handled differently by the OS (e.g. "/tmp/" vs "C:/tmp/" cannot be converted automatically).
This table shows the ways of loading scripts and the supported loading features:
Mechanism | paths | from file | from pnut | source | bytecode | compressed | crypted | refs ("require") | function main |
---|---|---|---|---|---|---|---|---|---|
Commandline | yes | yes | yes | yes | yes | yes | yes | yes | yes |
script.require | yes | yes | yes | yes | yes | yes | yes | yes | no |
script.run | yes | yes | yes | yes | yes | yes | yes | yes | yes |
script.getsource | yes | yes | yes | yes | no | yes | yes | no | no |
RealThread | yes | yes | yes | yes | yes | yes | yes | in new thread | threadmain |
::pxp.execfile | yes | yes | yes | yes | yes | yes | yes | no | yes |
::pxp.loadfile | yes | yes | yes | yes | yes | license dep. | license dep. | ** no** | no |
loadfile | no | yes | no | yes | yes | no | no | no | no |
dofile | no | yes | no | yes | yes | no | no | no | no |
#include (*1) | yes | yes | yes | yes | no | yes | yes | no | no |
(*1): not natively supported, but possible using preprocessor features of pxplib.pnut, see script.compilehook
Compiling happens when loading plain text source files (also compressed and/or encrypted ones for example from pnut archives) or when explicitly compiling strings to functions.
Compile errors won’t stop overall execution and will result in the script simply be neither loaded nor executed in all cases. However, logs / stderr output will output the first compile error encountered and the instruction used to try to load will return a proper value indicating loading failed. In debugging mode, the debugger will jump to the compile error, however any "run", "step", "stepinto" command or the like will refer to the instruction trying to load and/or compile, not to the line the debugger thinks the break occured at.
Note: When using libpxp.pnut preprocessor feature, an #include will inject the included source into the current source and the compiler will always throw any error on the #include instruction when the included file is erroneous!
Arguments from the command line invocation are present to all threads in the _argv and _argc elements of the root table, the first being an array, the latter an integer (though redundant due to len() method of arrays). However, note that _argv[0] is not necessarily the gonuts invocation binary, it can also be “-a” indicating preceding commandline parameters were suppressed.
Besides the standard squirrel constants and globals, gonuts provides these additions:
::_argv
see Arguments
::_argc
see Arguments
::ISWINDOWS
this constant is 1 when running on Windows, otherwise 0 (for Linux). Use to avoid accessing objects not available on the current platform
::_GONUTSREV_
integer gonuts revision (internal version number)
These are objects available in gonuts providing additional features to Squirrel. Unless otherwise specified, they're registered in any VMs root table, also across RThreads.
Object and function names grew historically, and were kept for backwards compatibility though naming unfortunately is not consistent across all features.
Please note
As in a programming language things can get really complex, not all combinations and cases were thoroughly tested. In case you experience a crash of gonuts related to the use of gonuts' classes and functions (except when you used the WITH_DTORS
class), please post a bug report to the proper board forum at http://forum.goinguts.net
Please make sure you were using the latest version of gonuts before doing so, and attach a code snipped and short explanation on how to reproduce the crash. In case too much code is required please ask for advice or try to user other means of providing source code (pastebin or hastebin for example)
The followimg functions are registered in a table "script" residing in the root table. As it is a standard table, it is possible to override or delete them (highly NOT recommended)
ATTENTION: There is a general gonuts-specific caveat: Any ENUMs are available within the same source only. Scripts loading other scripts containing enums will not have access to the enums, because they're processed by compiler and not available at runtime. Recommendation: enums that need to be shared between scripts should be in separate files and included using the preprocessor feature of pxlib.pnut. Similar applies to const declarations, however, consts are in fact available at runtime (at some slight performance costs).
Example:
at startup:
script.addarchive("pxlib.pnut"); if (script.require("pxp")) ::__px.prep.enable();
in scripts required or run afterwards, include your enums with
#include "myenums.nut"
(note the # must be the very first character of the line and the included file must be source code, no cnuts will work regarding enums)
function require(spec[,filename])
Requires a script to be present, and if it is not present it will be loaded.
Also provides increase of reference count for the specified namespace (see how namespace is derived below).
Will not run any function 'main' or 'threadmain' in the script (use script.Run
for that)
INPUTS:
spec
string specifying a namespace or a filename to derive a namespace from, depending onfilename
. In both casesspec
is what the script reference count will be accounted for
filename
string, optional. A filename to load the script from, when omittedspec
is treated as filename and the namespace is derived from it. When present,spec
specifies the namespace directly 'as is'.
RETURNS: bool. true if the script was sucessfully loaded, false if an error occured.
THROWS when a license violation occurs or the script was precompiled for a different architecture (e.g. 64bit vs 32bit)
When two parameters are used, the first specifies the exact namespace to load the script to and the second the filename, otherwise the only parameter specifies both at once, deriving the namespace from the filename by removing any filename extension or prepending path. The namespace will be the part of the string between its beginning or the last occurence of either backslash or slash, and the end of the string or the last occurence of a dot.
Filename handling:
In both cases (either one or two parameters passed) the filename can additionally hold a path in any notation(win or linux), and either relative or absolute - which is not recommended; if possible you should prefer using script.AddPath
Any filename extension is used to identify the very first attempt to load the exact file and then removed for further tries of different extensions.
Also see script Namespaces and script loading and script loading rules in the introductory part of this document.
Namespace handling
In both cases (either one or two parameters), the resulting or specified namespace is taken literally for reference counting. The exact same string must be used for other script-related methods to work (see script.release
, script.Run
, script.shrink
). If the string contains a prepending path of any kind this will be INCLUDED, but any filename extension will be EXCLUDED from identifying the reference counter.
In case the reference count is zero, the script will be loaded and placed into a slot in the roottable named after the namespace. This namespace table in the root table is set hard on loading, deleting any table already existing with this name! If the reference count is other than 0, however, no action is taken, be the table still present or not! (You can, but are not recommended to "fiddle around" with namespace tables. You should know what you're doing if so...)
Long story short, for most users it will simply work as expected, as long as they re-use the exact same string for all script-Functions in this section.
Namespaces should not (but in fact can) contain any special characters and are taken literally. Namespaces won't iterate and always reside in the root table, the common indexing syntax like table.subtable1.subtable2
won't do, instead it will create something along "getroottable()["table.subtable1.subtable2"]<-{}
".
Examples, providing myfile.nut exists and contains a function named somefunc
:
if (script.require("somepath/myfile.nut")) { myfile.somefunc() ; }
automatically derives namespace from filename, while the following will provide a custom namespace and execute somefunc from it:
if (script.require("myCustomNamespace","somepath/myfile.nut")) { myCustomNamespace.somefunc() ; }
However, no path iteration is provided, so this variant have a probably unexpected result:
if (script.require("some.weird.namespace","myfile.nut)) { //some.weird.namespace.somefunc() ; //this would fail getroottable()["some.weird.namespace"].somefunc() ; //but this way it does the job }
function script.release(asindicated_on_require)
Reduces the reference count for a script (see script.require
) and when the reference count drops to zero, remove the script from memory and drop the whole namespace table. Note this means deletion of any slot within the namespace of the script created in the meantime also.
INPUTS:
asindicated_on_require
string to identify the script, as described above (seescript.require
, namespace handling).
RETURNS: bool indicating wether the script got released or is still present
THROWS: no
function script.run(spec[,filename])
will load and reference count a script, then call its "function main"
In case the script already is loaded and script.shrink
was not yet called for it, it will delete and re-create its namespace table. In case the script was shrinked in the meantime tohugh, only function 'main' will be called but no re-creation of classes, globals or functions is performed.
See script.require
for parameters. Hence can be used to either re-run a script already loaded, or to load and execute a script.
function script.shrink(spec)
INPUTS:
spec
the identifier specifying the script, as descripted forscript.require
RETURNS: bool. true when re-run was executed successfully, otherwise false
THROWS: when spec is missing or no string
As some data needed to re-run (re-creating the namespace, classes and so on) an already loaded script is obsolete in many cases, script.shrink
will free such allocated memory script.Run would need. Note, however, it's not about the source code, which is discarded at loading time already. So shrinking may be applied to scripts loadad from bytecode, too.
function script.paths([newpaths])
Call without parameters to get an array of include paths used by script.require or other script loading mechanisms as described in section script loading rules.
INPUTS:
newpaths
array, optional. Set all paths according to array content. Omit for inquiry
RETURNS: an array of current paths
THROWS: when array contains other objects than strings
When newpaths
is provided it sets all paths at once accordingly. The array contains strings, each elemenet defining a path. The order of the elements is the order for loading attempts.
Paths may be relative to the working directory or absolute, and MUST be provided with a trailing slash. (Note in gonuts, all script paths should be provided using slashes, not backslashes. However, gonuts will convert slashes and backslashes to the required platform format)
Important note: script paths are not local to threads – all threads share the same set of paths, and altering the path array in one thread applies to all other threads, too. Beware of race conditions.
newpaths
may be omitted to perform an inquiry only.
script.addarchive([pnutfile]
Adds a pnut archive to search in when loading scripts. Note you can add an archive only, never remove.
INPUTS:
pnutfile
string, optional. path and filename of pnut archive
RETURNS: in casepnutfile
is provided:true
when successfully added,false
when archive file does not exist. When nopnutfile
provided returns an array of active pnut file locations
THROWS: on input parameter error
file locations may contain absolute or relative paths
Important note: Archives are not local to threads – all threads share the same set of archives, and adding an archive in one thread applies to all other threads, too. As archives can only be added there are no possible race conditions.
script.rule(command,parameter)
control overall behaviour, configure or lock global rules
INPUTS:
command
: string, see table below
parameter
: see table below
RETURNS: none
THROWS: exception on any error! (including unknown command or when rules are already locked)
command | parameter | effect |
---|---|---|
lock | ignored | further attempts to call script.rule will throw an exception |
priopnuts | bool | true = change loading order to "pnuts first, then files" |
By default, this "slot" is not present. If present, gonuts expect it to be a function taking a string argument with source code that is about to compile and returns either null (use unchanged) or a string, buffer, blob or precompiled closure that is taken over.
Prototype is
function myhook(input) { local retval=input ; //string. retval can be string, null, blob or buffer or a precompiled closure created by compilestring()(). Note it is important to EXECUTE the returned function of compilestring() ro return a closure and not a function! return retval; //returning null equals "return input", but saves memory. }
and the "this" object will be the root table (so the function is recommended to bindenv()), example:
function myhook(input) { return null ; //do stuff here when needed } script.compilehook<-myhook.bindenv(this)>
gonuts' PXP library uses this to implement a preprocessor with macros, macro conditions and include functionality. To use the pxp library preprocessor:
if (script.require("pxp")) ::__px.prep.enable() ;
Note this hook is called by script.require and script.run only, but not for dofile(...) or compilestring(...) for example.
Within the function, make sure to keep the line counter (for debugging) matching the original input sourcecode. You may stop line counting by emitting ___FREEZE___; in the output and continue line counting by emitting ___MELT___; (three underscores each side) in the return string to compile. BTW: Those keywords work in "normal, nonaltered" sourcecode, too, but aren't recommended in normal use.
function script.getsource(filename)
get the source code of a source file
INPUTS
filename
: string, same rules apply like with script.run or script.require
RETURNS: string sourcecode or false. When unable to fetch source code (e.g.file not found or file is precompiled bytecode), false will be returned
THROWS: no
function script.cwd()
get the current working directory
INPUTS: none
RETURNS: string
THROWS: no
function ensurestack(freesize)
reallocates the stack and makes sure at leastfreesize
stack entries are unused and available. If the stack is smaller it will grow, otherwise it will not change.
INPUTS:
freesize
integer defining the minimum amount of unused stack after the call
RETURNS: none
THROWS: when called from within a metamethod
Stacksize does not count bytes but squirrel objects.
Important note: gonuts starts with a default stack size of 512. However, increasing the stack size may significantly impact memory usage by magnitudes of the stack size, among others because internally gonuts creates temporary virtual machines quite often.
Especially complex multithreading and event usage may significantly increase memory footprint this way.
Gonuts will automatically double the current stack size every time it detects a potential shortage, so usually you won't need to call ensurestack
yourself, unless you want to avoid the automatic (and potentially costly) mechanism kicking in.
function getenv(key)
retrieve a named environment variable
INPUTS:
key
a string identifying the environment variable to get
RETURNS: the environment variable as a string, ornull
in case it does not exist
THROWS: no
internal purpose, do not use.
In some cases you may have the need for a VM that does not allow for I/O operations or is otherwise restricted. An example gonuts makes use of would be the ability to load for example JSON config files simply as scripts while suppressing functionality that would enable someone to create configuration files actually executing malicious code when loaded.
Blackboxes provide most functionality, except all about events and networking, file access, system access (squirrel standard system lib, or systemfork and related functions), all XML classes or anything that may be directily abused to create output (in the sense of I/O) - with the exception of print
, which is supported.
Also, a blackbox is unable to import
dens (to hinder someone writing and using a den to avoid the restrictions)
Additionally a blackboxed VM cannot use the experimental WITH_DTORS class to provide destructors to classes.
User will not have direct access to objects in the blackbox, but the blackbox class provides some functions to transfer objects from a VM to another (the same restrictions like described for multithreading object transfers, see rtsync
class).
Unlike rtsync, reading objects from the blackbox will convert non-transferrable objects to null
instead of aborting the copy operation.
IMPORTANT NOTES:
rtsync
objects are fully supported except signal event handlers, so you MAY install communcation. Note that the VM calling into a blackbox, however, STOPS EXECUTING ANYTHING until the blackbox VM is done executing. To utilize rtsync you will have to use threads.realthread
, it actually is no thread. You may combine both techniques.script.addarchive
is not available by defaultblackbox([stacksize[,...]])
Create an empty "black box" VM where
stacksize
is an optional integer defining the stacksize of the VM in bytes. When stacksize is provided, the following parameters will be passed as arguments to the VM ( _argc and _argv variables). When no parameters provided the stacksize will be 1024 Bytes.
function require(spec[,filepath])
externallly causes the script to execute "script.require(spec)
" or "script.require(spec,filepath)
" within the blackbox and returns the exact same result to the caller.
See script.require
above for reference.
Note you can neither externally release nor shrink a script within the blackbox
Note: this call may not return, depending on the script loaded (may already enter an infinite loop)
function run(namespace[,filepath])
Similar to require, this causes the blackbox to execute a respective script.run
command , and returns the same result to the caller.
See above for reference.
Note this call may not return when the code executed in the blackbox does not return (e.g. enters an infinite loop)
function allow(...)
activates certain functionality, that is registering classes, functions and constants within the blackbox.
INPUTS: any number of strings, which must exactly match one of the keywords desribed below
RETURNS: none
THROWS: on parameter type mismatch or unrecognized keyword
keyword | activates and registers within blackbox |
---|---|
events | all event and networking abilities |
system | sqstd_systemlib and shell/fork abilities |
io | sqstd_iolib |
threads | realthread (note rtsync is always available) |
xml | all xml handling objects |
destructors | the WITH_DTORS base class |
addarchive | script.addarchive function |
ATTENTION: Activating the event system also registers sockfd
, which may be used to read or write files already. Not allowing "io" will not prevent this!
function call(objpath[,...])
calls a function in the blackbox
INPUTS:
objpath
a string indicating the function to call, in squirrel notation
...
optional, any number of parameters to pass to the blackbox function
RETURNS: the return value of the called function, ornull
when it is not transferrable
THROWS: whenobjpath
does not point to a valid function or an exception occured within the blackbox during execution
Note this call may not return when the code executed in the blackbox does not return (e.g. enters an infinite loop)
function compile(objpath,source)
compiles a string to a closure and placing the result to the indicated location within the blackbox
INPUTS:
objpath
a string defining the location where to put the closure to, in squirrel notation
source
a string to compile
RETURNS:true
unless objpath indicated to replace the roottable (->false
)
THROWS: on any other error, including compile errors
imagine this executes [objpath]<-compilestring(source) ;
and objpath could iterate, effectively re-using existing or creating non-existing tables on its "path".
function take(objpath)
copies the indicated object (and all of its sub-objects in case of an array or table) from the blackbox to the return variable.
INPUTS:
objpath
string, path to the object to retrieve
RETURNS: a copy of the object, where non-transferrable objects will benull
. Returnsnull
whenobjpath
did not "hit" an object (or the object is non-transferrable or in fact by itselfnull
)
THROWS: on input parameter mismatch only
local box=blackbox(); box.allow("threads","destructors","xml") ; if (box.require("myscript")) { local result=box.call("::myscript.somefunc","parameter",1234) ; print("Result: "+result+"\n") ; } if (box.compile("::foo.bar","::foo <- function (y) { print (\"foo got \"+y+\"\n\") ; } print \"replacing ::foo\"") { box.call("::foo.bar","foobar") ; // will replace foo and print "replacing ::foo" box.call("::foo","stuff") ; //calls the replacement and hence prints "foo got stuff\n" box.call("::foo","too","much") ; //will throw an exception, beacuse ::foo takes a single parameter! } local boxroottable=box.take("::") ; //copies over the complete root table. Extreme costly!
gonuts provides a configurable multi-channel logging system, and internally uses some default channels always present. Users may use the default channels or add their own.
Channels can only be configured, but channel configuation can not be queried. Logging can be buffered or instant, configurable separately for each channel. Buffered logging provides more stable timing and is more effective, but in turn in case of a hard crash some logs may be lost.
Note that any string logged will have a linefeed added when an optional timestamp is activated (see below)
Also note, that the maximum byte (character) length for every string to log is 4095.
function script.logconf(channel,type[,synchroneous][,aux])
configures a log channel
INPUTS:
channel
integer, channel number to configure
type
string, any of the list below
synchroneous
bool, optional.true
for direct action,false
for background buffering (recommended)
aux
string, auxiliary info depending ontype
, for example an output filename or a syslog name. When omitted defaults to "gonuts" or "gonuts.log", depending ontype
RETURNS: none
THROWS: on parameter errors
available types:
type | linux | win32 | aux |
---|---|---|---|
"off" | no logging | no logging | ignored if provided |
"file" | log to file | log to file | filename (incl. optional path) to log to, will always have a timestamp |
"syslog" | log to syslog info | log to "syslog.txt" | source name string |
"syslog_crit" | log to syslog crit | log to "syslog.txt" | source name string |
"syslog_err" | log to syslog error | log to "syslog.txt" | source name string |
"syslog_warn" | log to syslog warning | log to "syslog.txt" | source name string |
"syslog_info" | log to syslog info | log to "syslog.txt" | source name string |
"syslog_debug" | log to syslog debug | log to "syslog.txt" | source name string |
"print" | print to stdout | print to stdout | when empty string suppress timestamp, otherwise print timestamp |
"stdout" | print to stdout | print to stdout | when empty string suppress timestamp, otherwise print timestamp |
"stderr" | print to stderr | print to stderr | when empty string suppress timestamp, otherwise print timestamp |
"debug" | syslog debug and print to stdout in parallel | win debug feature (OutputDebugString) | ignored if provided |
When starting up, these default log channels are preconfigured:
Channel | type | gonuts usage |
---|---|---|
0 - debug | "print" | no. buffered |
1 - info | "print" | print instruction. buffered |
2 - warn | "print" | no. buffered |
3 - error | "stderr" | error instruction, compiler, runtime and internal errors. unbuffered |
4 - fatal | "stderr" | internal fatal errors (usually indicating termination). unbuffered |
Note: the print
instruction will always print to stdout in parallel, and the error
instruction always prints to stderr in parallel. Only the respective log channel will have an optional timestamp and linefeed added when configured.
function log([channel[,output])
logs a string to a log channel
INPUTS:
channel
integer channel number.
output
string to log
RETURNS: none
THROWS: on parameter error
When channel
and output
provided the output string is logged to the channel, which is normal usage of the feature.
script.log(2,"something is slightly wrong") ; logs "something is slightly wrong" using channel 2
Special combinations exist to provide ability to flush files and/or background buffers:
when no output
is provided for a channel this flushes the given channel file, if any. In case the channel is not configured to type "file" the call will have no effect. This will NOT flush the background buffer queue for this channel:
script.log(1) ; //flush the file channel 1 writes to
When no channel and output is provided at all, all buffered log queues will be flushed, forcing all output. This may take quite some time depending on the size of buffered log strings to output:
script.log() ; //flush all background buffering
To make classes behave more like C++ classes, a base class providing (optional) class hierarchy constructors and destructors is available. However, there are a lot of side effects and constraints making usage a risky undertaking. However, we feel there are people out there that might find this feature useful for some special cases.
Use with care, no guarantees. This is experimental, nasty and has a lot of odd side effects!
Define your classes extending this base class to get the constructor- and destructor effects. The WITH_DTORS class itself has no members at all.
class myclass extends WITH_DTORS { constructor() {} ; _destructor() {} ; }
ALL class constructors will be iterated from bottom base class to top on instanciation, not just the constructor at the top in hierarchy(which is standard squirrel behaviour). This constructor behaviour is pretty much more like C++ does it. When a class or base class has no constructor, it's skipped on instanciation.
Note that any C++ constructors of any base class provided through dens (modules) will be skipped - hence such classes will probably be nonfunctional and prone to hard crashes or undefined behaviour. Use such base classes at your own risk!
This is also true for many gonuts native classes.
Destructors are highly experimental, use at your own risk!
Define a destructor being called when the class instance got destroyed.
They work like constructors, just other way around, executing going down any base class hierarchy. When a class or base class has no destructor it's simply skipped in the process.
However, note that the VM calling the destructor(s) IS INDETERMINED,in most cases it will be the VM or friend VM of the vm that ran the constructors, sometimes it will be the VM causing the object release.
Also destructors have a big disadvantage - there is no exact control when they're executed, usually they're executed one or two instructions after the point where one would expect the destruction. Object deconstruction is completely asynchroneous.
Be aware that accessing objects outside of the class instance being destructed generally is a very, very bad idea.
Do not fall into the pit to store references just to be able to access them from your destructor, or you'll find yourself in the "spurious host crash" or "unresolveable ring reference memory leak" situation soon.
Destructors make sense to operate on weak references ONLY.
Destructors can cause the garbage collector to fail lateron, and they may **cause gonuts to hard crash **on any thread deinitialization (that is, a real thread ends or application exits)
To put this explicitly: DESTRUCTORS ARE EVIL. They're really experimental... No guarantees, check them out yourself, but never, ever rely on them just for the sake of their existence!
gonuts supports importing modules called dens. Modules generally are dynamically linked libraries (Windows: .DLL, Linux: .so) loaded at runtime using the import
directive and extending functionality.
Users may create and distribute own modules. The import system ensures that loaded modules match the gonuts settings (like 32 or 64bits, float or double, garbage collector presence and some other squirrel specific settings) and provide general compatibility.
Gonuts comes with a C++ source code API for creating modules and some module sources are available as examples.
However, users must not modify the API source files in a way potentially or in fact breaking compatibility. Note that extending function pointer lists for example will probably do so LATER in time. However, the API provides a way for extensions (see hostspecific struct) which also can be used to have dens interoperate.
In this document only the Squirrel aspect is described, refer to specialized documentation about how to create dens.
function import(name[,table])
loads and initializse a den module
INPUTS:
name
string. Filename (optionally including path) of a den, **excluding any filename extension **(e.g. no .dll or .so)
table
table object to pass to module initialization (destination table). Defaults to root table when omitted
RETURNS: null on failure, otherwise a table
THROWS: on lots of possible errors
Notes:
if (import("denmylib")) { /*success*/ }
is and will stay valid.Sometimes, on 32 bit systems explicit 64 bit operations are required. Of course, they will also work on 64bit systems to keep code compatilbe with the drawback of less performance.
The int64 class "emulates" (as much as possible) an 64bit (signed) integer type.
The class though has some syntactic drawbacks when it comes to expressions. It always must be on the right (in terms of 'proper') side of the operand of any (sub-)expression, or an exception will be thrown (unless both operands are an int64 instance).
This will fail:
local foo=1 ; local bar=int64(2) ; local result=foo+bar ; //will throw an exception if (1 < bar) print("less\n") ; //will throw an exception
but this will work:
local foo=1 ; local bar=int64(2) ; local result=bar+foo ; //result will be an int64 class of the value 3.
Another drawback is taht not all operators will work, like shift operators or binary operators like or, and, xor... and even not.
The unary ! operator will also not work properly:
local foo=int64(0); local isnull = !foo ; //isnull will be false, in absolutely ALL CASES!
Another drawback besides inevitable longer execution speed of operations (even on 64bit systems) is memory usage. Each "number" represented by an int64 instance will use up DOUBLE the memory needed, plus some global overhead - per RThread.
Comparisons will work properly between two instances, to be able to compare to other types (like standard integers, strings or floats) you need to utilize the member functions:
local foo=1 ; local bar=int64(1) ; local tmp=int64(2) ; if (bar==foo) print("equal!\n") ; //will throw an exception. if (bar==tmp) print("equal!\n") ; //will work, but not print (they aren't equal!) if (foo==bar) print("equal!\n") ; //will throw an exception if (bar.equals(foo)) print("equal!\n); //will print.
Additionally, to be compatible with legacy functions, an int64 also accepts strings as constructor input. Unrecognized strings will result in a value of 0 (zero),
these operators are supported (int64 being an int64 instance, op2 being any of int64 instance, string, integer or float):
Operator | position | remarks |
---|---|---|
- | -int64 | (unary minus, like in local negative=-positive ; ) |
- | int64 - op2 | |
+ | int64 + op2 | |
* | int64 * op2 | |
/ | int64 / op2 | |
% | int64 % op2 | |
++ | ++int64 or int64++ | post- or preincrement |
-- | --int64 or int64-- | post- or predecrement |
when int64 instances being on both sides of the operator, these are additionally supported:
(this is Squirrel-inherent, and gonuts already "tricks" squirrel to make even these possible)
!= == < > <= >=
However, there are member functions to be able to do comparisons of all kind between an int64 instance and either a string, another int64 instance, an integer or a float type. Also, all math operators are mirrored using a member function, plus some binary operation member functions exist.
Attention: The unary not (!) will never work properly
class int64{ constructor(int[,inth]); function tostring(); function tointeger(); funtion gethi(); function getlo(); function isnative(); function add(other[,otherh]); function sub(other[,otherh]); function mul(other[,otherh]); function div(other[,otherh]); function mod(other[,otherh]); function or(other[,otherh]); function xor(other[,otherh]); function add(other[,otherh]); function sl(bits); function sr(bits); function cmp(other[,otherh]); function equals(other[,otherh]); }
int64(int[,inth]) ;
constructs a 64 bit instance from the parameters, where
int
is either another int64 instance or an integer value, and
inth
is an optional second "hi order" integer value. Must be omitted when int
is an int64
The 64bit value is combined from int and inth with the following formula:
value=int | (inth<<32);
getlo()
returns the least significant 32 bit of the represented 64bit value
INPUTS: none
RETURNS:integer, the low order 32 bit integer value of the stored 64bit value
THROWS: no
gethi()
returns the most significant 32 bit of the represented 64 bit valuie
INPUTS: none
RETURNS:integer, the high order 32 bit integer value of the stored 64bit value
THROWS: no
tointeger()
on 64 bit systems returns the 64 bit integer value, on 32 bit returns the least significant 31 bit of the represented 64 bit value, keeping its sign bit in the MSB.
INPUTS: none
RETURNS:integer, the "cast" of the 64bit to the native integer representation
THROWS: no
isnative()
check if the stored 64 bit value fits into a native integer. On 64bit systems always returns true, otherwise the return value depends on the stored 64bit value.
INPUTS: none
RETURNS:bool, true=value fits into an integer, false=value does not fit into an integer
THROWS: no
add(other[,otherh])
adds "other" to the current value and returns another int64 instance of the result
INPUTS:
other
any int64 instance, string representing a decimal number, float or integer value
otherh
optional and only whenother
is an integer, an integer similar to constructorinth
(MSB part).
RETURNS:an int64 instance holding the result of the operation
THROWS: on any input parameter mismatch
sub(other[,otherh])
subtracts "other" from the current value and returns another int64 instance of the result
INPUTS:
other
any int64 instance, string representing a decimal number, float or integer value
otherh
optional and only whenother
is an integer, an integer similar to constructorinth
(MSB part).
RETURNS:an int64 instance holding the result of the operation
THROWS: on any input parameter mismatch
mul(other[,otherh])
multiplies "other" with the current value and returns another int64 instance of the result
INPUTS:
other
any int64 instance, string representing a decimal number, float or integer value
otherh
optional and only whenother
is an integer, an integer similar to constructorinth
(MSB part).
RETURNS:an int64 instance holding the result of the operation
THROWS: on any input parameter mismatch
div(other[,otherh])
divides the current value by "other" and returns another int64 instance of the result
INPUTS:
other
any int64 instance, string representing a decimal number, float or integer value
otherh
optional and only whenother
is an integer, an integer similar to constructorinth
(MSB part).
RETURNS:an int64 instance holding the result of the operation
THROWS: on any input parameter mismatch or on division by zero
mod(other[,otherh])
modulo operation. Returns another int64 instance of the result of the operation "current modulo other".
INPUTS:
other
any int64 instance, string representing a decimal number, float or integer value
otherh
optional and only whenother
is an integer, an integer similar to constructorinth
(MSB part).
RETURNS:an int64 instance holding the result of the operation
THROWS: on any input parameter mismatch
cmp(other[,otherh])
compares the current value with the provided other value
INPUTS:
other
any int64 instance, string representing a decimal number, float or integer value
otherh
optional and only whenother
is an integer, an integer similar to constructorinth
(MSB part).
RETURNS: integer 0 when both values are equal, a negative integer when the current value is less than the provided, or a positive integer when the current value is greater than the provided
THROWS: on any input parameter mismatch
equals(other[,otherh])
compares current value with other and returns true or false
INPUTS:
other
any int64 instance, string representing a decimal number, float or integer value
otherh
optional and only whenother
is an integer, an integer similar to constructorinth
(MSB part).
RETURNS:bool, true when both values are equal, otherwise false
THROWS: on any input parameter mismatch
Note: when other is an int64 you may as well directly compare them using the comparison == operator
or(other[,otherh])
returns the result of the binary "or" operation of current and given value
INPUTS:
other
any int64 instance, string representing a decimal number, float or integer value
otherh
optional and only whenother
is an integer, an integer similar to constructorinth
(MSB part).
RETURNS:an int64 instance holding the result of the operation
THROWS: on any input parameter mismatch
xor(other[,otherh])
returns the result of the binary "xor" (exclusive or) operation of current and given value
INPUTS:
other
any int64 instance, string representing a decimal number, float or integer value
otherh
optional and only whenother
is an integer, an integer similar to constructorinth
(MSB part).
RETURNS:an int64 instance holding the result of the operation
THROWS: on any input parameter mismatch
and(other[,otherh])
returns the result of the binary "and" operation of current and given value
INPUTS:
other
any int64 instance, string representing a decimal number, float or integer value
otherh
optional and only whenother
is an integer, an integer similar to constructorinth
(MSB part).
RETURNS:an int64 instance holding the result of the operation
THROWS: on any input parameter mismatch
sl(bits)
performs a binare "shift left" operation of current value by given number of bits
INPUTS:
bits
any int64 instance, string representing a decimal number, float or integer value denoting the number of bits to shift (only values making any sense are 0 to 64)
RETURNS:an int64 instance holding the result of the operation
THROWS: on any input parameter mismatch
sr(bits)
performs a binare "shift right" operation of current value by given number of bits
INPUTS:
other
any int64 instance, string representing a decimal number, float or integer value denoting the number of bits to shift (only values making any sense are 0 to 64)
RETURNS:an int64 instance holding the result of the operation
THROWS: on any input parameter mismatch
tostring()
returns the 64bit value as a human readable string (in decimal format)
INPUTS: none
RETURNS:string
THROWS: no
Basic 64bit operations can be performed using the following global functions.
All these functions can perform on 32 bit integers, but for values exceeding 32 bits they will use human readable string representations of numbers. All numbers can be entered as string or integer, return values depend on the necessary bit size.
(On 64 bit Squirrel they operate normally, not using strings as return values, but still accept them as an alternative input)
However, note that due to number conversions on 32bit Squirrel systems, these operations can get pretty slow especially when processing string inputs or outputs.
Note: String representations are always "plain decimal". There is no support for hexadecimal or octal representation.
function add64(op1,op2)
Adds two operands
INPUTS:
op1 Operand1 (integer or string containing an integer representation)
op2 Operand2 (integer or string containing an integer representation)
RETURNS:The sum of op1 and op2(op1+op2)
as an integer, or when an integer is limited to 32 bit and the result does not fit into 32 bits returns a human readable string representation
THROWS: no
function sub64(op1,op2)
Subtracts op1 from op2
INPUTS:
op1 Operand1 (integer or string containing an integer representation)
op2 Operand2 (integer or string containing an integer representation)
RETURNS:The difference between op2 and op1(op1 - op2)
as an integer, or when an integer is limited to 32 bit and the result does not fit into 32 bits returns a human readable string representation
THROWS: no
function mul64(op1,op2)
multiplies two operands
INPUTS:
op1 Operand1 (integer or string containing an integer representation)
op2 Operand2 (integer or string containing an integer representation)
RETURNS:The product of op1 and op2(op1*op2)
as an integer, or when an integer is limited to 32 bit and the result does not fit into 32 bits returns a human readable string representation
THROWS: no
function div64(op1,op2)
divides two operands
INPUTS:
op1 Operand1 (integer or string containing an integer representation)
op2 Operand2 (integer or string containing an integer representation)
RETURNS:The quotient of op1 divided by op2(op1/op2)
as an integer, or when an integer is limited to 32 bit and the result does not fit into 32 bits returns a human readable string representation
THROWS: no
function mod64(op1,op2)
calculates the division remainder pf two operands
INPUTS:
op1 Operand1 (integer or string containing an integer representation)
op2 Operand2 (integer or string containing an integer representation)
RETURNS:The remainder of op1 divided by op2(op1%op2)
as an integer, or when an integer is limited to 32 bit and the result does not fit into 32 bits returns a human readable string representation
THROWS: no
function gt64(op1,op2)
compares the operands
INPUTS:
op1 Operand1 (integer or string containing an integer representation)
op2 Operand2 (integer or string containing an integer representation)
RETURNS:True if op1 is greater than op2, otherwise false.
THROWS: no
function lt64(op1,op2)
compares the operands
INPUTS:
op1 Operand1 (integer or string containing an integer representation)
op2 Operand2 (integer or string containing an integer representation)
RETURNS:True if op1 is lesser than op2, otherwise false.
THROWS: no
function eq64(op1,op2)
compares the operands
INPUTS:
op1 Operand1 (integer or string containing an integer representation)
op2 Operand2 (integer or string containing an integer representation)
RETURNS:True if op1 is equal op2, otherwise false.
THROWS: no
function millitime()
INPUTS: none
RETURNS: an integer representing milliseconds since 1.1.1970
THROWS: no
Note: integers in squirrel generally are signed, but the value returned is meant to be unsigned. Expect a "negative" overflow accordingly.
function microtime()
INPUTS: none
RETURNS: an integer representing microseconds since 1.1.1970
THROWS: no
Same as millitime (negative overflow), plus 32 bit integers are prone to multiple overruns. Use for relative timings only
function adler32(buffer[,concat])
calculates an adler32 checksum
INPUTS:
buffer
any string,buffer
,sockbuf
orblob
instance
concat
integer, optional. start value (result of previous adler32 call to "concatenate" several buffers)
RETURNS: integer adler32 checksum of the given buffer.
THROWS: on input errors
Note: adler32 is a very fast algorithm with the drawback of high collision probability especially on short input buffers.
function crc32(buffer[,concat])
calculates an 32 bit crc
INPUTS:
buffer
any string,buffer
,sockbuf
orblob
instance
concat
integer, optional. start value (result of previouscrc32
call to "concatenate" several buffers)
RETURNS: integer crc of the given buffer.
THROWS: on input errors
A standard cyclic redundancy checksum using 8 shifts per input byte and polynomial 0x04C11DB7 XOR on MSB(bit 31) set.
function zinflate(buffer[,expectheader])
INPUTS:
buffer
anybuffer
,sockbuf
orblob
instance
expectheader
bool, optional. Whether or not to expect a zlib header in the compressed data.
RETURNS: abuffer
instance holding decompressed data, or null on invalid input data
THROWS: on input errors
function zdeflate(buffer[,rate])
INPUTS:
buffer
any string,buffer
,sockbuf
orblob
instance
rate
integer, optional compression rate. Value must be from 0 to 4095. Generally spoken, the higher the number the better (and slower) compression. Defaults to 128 when omitted.
RETURNS: abuffer
instance holding compressed data, or null on compression failure
THROWS: on input errors
function urlencode(string)
Encodes a string to an url compatible formatted string
INPUTS:
string
string to encode
RETURNS: encoded string
THROWS: on input parameter failures
Decodes an url encoded string back to its standard format
function urldecode(string)
INPUTS:
string
url encoded string to decode
RETURNS: decoded string
THROWS: on input parameter failures
Convert a buffer object to a base64 encoded string
function base64encode(buffer)
INPUTS:
buffer
any string,buffer
,sockbuf
orblob
instance
RETURNS: encoded string
THROWS: on input parameter failures
function base64decode(string)
Convert a base64 encoded string to a binary SQBuffer
instance
INPUTS:
string
a base64 encoded string
RETURNS: an SQBuffer containing the decoded binary data or null when input cannot be decoded
THROWS: on input parameter failures
Note: for base32 encoding and decoding, use ::pxp.base32encode and ::pxp.base32decode from pxlib.pnut (remember: script.addarchive("pxlib.pnut")
, but script.require("pxp")
)
function tohex(buffer)
Convert binary data to a 2character hexadecimal representation string
Convert a buffer object to a base64 encoded string
function base64encode(buffer)
INPUTS:
buffer
any string,buffer
,sockbuf
orblob
instance
RETURNS: encoded string
THROWS: on input parameter failures
converts a hex string back to an SQBuffer
object
function fromhex(string)
INPUTS:
string
a hex encoded string.
RETURNS: an SQBuffer containing the decoded binary data
THROWS: on input parameter failures or when input is no valid hex encoded sring
Note: accepts both upper- or lowercase alpha characters
function GUUID()
Creates and returns a new GUUID (Global unique universal ID) string
INPUTS: none
RETURNS: a string containing a new GUUID
THROWS: no
Global Universal Unique IDs are usually created from current time, hardware and random entropy data. It is utmost unlikely, if not impossible, that two identical GUUIDs are created anytime and anywhere on this planet. However, this behaviour is not guaranteed. Also note that GUUIDs are not inherently cryptographically safe.
NOTE: On Windows, this function uses the built in CoCreatGuid windows function. On linux, a random algorithm based on Yarrow is used. In case you want to use libuuid of your linux distribution there is a den supporting that (the den is available for windows, too, again using that CoCreateGuid function). That den will overwrite the built-in function when imported to the root table.
function stat(pathtofile)
returns general information about a file (calls stat() C function).
Especially useful in combination with fsdir class, which handles names only
INPUTS:
pathtofile
: a string with a relative or absolute path to a file or directory
RETURNS: null when file not found or accessible, otherwise a table with below structure
THROWS: no
returned table when file or directory exists and is accessible:
{
path=pathtofile, //exactly the same as passed argument
len=int64(filesize), //size of file in bytes, see int64 class
ctime=creation_timestamp, //unix timestamp integer of file creation time
mtime=modify_timestamp, //unix timestamp integer of last file modification
atime=access_timestamp, //unix timestamp integer of last access
mode=filemode, //file type and mode (see linux/windows stat() c-function)
}
buffers are binary buffer representations, offering a variety of data conversion and access.
They can also be a reference to parts of another buffer
(copyless operations). They’re widely used within gonuts and a general binary data representation, unlike blobs (see Squirrel standard library) which represent binary data as streams.
Virtual representation:
class buffer { constructor(in) ; _get(offset) ; //operator [] _set(offset) ; //operator [] function get(offset) ; function set(offset,integer) ; //no return value function set(offset,array) ; //no return value function len() ; function size() ; //synonym for len() function array([offset[,len]]) ; function getstring() ; function setstring(string,offset[,len]) ; //no return value function setstringraw(string,offset[,len]) ; //no return value function setname(string) ; function getname() ; function slice(offset[,len[,forcecopy]] ; function readblob(offset,len) ; function readsockbuf(offsetl,len) ; function getparent() ; function fillfrom(other) ;//inline operation, no return value function append(other) ; //inline operation, no return value function prepend(other) ;//inline operation, no return value function search(needle[,offset[,limit]]) ; }
buffer(integer)
creates a new buffer with the size in bytes given by the integer parameter. The buffer content is random and not initialized!
buffer(string)
creates a buffer from a string, copying the string. Note the buffer is of size string.len()+1, adding a null termination!
buffer(array)
creates a buffer from an array. The array elements are expected to be integer values from 0 to 255 only!
buffer(blob)
Takes the given blob
(see Squirrel standard library)) and REFERENCES IT. Changing the blob
will change the buffer content and vice versa. However, changing the blob size will not be reflected on the buffer
or vice versa. (For example when appending another buffer
to this reference, the blob data will be copied before concatenation happens. Writing to the blob will not be reflected in the buffer).
buffer(sockbuf)
Creates a buffer
by copying the contents of the given sockbuf
instance. The sockbuf
will not be drained and keeps its contents.
Note: You cannot create an buffer from another buffer using constructors. Use appropriate methods instead (fillfrom, append, prepend or slice, where slice puts you in control whether you want a referencing or copied buffer)
You can access any byte within the buffer like an array by using brackets, both reading and writing. Will throw an exception when accessing indexes outside boundaries or the index is no integer
Example:
local buf=buffer(5) ; buf[0]=0 ; print(“buf 1: “+buf[1]) ; local test=buf[5] ; //will throw an exception, as buffer size is 5 maximum index is 4.
function get(index)
An alternate way of reading a byte from the buffer at a given position.
INPUTS:
index
integer index position within the buffer
RETURNS: integer, byte at given buffer position
THROWS: when index is outside buffer bounds or no integer
function set(index,value)
An alternate way of writing a byte to a buffer.
INPUTS:
index
integer buffer position to write to
value
integer byte to write to given position. Note the value will be ANDed with 255.
RETURNS: none
THROWS: on invalid input parameters or when index is outside buffer bounds
function set(index,array)
Copy several bytes to the given position in the buffer.
Array must be an array containing integer values. Index is integer.
INPUTS:
'index' integer start position for writing to the buffer
'array' array, each array element must be an integer value representing a byte to write (will be ANDed with 255)
RETURNS: none
THROWS: on invalid input parameters or when index is out of bounds
Synonym for size()
, see below
function size()
returns the size of the buffer in bytes
INPUTS: none
RETURNS: integer, size of the buffer in bytes
THROWS: no
function array([offset[,len]])
returns buffer content as an array of integers representing bytes
INPUTS:
offset
integer, optional. Offset within the buffer to start conversion. Defaults to 0 (start of buffer) when omitted
len
integer, optional. Amount of bytes to process. When omitted, all bytes until end of buffer will be processed.
RETURNS: an array containing the bytes according to the input parameters
THROWS: on invalid input parameters or when the requested area of the buffer is out of bounds
getstring([offset[,len]])
converts the buffer or part of the buffer to a string
INPUTS:
offset
integer, optional. Start position within the buffer
len
integer, optional whenoffset
is given. Amount of bytes to read from the buffer
RETURNS: a string consisting of the bytes in the buffer according to input parameters
THROWS: when input parameters are invalid
When len
is omitted, zero or negative, all bytes until end of the buffer are returned.
When offset
is omitted it is assumed to be 0, denoting the begin of the buffer.
function setstring(string,index,[len])
Copies the first len bytes of given string to given index position of the buffer (will cut the string regarding len and buffer size). Will append a null termination to the string. If you don't want an addition termination byte, use SetStringRaw
instead (see below).
INPUTS:
string
string, the input string
index
integer (mandatory!), index of the target position within the buffer
len
integer, optional. The amount of bytes to read from string. When omitted, all of the string will be processed.
RETURNS: integer, number of processed bytes
THROWS: on invalid input parameters
Notes: you must not omit index
.
The buffer size will not change, when there is not enough room not all of the string will be copied to the buffer. The return value indicates how many bytes were actually processed, not including the terminating null character.
If len is omitted it is regarded zero (=copy to end of buffer or end of string, whichever occurs first).
function setstringraw(string,index,[len])
identical to setstring
(see above), but will not append a null termination within the buffer.
function setname(string)
Sets an additional string “name”, which basically is metadata attached to the string. When not set, defaults to an empty string. Note that this string will be included in the string returned by the tostring()
metamethod of the buffer.
INPUTS: string, any string
RETURNS: none
THROWS: no
function getname()
Returns the additional name string. (empty string if not set)
INPUTS: none
RETURNS: the string set bysetname
(or an empty string when none is set)
THROWS: no
function fillfrom(bufferobject)
Re-creates the buffer by copying from a given buffer object, which can either be another buffer
, a blob
, an sockbuf
or a string. Discards any current buffer content before copying. (Basically this is the same as constructor, but additionally supports SQBuffers, plus it never references a nother buffer but always copies data!)
INPUTS: any string,
buffer
,sockbuf
orblob
instance
RETURNS: none
THROWS: no
function slice(offset[,len[,forcecopy]]) *
Returns part of the buffer as another new buffer
.
INPUTS:
offset
integer, start index within the buffer
len
integer, optional. Byte count, when omitted, zero or negative processes until end of buffer
forcecopy
bool, optional. When true or omitted the returned buffer will be independent and data is copied, otherwise (false) the returned buffer is a subbuffer referencing the data requested
RETURNS: Anbuffer
instance referencing or containing the bytes requested
THROWS: on illegal input or out when requested area is out of bounds
Note that when referencing for a copyless operation, forcecopy
must be provided as boolean false, and later changes to the subbuffer or the original buffer will reflect in the paired buffer, too.
function readblob(offset,len)
Similar to slice, get the buffer or part of the buffer as a blob
instance
INPUTS:
offset
integer, mandatory. Offset where to start copying to blob in thebuffer
len
integer, mandatory. Number of bytes to copy from thebuffer
. When zero or negative will process all bytes until end of buffer
RETURNS: A blob fresh instance containing a copy of the indicated slice
THROWS: when indicated slice is outside buffer bounds or input parameters are invalid
Note: both input parameters are mandatory (yet)
function getparent()
returns the parent buffer
referenced
INPUTS:: none
RETURNS: a parent SQBuffer instance or null when there is no referenced parent
THROWS: no
If the SQBuffer is a subbuffer (referencing another buffer), it returns the parent buffer, otherwise returns null.
Note that in case of a referenced blob this method will also return null!
function search(needle,offset[,limit])
Performs a binary search for first occurrence of needle within the buffer, starting at offset in the buffer, optionally limiting the search to limit bytes of needle.
INPUTS
needle
: any string,buffer
,sockbuf
orblob
instance to search fo within the buffer
offset
: integer. An offset where to start the search in the buffer
limit
: intereger, optional. Limit to "cripple" the amount of bytes taken into consideration from needle (always starting at its beginning)
RETURNS: a negative integer value of -1 when needle was not found in the indicated area of the buffer, otherwise an integer value indicating the first byte of the matching byte sequence within the buffer
If needle
is a string, no terminating null character is included.
When offset
is negative, it is calculated from the end of the buffer, but still a forward search is performed.
When limit
is 0 or omitted, all of needle is searched for.
Returns the an integer value of the found starting position within buffer or -1 when there’s no match
function readsockbuf()
Copies all content of this buffer to a sockbuf
and returns it. Unlike readblob, this function always copies the complete buffer content.
INPUTS: none
RETURNS: an EvBuffer containing a copy of the SQBuffer content
THROWS: no
function append(object)
Appends the content of the passed buffer object
to the current buffer content by copying.
INPUTS: a string,
blob
,buffer
orsockbuf
instance to append
RETURNS: none - the operation is inline
THROWS: exception if the input is invalid or missing
Note: When object
is a sockbuf
, this operation will not drain it and the sockbuf
will keep its content.
function prepend(object)
Same a append, just the concatenation is other way around, the buffer will contain a copy of the content of object
followed by its former content.
INPUTS: a string,
blob
,buffer
orsockbuf
instance to prepend
RETURNS: none - the operation is perfoming inline on current buffer content
THROWS: exception when parameter is invalid or missing
Note that this involves significantly more copying (the current buffer content will be also copied, not only that of the given object) compared to append()
.
function array([offset[,len])
Returns buffer content as an array of integers. Optional offset and len to indicate where to start and how many bytes to read from the buffer.
INPUTS:
offset
: integer, optional. Index where to start reading from buffer content. Defaults to start of buffer content when omitted
len
: integer, optional. Number of bytes (and hence array elements) to read. Defaults to read all bytes until end of buffer.
RETURNS: an array containing an integer element for each byte
THROWS: no
Note: Zero or negative len
has the same effect like omitting and will indicate to get all bytes until end of buffer.
Tool class for reading filesystem directories.
Available on both windows and linux, but unfortunately working slightly different depending on platform. It is recommended to use the ISWINDOWS
global to compensate yourself.
Currently fsdir
can not differentiate between links, hardlinks and will not return hidden or system files on Windows.
See also stat function if you need more detailed infos besides names
Virtual class definition:
class fsdir { constructor(filespec) ; function get() ; function getfiles() ; function getdirectories() ; function getentry(indexno) ; function size() ; }
fsdir(filespec) ;
Where filespec is a string.
It may contain an absolute or relative path and * wildcards (comparable to “dir” or “ls” shell commands).
Note: Will read the given directory immediately on construction ONCE, any changes in the filesystem won't be reflected afterwards and all methods will access this buffered "snapshot" information only.
Note that windows and linux operate differently about wildcards, and that path notation differs too. On linux, it is possible to get a subset of files matching a wildcard, while on windows you can only read a full directory and any "file specification" is treaded as a folder name.
Example Windows:
local a=fsdir("C:\mypath\somepath") ; //windows. note somepath is a PATH. A trailing backslash is optional
Examples Linux:
local a=fsdir("/var/log/*.log") ; //will return all files (and folders, if any) ending with ".log" local b=fsdir("/var/log/") ; similar behaviour as Windows, gets the full directory local c=fsdir("./mypath/somepath") ; undefined, depending on somepath being a directory, file or is missing
Hint: There are tool functions in pxlib.nut providing automatic slash/backslash conversion depending on platform
function get()
Get all all entries as an array containing of tables with name and type(file or dir)
INPUTS: none
RETURNS: an array of tables representing all matching entries
THROWS: no
The tables include strings for name and type of entry each. Later versions may add more slots to the tables. Can return an empty table when something about the filespec was wrong (like the path did not exist).
Note that on linux, the parent and current directories will be returned (".." and ".") as usual.
function getfiles()
Get all files (Linux: matching the filespec), omitting directories
INPUTS: none
RETURNS: an array of strings representing the filenames
THROWS: no
function getdirectories()
like Getfiles, but returns directories only
INPUTS: none
RETURNS: an array of strings representing directory names
THROWS: no
function getentry(index)
Returns a single table for the directory entry requested
INPUTS: an integer index specifying the entry number to retrieve, starting with 0.
RETURNS: A table containing type and name of entry
THROWS: on illegal index, negative index or when index is equal or larger than the return value ofSize()
(see below)
function size()
Returns the number of present directory entries in the snapshot
INPUTS: none
RETURNS: integer value, number of available entries
THROWS: no
getentry()
and get()
Tables returned by GetEntry and Get contain the following keys:
table key | value |
---|---|
name | a string with the directory entry name (e.g. filename, or subdirectory name) |
type | a string denoting the entry type, either being “file”, “directory” or “unknown”. |
Later versions may extend functionality and present more sophisticated information in the tables
These classes and function do not exist on windows platform
function systemfork(command[,path][,daemon[,pipes]][,parameters…])
Forks a program or shell command either as a child or daemon process, optionally installing I/O pipes for communication.
This function is much more flexible than the Squirrel stdlib system
call and allows for parallel processing and process communication through standard pipes.
INPUTS:
command
: string. shell command to execute (or path to executable)
path
: string, optional. Working directory for the new process
daemon
: bool, optional. true=daemonize, false= start a child process
pipes
: integer, optional. Bitflags controlling pipe installment (see below)
parameters
: any number of strings being the command line parameters to pass to the new process. Note that a single string with space separated parameters will not be the same like passeng them separately, but rather look like a single parameter put in parantheses for the new process
RETURNS : a ProcPipe instance when pipes were installed or the PID as an integer value if not, or null in case an error occured
THROWS: on any error but "unknown command/file not found"
Note some optional parameters can be omitted while providing followup parameter(s) due to type safety.
Bitflag table for pipes
:
bit | value | meaning |
---|---|---|
0 | 1 | install a pipe to childs stdin stream |
1 | 2 | install a pipe from childs stdout stream |
2 | 4 | install a pipe from childs stderr stream |
bits not set will cause the related stream to be closed.
A value of 7 installs all three pipes, where a value of 2 will only install a pipe to read the standard output of the process
When pipes
is omitted its value is assumed 0, which will cause the function to return a PID value rather than a ProcPipe
instance on success
function childstat (PID)
Must be called regularly on forked childs to check child status and avoid zombie processes. Not necessary for daemons, but when a child process terminates it is wiped from the kernels process table ONLY when you called this function on its PID.
Use when SystemFork returned a PID integer value
INPUT:
PID
integer, mandatory. The process ID as returned bySystemFork
RETURNS: null, integer or bool (see below)
THROWS: no
Returns either:
-an integer representing the exit status of the child
-boolean false when the child was signalled (for example killed or crashed)
-boolean true when the child is still running
-null when the child does not exists or any other error occurred
When systemfork
returns a procpipe
instance users should prefer its status
method over this global childstat
function.
However, in Theory the PID can be of ANY process in theory to get their status, even foreign ones not started by gonuts
function killPID(PID[,signal])
Signal a process, usually to terminate it
Signals a process with the given PID. Not necessarily restricted to processes created using SystemFork.
It is similar to the well-known linux shell command "kill"
INPUTS:
PID
integer, process ID of the process to signal
signal
integer, optional. Signal number. Defaults to 15 (SIGTERM
) when omitted. See table below
RETURNS: null when the PID did not exist, integer zero when signalled or integer -1 on any error (happens for example in cases of an invalid signal number, your gonuts does not have the permission to send the specified signal or when the targeted PID does not exist or is a zombie already.
Important: If you run gonuts underh admin privileges (root) and provide a PID of -1 here you signal to ALL PROCESSES of the host system (except gonuts and the 'init' process).
gonuts provides constants for the most common signals:
SIGHUP
, SIGINT
, SIGABRT
, SIGKILL
, SIGSEGV
, SIGPIPE
, SIGALRM
, SIGTERM
and SIGQUIT
for convenience.
Table of sample signals:
Integer value | Signal |
---|---|
1 | SIGHUP - indicate the death of terminal or controlling process |
2 | SIGINT - interrupt from keyboard, attention signal |
3 | SIGQUIT - caused by CTRL-C on keyboard |
9 | SIGKILL - immediate "forced" termination via kernel, unblockable |
10 | user defined signal |
12 | user defined signal |
14 | SIGALRM (alarm clock timer signal) |
15 | SIGTERM - normal termination request |
19 | SIGCONT - continue after SIGSTOP |
20 | SIGSTOP - stop process, unblockable |
consult your host system documentation about its signals.
You can catch some external signals using gonuts' event system (see Event
), too.
Note: Users should prefer the methods of ProcPipe
instances over this general global function when handling non-daemonized childs.
The procpipe
class instance represents a forked child or daemon process, enabling communication through pipes (and also providing the PID and functions to signal or check the status).
Virtual prototype:
class procpipe { //note this class has no constructor function Read(count); function ReadErr(count); function Write(buffer); function WriteString(string); function CloseWrite(); function CloseErr(); function GetPID(); function Signal(sig); function Status(); }
Not available. Users cannot create instances directly, they’re ONLY returned by the function SystemFork.
function read(count) //blocking call
reads (up to) count bytes from the standard I/O pipe (process stdout).
INPUTS:
count
integer, amount of bytes to rad
RETURNS: null when pipe is disconnected or a read error occurs, otherwise aSQBuffer
object
THROWS: on input parameter error
This is a blocking call and will only return on error or after the requested number of bytes was read!
When count
is negative, it defaults to 128. When it is 0, all available data is read.
Again, note this is a blocking call!
function readerr(count) //blocking call
Same as Read, but uses the stderr pipe of the process
function write(buffer)
Writes an SQBuffer to the stdin of the process
INPUTS:
buffer
abuffer
object. Mandatory, no buffer conversions!
RETURNS: integer. -1 = write error, 0 = success
THROWS: on input parameter error
Note this method accepts buffer
objects only.
function writestring(string)
Writes a string to the stdin of the process without a terminating null character (note converting a string to a SQBuffer object usually inplies a terminating null character)
INPUTS:
string
string to write
RETURNS: integer. -1 = write error, 0 = success
THROWS: on input parameter error
Returns the integer Process ID of the process
function getPID()
INPUTS: none
RETURNS: integer PID of the process, or -1 after process terminated
THROWS: when input parameter provided
Do not rely on the process NOT being terminated when return value is not -1. Use Method Status()
instead.
function closewrite()
Close the pipe to stdin of the process
INPUTS: none
RETURNS: none
THROWS: when input parameter provided
function closeerr()
Close the pipe to stderr of the process
INPUTS: none
RETURNS: none
THROWS: when input parameter provided
function closeread()
Close the pipe to stdout of the process
INPUTS: none
RETURNS: none
THROWS: when input parameter provided
Signal the child process
function signal(sig)
INPUTS:
sig
integer or integer constant, see table of functionKillPID
RETURNS: null when the PID did not exist, integer zero when signalled or integer -1 on any error (happens for example in cases of an invalid signal number, your gonuts does not have the permission to send the specified signal or when the targeted PID does not exist or is a zombie already.
THROWS: onm input parameter number or type mismatch
function status()
Returns process status similar to function ChildStat, however is will invalidate the stored PID (by setting PID to -1) when child status indicates any termination.
Users should prefer this method over using global function ChildStat
with method GetPID
.
However, status will return false or an integer only ONCE when the child terminated meanwhile, any subsequent calls will then return null. In case of a null return, if you're unsure if there was another call returning a termination/exit value, you may check for GetPID() returning -1 which is indicating a termination.
Native database support has been removed from the gonuts core to reduce footprint for users not requiring database access.
There are several options for database support using den modules: dendbi(linux only), densqlite or dencql. A denmariadb to access mysql is work in progress (use dendbi to access mysql or mssql for now. Not available for MS Windows)
.
classes for event handling and networking.
VMs do have a mutex for their shared state, which is locked while the VM is running, and temporarily unlocked for any eventbase
dispatching, osyield
or rtsync
operation. Basically spoken, this happens every time gonuts expects code execution within the same VM state before the respective call returns (called code injections), for example caused by event handlers. Before the call returns, it will lock the VM again.
This functionality may cause lockups in some special situations, which is intended and necessary to resolve improper nesting of event and multithreading capabilites. In a worst case scenario a programmer in fact could run his application into an infinite stall situation. Be careful when nesting these gonuts capabilities, and make sure you understand what is happening under the hoods and which situations may possibly arise in your application. A simple truth when it comes to multitasking is "sh!t happens just because (you did not think twice)".
gonuts uses strings for network addresses, in "the usual human-readable form". That is, you may use (and expect) strings like "127.0.0.1",
"0.0.0.0:8000"
or "192.168.1.17:80"
.
Most methods try to resolve hostnames, so you also may use "localhost"
, "mydomain.com:80"
or such representations you are used to. However, you **must not ** add protocol prefixes like "http://"
Important: gonuts does not yet fully support IPV6. As gonuts grew historically, this is pretty much what people call a PITA...
Event handlers are described in the respective sections of the classes using them. Depending on the type of event they have slightly different prototypes, but they have some common traits.
When called, the Squirrel this
object is always the event handler function itself, hence you cannot access any objects except parameters passed to the function or functions and class definitions in the root table, which is a Squirrel default fallback. Hence you will need to use the bindenv
method on the function to provide a this object in case you need a different behaviour - or you need to access globals using an absolute "path" (using the double colons to reference the root table and "index your way" through the tables/objects).
Within the handler, the callee
function would return the handler function itself, while a stack trace would reveal the eventbase.dispatch line followed by an "unknown" native function and the handler function (gonuts is performing some tricks here and cannot provide proper function names).
Dispatching the calling eventbase loop from within a handler will throw an exception.
Any runtime error or exception occuring will not survive or propagate beyond the event call, however, at the end of the call any exception not catched (try
/catch
) will definitely and inevitably print the callstack (That is, if you set up a timer to call every 2 milliseconds and the handler code is erroneous, excessive error output or logging would occur).
function hostresolve(host)
resolves a hostname string to an IP address string
INPUTS:
host
string. a hostname/ domain name
RETURNS: a string in human readable IP address format, e.g. "127.0.0.1" when resolving "localhost"
THROWS:
The human readable address representation can be passed to network classes and functions. There is no need for a binary representation.
function hostname() ;
returns the hostname of the local platform
INPUTS: none
RETURNS: string
THROWS:
function getmacs()
returns a list of all (networking) interfaces including their hardware address
INPUTS: none
RETURNS: a table, where each key is a network interface identifyer (string) and its value is abuffer
holding the binary hardware address
THROWS: no
you will need to iterate through the table entries using a foreach
loop
function pairedbuffers(base[,options])
creates paired (interconnected) bufferedevent
instances. Each output is connected to the input of the partner, making your app able to "talk to itself" via the pair.
INPUTS:
base
: aneventbase
instance the buffers will be linked to.
options
: integer, option flags - see bufferdevent options
RETURNS: an array with two elements, each holding abufferedevent
instance
THROWS: on input parameter mismatch
Note there is only a single eventbase for both bufferedevent instances created, and they both do not have a socket. Hence when used in multithreading, only one single thread can dispatch. For a multithreading usage, you either must not provideoptions
or provide at leastBEV_THREADSAFE
, or really, really bad things will happen!
options
will default toBEV_THREADSAFE | BEV_DEFER
when omitted. AlsoBEV_DEFER
will always be set, no matter which options you provide.
function pairedsockets()
creates a pair of interconnected sockets (on localhost/127.0.0.1).
INPUTS: none
RETURNS: an array with two elements, each holding asockfd
instance
THROWS: when no socket pair could be created
Similar topairedbuffers
, thesesockfd
instances are connected via localhost and are intended to be used to createbufferedevents
in different threads, which then are able to use differenteventbase
loops and provide means for the threads to talk to each other using a tcp stream over localhost. (Note, there is also thertsync
class providing a FIFO for complex objects which do not require a protocol or manual serialization and deserialization, or use any socket). Important: a firewall may block these paired sockets or prevent their creation, same as kernel issues/limitations may do, both resulting in an exception.
The base for handling events (event loop object). Events may be handled by several base objects in parallel, whereas only one eveentbase per RThread may actually process or loop at a given time.
IMPORTANT NOTE: events are prone to lockups or exceptions when trying to re-enter the same eventbase loop from within an event handler called by that loop.
virtual prototype:
class eventbase { constructor() ; function dispatch(); function dispatchonce(); function dispatchnoblock(); function exit(inms); function break(); function priority_init(prioritiescount) }
eventbase()
dead simple create an event base, no parameters
function dispatch(ms)
run an event loop and dispatch events for a certain time
INPUTS:
ms
time in milliseconds to run the loop. Pass any negative value for infinity
RETUNRS: none
THROWS: on any direct or indirect recursive call
Notes:
osyield
, any eventbase
loop dispatching or most rtsync
operations.function dispatchonce()
runs the event loop exactly one time until a first active and pending event is served. Hence calling exactly one event handler of a pending event or does nothing if none is both active and pending.
INPUTS: none
RETUNRS: none
THROWS: on any direct or indirect recursive call
function dispatchnoblock()
runs the event loop exactly one time, dispatching all active and pending events, if any.
INPUTS: none
RETUNRS: none
THROWS: on any direct or indirect recursive call
function exit([ms])
Will end any dispatch command above after the given time. When time is 0 or negative, it will end dispatching after the current loop cycle is finished.
INPUTS:
ms
integer, optional time in milliseconds until the looping of any dispatch command will end.
RETUNRS: none
THROWS: no
function break()
Aborts the loop so that a running dispatch command will return without serving any event that may still be pending.
When the eventbase is not in its loop and dispatching, this function will have no effect.
Any active and pending event stays pending and will be served by the next dispatch loop
INPUTS: none
RETUNRS: none
THROWS: no
function priority_init(prioritiescount)
sets a range of event priorities supported by the eventbase. When set, event priorities may be set within the range provided.
INPUTS:
prioritiescount
integer, number of priorities supported by the event base
RETUNRS: none
THROWS: no
basically this configures the eventbase to several loop passes, where any event assigned is located in a loop queue corresponding to its priority. Every dispatch loop of the eventbase will run these queues in order.
general event object. An event always is assigned to an eventbase, where it can be active or inactive, and when it is active it may be pending or not. Whether it is active or not is under user control; whether it is pending is controlled by the event source (socket, file descriptor, timer, and/or user control also, see fire
method).
An active pending events callback handler (event handler) is subject to be called by the eventloop when dispatch
ing.
virtual prototype:
class event { constructor(ebase,cbfunc,arg,flags[,evfd]) ; function fire(argflags) ; //trigger function getflags(); function getbase(); function activate(timeout); function deactivate(); function setpriority(prio); function setcallback(func[,arg]); function setcbarg(arg); }
Any event handler function for an event
must accept exactly 4 parameters:
function myhandler(fd,eventtype,eventinstance,argument)
where
fd
is asockfd
instance. It might be an invalid one (containing -1) when there is no file or socket in useeventtype
is an integer value passed by the "cause" of the event, depending on the eventtype it could be a bitmask indicating whether a read or write event occured, or an integer indicating an error, or an integer passed by manually callinevent.fire
(see below). See also table below (event flags)eventinstance
is theevent
instance in questionarg
is the user defined argument passed to theevent
constructor
Any return value of the handler function will be ignored
event(ebase,cbfunc,arg,flags[,evfd]) //when EV_SIGNAL is not set in flags event(ebase,cbfunc,arg,flags,sig[,evfd]) //when EV_SIGNAL is set in flags
where
ebase
instance ofeventbase
to assign this event to
cbfunc
function, callback/event handler function to call when the event is triggered, prototype described above
arg
any type/object. Argument to be passed to the event handler function
flags
integer, bitwise flags determining event type and behaviour, see table below
evfd
optional instance ofsockfd
to assign a socket or file descriptor to the event
sig
integer, mandatory when EV_SIGNAL is set inflags
. See table of signals and predefined constants. Use on linux platform only, on win32 there are no such signals and the event can only be triggered manually.
Creates an event
instance and assigns it to an eventbase
instance. The event instance is inactive and needs to get activated using event.activate
method before any event.fire
or external event (as defined by flags) can be processed. Events will be handled during any dispatch
call on the assigned eventbase
Event flags, except EV_SIGNAL, may be ORed using the pipe operator to combine them
Flag | Effect |
---|---|
EV_SIGNAL |
Listen to a linux process signal, see table of signals |
EV_READ |
pending/trigger when there is data to read on the connected sockfd stream |
EV_WRITE |
pending/trigger when the connected sockfd is ready to write to |
EV_TIMEOUT |
pending/trigger when the timer/timeout elapsed (as passed to event.activate ) |
EV_PERSIST |
enable repetitive triggering; when triggered and handler was called, the event shall remain active in the eventbase |
EV_ET |
enable edge triggering (may be necessary for some hardware related events, will trigger every time the status of the event source changes, be it from 1 to 0 or other way around) |
Note: these are not the values passed to eventhandlers in the eventtype
parameter. See [constants table for bufferedevent
(#eventtypetable) for flags passed to handlers by bufferedevent
or event
instances connected to a socket
function fire(pass)
make an event pending (aka trigger it). If it is active for a certain eventbase
its eventhandler will be called by running (or by future) dispatching of that eventbase.
INPUTS:
pass
an integer value passed to the handler function of the event.
RETURNS: none
THROWS: no
The parameter will be passed as the "eventtype" parameter to the handler function.
function activate(ms)
activate the event in its eventbase. Non-active events will not trigger. Optionally sets a timer for the event to trigger when elapsed (to use as timeout, or to use the event as a timer)
INPUTS:
ms
integer, optional Milliseconds for timeout/timer
RETURNS: bool, wether the event was activated (true) or an internal error occured (false)
THROWS: no
function deactivate()
deactivate the event in its eventbase. The event source cannot trigger the event anymore
INPUTS: none
RETURNS: bool, wether the event was deactivated (true) or is still active (false on error)
THROWS: no
function setpriority(prio)
INPUTS:
prio
integer, positive priority of the event compared to other events in the same eventbase. Note this value must be within the range already defined usingeventbase.priority_init
RETURNS: bool, wether the event was activated (true) or an internal error occured (false)
THROWS: no
function getflags()
returns the flags set on construction of the instance (see event flags above)
INPUTS: none
RETURNS: integer, event flags set on construction of the instance
THROWS: no
function getbase()
INPUTS: none
RETURNS: theeventbase
instance this event is assigned to
THROWS: no
function setcallback(cbfunc[,arg]);
(Re-)Sets the callback function and optionally user argument (see also: constructor)
INPUTS:
cbfunc
the callback function (prototype see above)
arg
optional user argument to pass
RETURNS: none
THROWS:: on invalid input arguments
function setcbarg(arg);
(Re-)Sets the callback user argument. Does not care wether a callback is actually set or not
INPUTS:
arg
the user argument to pass to the callabck function
RETURNS: none
THROWS: no
…A class representing a single stream buffer, optimized for copyless operation on streams, especially utilized by the Event system for I/O operations on files and sockets. sockbufs basically work as a byte FIFO, supporting copy-less operations in the background as well as automatic concatenation of allocated memory blocks through a linked list. They're the most effective type of binary buffer when you need a FIFO only without any seeking or indexing abilities - aka streams.
virtual prototype:
class sockbuf { constructor() ; function enablelocking(); //multithreading precaution function lock(); //multithreading precaution function unlock(); //multithreading precaution function size(); function readln(bytes,EOLtype); function getcontiguous(); function reserve(bytes); function linearize(bytes); function drain(bytes); function remove(len) ; function peek(len) ; function add(data[,len]); function prepend(data[,len]); function write(fd); function read(fd,bytes); }
predefined constants for EOLtype:
constant | type of linefeed |
---|---|
EOL_LF | newline ('\n') character only |
EOL_CRLF | carriage return ('\r') and newline ('\n') in any order |
EOL_CRLF_STRICT | carriage return followed by newline only |
EOL_ANY | any newline, carriage return or both in any order |
sockbuf()
Dead simple, create an empty stream buffer. Unlike buffer
or blob
, as sockbufs are FIFO only, there are no parameters to reserve space or assign data upon construction.
function enablelocking()
enable mutex locking for this buffer. Used internally, but provided to Squirrel in case someone finds a special use case. Note sockbufs cannot be passed to another thread (yet)
function lock()
locks a sockbuf for access. Any subsequent lock will wait infinitely for the buffer to be unlocked. used internally, but provided to Squirrel in case someone finds a special use for this.
When the sockbuf is not enabled for locking, this function won't do anything.
function unlock()
counterpart of lock(), unlocks a sockbuf
function size()
returns the size of the data in the sockbuf in bytes. Note there intentionally is no len() alias, because a sockbuf cannot be indexed
INPUTS: none
RETURNS: integer, total size of data within the sockbuf
THROWS. no
function readln(len,EOLtype)
reads a line from the buffer, where EOLtype defines how line endings will be detected
INPUTS:
len
integer, maximum number of characters to read (must be positive, there is no "unlimited" indication!)
EOLtype
mandatory integer, eitherEOL_LF
,EOL_CRLF
,EOL_CRLF_STRICT
orEOL_ANY
as described above.
RETURNS: a string holding the line read
THROWS: on input parameter failures
function getcontiguous()
returns the maximum amount of bytes a peek
or remove
operation will be most effective (Exceeding this value will involve more copying internally), or when the sockbuf is not split into several memory areas, the amount of bytes readable.
INPUTS: none
RETURNS: integer, size of first memory block in the FIFO
THROWS: none
function reserve(bytecount)
internally allocates memory for a certain amount of bytes to be added without involving further allocations. subsequently added data using add up to this amount of bytes will be located in this contiguous memory area.
INPUTS:
bytecount
integer, number of bytes to ensure add operations will hit the same linear chunk of memory internally
RETURNS: none
THROWS: no
function linearize(bytecount)
internally ensures, by allocations and copying data if needed, that a certain amount of data subsequent drain
, readln
, remove
or peek
methods will operate on, is within a linear chunk of memory.
INPUTS: bytecount integer, number of bytes to linearize within the buffer. Negative values will linearize the whole buffer into a single memory chunk
RETURNS: none
THROWS: no
Note: On large buffers created by many subsequent adds this may be a quite costly operation
function drain(len)
removes bytes from the buffer as if they were consumed by remove
INPUTS:
len
integer number of bytes to remove without actually copying or returning them
RETURNS: none
THROWS: no
function remove([len])
reads bytes from the buffer and returns them in a buffer
instance. This is the standard method for reading data from a sockbuf, do not confuse with read
method (see below)
INPUTS:
len
integer, optional. Number of bytes to read and return. When omitted, all data in the sockbuf is processed.
RETURNS: abuffer
instance holding up tolen
bytes of data read from the FIFO (depending on bytes present in the FIFO), ornull
when there are no bytes in the FIFO.
THROWS: no
function peek([len])
copies bytes from the FIFO without draining/removing them. The sockbuf FIFO stays unchanged and data returned by subsequent drain
, remove
or peek
calls is not affected.
INPUTS:
len
integer, optional. Number of bytes to copy and return. When omitted, all data in the sockbuf is processed.
RETURNS: abuffer
instance holding up tolen
bytes of data as if read from the FIFO usingremove
, ornull
when there are no bytes in the FIFO.
THROWS: no
depending on the memory chunks present in the FIFO this may involve costly copying operations internally. Also see linearize
and reserve
to manipulate internal memory organization.
function add(data[,len])
adds data to the FIFO. This is tha usual operation for adding data not to be confused with write
(see below)
INPUTS:
data
: anystring
,blob
,buffer
or othersockbuf
instance. When asockbuf
instance is passed, you must omitlen
.When a
sockbufis passed it will be drained
and hence empty afterwards
len
: integer, optional. (Maximum) number of bytes to copy fromdata
to the FIFO
RETURNS: integer number of bytes added to the FIFO
THROWS: whenlen
is provided, butdata
is anothersockbuf
instance
Note sockbuf
data amount can not be limited and always all data in it will be used, however such an operation is most effective and not actually copying any data - it simply concatenates internal memory chunks and leaves tha passed sockbuf instance fully drained (=empty).
function prepend(data[,len])
similar to add, but data is added to the 'wrong' end of the FIFO (= LILO operation). Subsequent peek
, drain
or remove
operations will see the passed data first.
INPUTS:
data
: anystring
,blob
,buffer
or othersockbuf
instance. When asockbuf
instance is passed, you must omitlen
, and it will be drained and hence empty afterwards
len
: integer, optional. (Maximum) number of bytes to copy fromdata
to the FIFO
RETURNS: integer number of bytes actually prepended
THROWS: whenlen
is provided, butdata
is anothersockbuf
instance
Again, prepending a sockbuf is an almost copyless operation, but leaves the passed other sockbuf drained (empty).
function write(fd)
writes the whole content of the sockbuf to a socket or file, draining the sockbuf
INPUTS:
fd
asockfd
instance to write data to
RETURNS: integer amount of bytes written, or -1 on any error
THROWS: no
Note that the amount of bytes actually written not only depends on the sockbuf FIFO content, but also on the sockfd. This method tries to write as much data as possible, but does not guarantee to write all FIFO content.
If fd
is configured as nonblocking a return value of -1 possibly means the call would block and should be tried again later. There is no way yet to determine the exact cause of an error return value.
function read(fd,count)
reads data from a socket or file and add it to the FIFO
INPUTS:
fd
asockfd
instance to read data from
count
integer, maximum number of bytes to read from given sockfd. When negative, tries to read as much as possible
RETURNS: integer amount of bytes read, 0 on EOF or -1 on any error
THROWS: no
Same as write, may read less than count
bytes, and also may return -1 when fd is in nonblocking mode and the call would block (which may happen when count is not negative, a positive count WILL try to block until the amount of bytes is read, and hence return an error on nonblocking sockfd
)
On Windows, this represents a network socket only. On Linux, where “everything is a file”, it additionally supports files and provides special functionality to operate on tty (serial) interfaces.
It allows for manual binding of network sockets and provides basic means for DATAGRAM or CAN sockets. However, using sockfd and its recvfrom and sendto methods (for UDP or CAN) is somewhat slower compared to TCP sockets utilizing the bufferedevent
class.
When using sockfd in conjunction with events, you shall use the event system access methods for reading and writing rather than the sockfd
methods, otherwise the event system may behave in an odd manner.
Important note: Many methods are unavailable on win32 platform
Be aware, sockfd is a rather sloppy implementation grown historically. Originally it was intended to be able to pass around file/socket descriptors for the event system only, then to be able to access and configure serial interfaces and latest to make UDP sockets available to the event system. It is not tested for other uses or socket types yet.
virtual protoype:
class sockfd { constructor(); constructor(fd); construcotr(af,prot[,pf]); //file stream methods, linux only function read(count); function write(data[,count]) function openfile(filespec,mode); function ioctl(request,...); //tty/serial methods, linux only function setbaudrate(bps,mode); function TIOGet(); function TIOSet(flags); function TIOClearBit(which); function TIOSetBit(which); //socket methods, all platforms function setnonblocking(); function setreusable(); function setcloseonexec(); function setbroadcast(); function bind(address); function connect(address); //recommended to prefer bufferedevent class function sendto(data,address); function recvfrom([mtu]); function accept(); //recommended to prefer socketlistener class function listen([backlog]); //recommended to prefer socketlistener class }
modes (flags for openfile, to combine using OR operator | ):
O_RDONLY
, O_WRONLY
, O_RDWR
, O_APPEND
, O_CREAT
, O_EXCL
, O_NOCTTY
, O_DSYNC
, O_NONBLOCK
, O_RSYNC
, O_SYNC
, O_TRUNC
TIO bits /flags (modem control)
TIO_RTS
, TIO_CTS
, TIO_DSR
, TIO_DTR
, TIO_RI
, TIO_DCD
socket modes
AF_INET
, AF_UNIX
, AF_INET6
, AF_APPLETALK
and
SOCK_STREAM
, SOCK_DGRAM
, SOCK_RAW
, SOCK_RDM
, SOCK_SEQPACKET
as well as
AF_CAN
, PF_CAN
, CAN_RAW
, CAN_BCM
, CAN_MCNET
, CAN_ISOTP
(not supported by sockfd directly)
sockfd() ;
creates an "empty" instance not connected to any socket or file yet (for use with openfile
)
sockfd(fd) ;
copy constructor, take over the socket or file from another sockfd instance. Both then can operate in parallel
where fd
is another sockfd
instance.
sockfd(af,type[,pf])
create a socket, where
af
integer, address family. PassAF_INET
,AF_UNIX
,AF_INET6
,AF_APPLETALK
orAF_CAN
type
integer, socket type. PassSOCK_STREAM
,SOCK_DGRAM
,SOCK_RAW
,SOCK_RDM
orSOCK_SEQPACKET
pf
integer, optional. Protocol family, passAF_INET
,AF_UNIX
,AF_INET6
,AF_APPLETALK
orPF_CAN
Note: CAN is not really supported by sockfd methods, but present as preparation for a respective den module, where sockfd instances may still be used for passing around only.
function read(count)
attempts to read bytes from a stream. Note this is a blocking call!
INPUTS:
count
maximum number of bytes to read
RETURNS:false
on any error (including EOF), integer 0 when no bytes were read, otherwise abuffer
instance holding the bytes actually read
THROWS: on parameter errors
Note: In case of any error there is no way to retrieve more detailed information about the cause
function write(data[,count])
attempts to write as much bytes as possible to a stream, up to an optionally passed limit
Note this basically also can block.
INPUTS:
data
abuffer
,sockbuf
orblob
instance, or a string
count
maximum number of bytes to write
RETURNS: integer count of bytes actually written, orfalse
in case of any error
THROWS: on parameter errors
Note: In case of any error there is no way to retrieve more detailed information about the cause
function openfile(filespec,mode)
opens a linux file stream.
INPUTS:
filespec
string. file specification as an absolute or relative path
mode
integer, bitflags combined ofO_RDONLY
,O_WRONLY
,O_RDWR
,O_APPEND
,O_CREAT
,O_EXCL
,O_NOCTTY
,O_DSYNC
,O_NONBLOCK
,O_RSYNC
,O_SYNC
and/orO_TRUNC
RETURNS: bool,true
on success,false
on any error
THROWS: no
Trying to call openfile on a sockfd already being a socket or already opened file will fail and return -1
Refer to linux man pages for "open".
Note there is no dedicated close. To close an opened stream you need to simply destroy the sockfd instance.
(Also note there is no flush method! When using actual files the use of Squirrel Standard Lib's file
class is much more convenient). Using sockfd for files is meant to allow for event
or bufferedevent
driven data transfers, especially when it comes to devices like serial ports.
function ioctl(request,...)
performs an ioctl operation on the sockfd file descriptor
INPUTS:
request
integer, the "command" for ioctl. For now, gonuts does not provide enums for that, so refer to the appropriate c/c++ headers of the target OS!
... additional paremeters may be intereger, float, string orbuffer
. Buffers will be double buffered with a maximum size of 4KB.
RETURNS:
either integer in case of an error (ERRNO), or an array of the input parameters, possibly modified by ioctl.
THROWS: no. ATTENTION: In case the system call writes to the parameters, strings should be passed as buffers. POSSIBLE HARD CRASH WHEN SHORT STRINGS BECOME LONGER STRINGS! (or ioctl writes to buffers more than 4KB).
function setbaudrate(baud,mode)
sets the baud (Bits per second) rate and operation mode for a device supporting the feature (aka serial ports)
INPUTS:
baud
integer baud rate. Supports 50,75,110,134,160,200,300,600,1200,2400,4800,9600,19200,38400,57600,115200 and 230400 baud. All other values will cause an error
mode
string denoting data format, for example"8N1"
or"7O2"
. Supports 5,6,7,8 for the first character (data bits), N (no parity), O (odd parity) or E(even parity) for the second and 1 or 2 for the third (stopbits)
RETURNS: true when set, or false on any error
Note: On success this call also disables canonical mode (=line oriented processing), disables blocking and enables raw output.
You will have to handle raw binary data by all means. If you do not want this behaviour you may try to configure the interface using system shell commands instead and leave the sockbuf
unconfigured...
function TIOGet()
return the current TIO flags (for serial interfaces this is modem control usually)
INPUTS: none
RETURNS: integer, curent flags as returned by ioctl with TIOCMGET. Returns 0 when no file descriptor connected
THROWS: no
function TIOSet(flags)
sets tthe TIOFlags, any bitwise ORed combination of TIO_RTS
, TIO_CTS
, TIO_DSR
, TIO_DTR
, TIO_RI
, TIO_DCD
returns true
on success, false
on error
function TIOSetBit(flag)
sets a single bit, any one of TIO_RTS
, TIO_CTS
, TIO_DSR
, TIO_DTR
, TIO_RI
, TIO_DCD
returns true
on success, false
on error
function TIOClearBit(flag)
clears a single bit, any one of TIO_RTS
, TIO_CTS
, TIO_DSR
, TIO_DTR
, TIO_RI
, TIO_DCD
returns true
on success, false
on error
function setnonblocking()
sets the stream to nonblocking operations. Note this introduces additional causes of errors for usually blocking methods of sockfd
.
No parameters, no return value.
function setreusable()
Sets a socket to reuseable mode. That is, the kernel will allow to re-bind to the port/socket in question, preventing an error when trying to do so. Find more information on the Internet about SO_REUSEADDR. (Note this method does not set SO_REUSEPORT!)
Works on win and linux platforms. You need to call this BEFORE any binding to an address.
No parameters, no return value.
function setcloseonexec()
Sets the close-on-exec flag. When forking a child (see systemfork
) that child will usually have access to sockets created by its parent. Setting this flag will close the socket in that case. For gonuts, this basically can be considered a simple security issue for now.
No parameters, no return value.
function setbroadcast()
Allows sending broadcast packets through this socket. Usually used for SOCK_DGRAM (UDP) sockets. Recommended to do BEFORE binding to an address.
UDP sockets are not allowed to send to broadcast addresses for security reasons. To enable a socket to broadcast, you need to call this method on it.
No parameters, no return value.
funtion bind(address)
Binds a socket to an address. The address must be available on the host system.
INPUTS:
address
string address representation as described above
RETURNS:true
on success,false
on any error
THROWS: no
Note: You should prefer event instances (like socketlistener
or bufferedevent
) when it comes to TCP sockets.
funtion connect(address)
connects to a target address
INPUTS:
address
string address representation as described above
RETURNS:true
on success,false
on any error
THROWS: no
Note that this returns immediately, and a TCP stream may not already set up properly by the kernel. Will return true even when the host at the target address is not up, or if it is rejecting the connection.
For TCP sockets you really should prefer bufferedevent
function listen([backlog])
makes the socket listening for connections.
INPUTS:
backlog
integer, optional backlog size. When omitted, backlog is set to 0. See BSD socket documentation about backlogs.
RETURNS:true
on success,false
on any error
THROWS: no
you should have reasons to use this directly on a sockfd, you are recommended to use the socketlistener
class if possible.
function accept()
Accepts an incoming connection, and waits for connections when none is available
INPUTS: none
RETURNS: null on any error, or a freshsockfd
instance holding the new connected socket
THROWS: may
Again, you should prefer using the socketlistener
class and event system if possible, tt's much more effective and less programming involved.
function sendto(data,addr)
send data to an address. This method is meant for use with UDP sockets, but will also work on SOCK_STREAM (TCP) sockets by ignoring addr
.
When possible use event system instead (bufferedevent
)
INPUTS:
data
anybuffer
,sockbuf
orblob
instance, or a string
addr
string target network address,
RETURNS: number of bytes sent, or -1 on any error
THROWS:: no
Note you can not limit the amount of bytes to send, the method always tries to send all of it. Also note, this method does not drain any sockbuf
.
function recvfrom([mtu])
gets data received on the socket. This is a costly call, for TCP you really should prefer using the event system. We cannot repeat this enough.
INPUTS:
mtu
integer, optional. Maximum size of data. Note for UDP this is PER PACKET, and this call may return back more than one packet from a single callRETURNS:
- no error and data is present: an array of packets (or available stream data) where each element is a table with a single entry, the slot name(key) being the source address and the data being a
buffer
holding the data received. You will have to get hold of the sender (slot key) using aforeach
loop instruction.- no error and no data is present:
true
- any error:
false
THROWS: no
a bufferedevent instance is an event object combined with both input- and output-buffer of a stream (socket or file), streamlining event driven stream/socket communications
virtual prototype:
class bufferedevent { constructor(evbase[,flags]); constructor(evbase,sfd[,flags]); function getinput(); function getoutput(); function enable(flags); function disable(flags); function connect(destination); function getfd() ; function write(data[,count]); function read([count]); function readsockbuf([addto]); function setreadcb(fn,arg); function setwritecb(fn,arg) ; function seterrorcb(fn,arg) ; function setcallbacks(readfn,writefn,errfn,arg[,argw,arge]); function settimeouts(read,write) ; }
(eventtype
parameter of handlers)
constant bitflag | meaning |
---|---|
EVENT_READING | data available to read from socket/stream |
EVENT_WRITING | socket/stream ready to write data to |
EVENT_ERROR | socket/stream error |
EVENT_TIMEOUT | timer/timeout elapsed |
EVENT_EOF | streak end of file / socket closing |
EVENT_CONNECTED | socket connected, ready for data transfers |
constant bitflag option | meaning |
---|---|
BEV_CLOSE_ON_FREE | flush and close stream/socket on bufferedevent instance destruction |
BEV_THREADSAFE | bufffer access will be thread safe. You may need this flag for future den modules, gonuts itself cannot pass bufferedevents or its buffers to foreign threads anyways. |
BEV_DEFER | when set, event handlers will be called deferred after all events were evaluated in a eventbase loop run. When not set, handlers will be called immediately while the loop is running |
BEV_UNLOCK_CALLBACKS | usually a mutex for an event is locked when calling an event handler, preventing access to its event instance or the loop. When set, the event system will unlock before calling the event handler |
Use BEV_DEFER to solve synchronization issues regarding events and to control the order of handler calls in case of "parallel" events. Recommended to beginners: Easiest handling when always setting (BEV_CLOSE_ON_FREE | BEV_THREADSAFE | BEV_DEFER)
bufferedevent(evbase)
create a bufferedevent instance, put it into the eventbase
passed and activate it.
the bufferedevent instance is not connected to a socket or file stream
bufferedevent(evbase,flags)
create a bufferedevent instance, join the eventbase
and pass option flags (see table above)
the bufferedevent instance is not connected to a socket or file stream
bufferedevent(evbase,sfd)
create a bufferedevent instance, connect it to the passed sockfd
instance and join the given eventbase
bufferedevent(evbase,sfd,flags)
The most common constructor. Create a bufferedevent
instance connected to sfd
and join evbase
Constructors throwing exception on parameter mismatch
getinput() ;
get the input buffer data from the source socket/stream will be readable from
INPUTS; none
RETURNS: asockbuf
instance being the input buffer of the stream/socket, or null when no data source connected
THROWS: no
Note bufferedevent also provides direct methods to read from this buffer without indirection
getoutput() ;
get the output buffer for writing to the sink socket/stream
INPUTS: none
RETURNS: asockbuf
instance being the output buffer of the stream/socket, or null when no data sink connected
THROWS: no
Note bufferedevent also provides direct methods to write to this buffer without indirection
function enable(flags)
activates event triggers for the callback handlers
INPUTS:
flags
integer bitflag, an ORed combination ofEV_READ
and/orEV_WRITE
constants
RETURNS: integer 0 on success, -1 on error
THROWS: no
function disable(flags)
deactivates event triggers for the callback handlers
INPUTS:
flags
integer bitflag, an ORed combination ofEV_READ
and/orEV_WRITE
constants
RETURNS: integer 0 on success, -1 on error
THROWS: no
function connect(address)
initiates a socket connection to a peer
INPUTS:
address
string address representation as described above (e.g. "127.0.0.1:1234" or the like)
RETURNS:true
on success,false
on error
THROWS: no
Note a return value of true
indicates the initiation of a connection, not the success of the connection. When connection succeeds or fails, the event handler will be called with the respective EVENT_CONNECTED
, EVENT_ERROR
or EVENT_TIMEOUT
flags
function getfd() ;
returns the sockfd instance the bufferedevent is connected to.
INPUTS: none
RETURNS: asockfd
instance (in all cases!)
THROWS: no
note when the bufferedevent
instance does not have a socket or stream, this method still will return a sockfd
instance being an "empty" sockfd
function write(data[,count])
writes data to the socket/stream (via the output buffer)
INPUTS:
data
: anyblob
,buffer
orsockbuf
instance or a string
count
: integer, optional. Number of bytes to write
RETURNS: the byte count actually written
THROWS: when data is asockbuf
and count is provided, or any other parameter error occurs
This is the preferred method of writing data to a socket
Note: Best performance when passing a sockbuf
instance. Note a sockbuf
passed will be drained accordingly
function read([count])
reads data from the socket/stream input buffer
INPUTS:
count
integer, optional. Maximum number of bytes to read
RETURNS: abuffer
instance, ornull
when no data is available
THROWS: no
will try to read all available data when omitting count
.
Actual byte count read may be less than provided by count
function readsockbuf([add])
reads data from the socket/stream and return or add it to a sockbuf
INPUTS:
add
optional, a sockbuf instance to add the data read to
RETURNS: asockbuf
instance (whenadd
is provided will be the same instance)
THROWS: no
the most performant way of reading data from a socket/stream
function setreadcb(fn,arg)
sets the event handler to call on read events
INPUTS:
fn
a function object of the event handler (see below) ornull
do disable the callback
arg
any variable to pass to the handler as user argument
RETURNS: none
THROWS: no
function setwritecb(fn,arg)
sets the event handler to call on write events (that is, when the socket or stream is ready to process output data)
INPUTS:
fn
a function object of the event handler (see below) ornull
do disable the callback
arg
any variable to pass to the handler as user argument
RETURNS: none
THROWS: no
function seterrorcb(fn,arg)
sets the event handler to call on other/error events (including outgoing TCP connection established)
INPUTS:
fn
a function object of the event handler (see below) ornull
do disable the callback
arg
any variable to pass to the handler as user argument
RETURNS: none
THROWS: no
function setcallbacks(readfn,writefn,errfn,arg[,argw,arge]);
sets all callback handlers at once
INPUTS:
readfn
function object for read events ornull
do disable the callback
writefn
function object for write events ornull
do disable the callback
errorfn
function object for state/error events ornull
do disable the callback
arg
any variable to pass as user argument to the read handler, or all handlers whenargw
andarge
omitted
argw
optional, any variable to pass to the write handler as user argument. When omitted,arg
is used
arge
optional, mandatory whenargw
is provided. Any user variable for the error handler. When omitted,arg
is used
RETURNS: none
THROWS: on input parameter errors
note you may provide a single user argument for all three types of callbacks or three user arguments for each type of handler, but not two user arguments.
function settimeouts(readto,writeto)
sets and activates or deactivates timeouts for reading and/or writing
INPUTS:
readto
: integer, milliseconds read timeout, when 0 or negative deactivates read timeout events
writeto
: integer, milliseconds write timeout, when 0 or negative deactivates write timeout events
RETURNS: none
THROWS: no
event handler functions for a read and/or write events of a bufferedevent
must match this prototype:
function onReadOrWrite(bev,argument)
where
bev
is thebufferedevent
instance causing the eventarg
is the user defined argument
note there are no flags, as bufferedevent will use different handler callbacks for reading and writing
Any return value of the handler function will be ignored
a simple tcp echo server would use such a handler:
function onRead(bev,arg) { local data=bev.read() ; //read all data available bev.write(data) ; //and write back to output }
or, most performant:
function onRead(bev,arg) { bev.write(bev.readsockbuf()) ; }
A note on write event handlers: write events will be triggered when a socket or stream gets ready to send data, not when data is actually sent.
an error event handler must follow this prototype:
function myerrorhandler(bev,state,argument)
where
bev
is thebufferedevent
instance in questionstate
is an integer bitflag mask indicating the error, see below
Any return value of the handler function will be ignored
state
has bits sets depending on the cause, matching the constants EVENT_READING
, EVENT_WRITING
, EVENT_CONNECTED
, EVENT_EOF
, EVENT_ERROR
or EVENT_TIMEOUT
, where the lower bits indcate further information (reading or writing) and the upper 4 bits usually match exactly ONE of connected, eof, error or timeout.
EVENT_ERROR
is indicating any other error not listed, usually some socket error.
Usually, one one of the upper 4 bits is set and additionally none, one or both bits for reading and writing in teh lower bits is set to indicate the operation on wich the error indicated by one of the upper 4 bits.
Hence, you may check the error for example as following:
function onError(bev,state,arg) { switch (state&0xf0) { case EVENT_CONNECTED: //TCP socket connection established, do something break ; case EVENT_EOF: //peer terminated the connection (or end of file), do something else break ; case EVENT_TIMEOUT: if (!(state&0x0f)) { /*probably unable to connecto to peer*/ } if (state&EVENT_WRITING) { /*write timeout*/ } if (state&EVENT_READING) { /*read timeout*/ } break ; default: //any other error, if (state&EVENT_WRITING) { /*occured when trying to write*/ } if (state&EVENT_READING) { /*occured on reading data*/ } if (!(state&0x0f)) { /*spurious socket error*/ } break ; } }
As you may note an "error" event is also triggered when a tcp connection is established after you called connect
Setting up a socket to listening mode and operating “accept” on it, causing an event for each connection
virtual prototype:
class socketlistener { constructor(evbase,addr_or_fd[,flags]) function enable() ; function disable(); function getfd(); function isbound(); function onaccept(fn[,arg]); }
constant bitflag | effect |
---|---|
SLI_BLOCKING_SOCKETS | when set, all sockets accepted will be in blocking mode, otherwise nonblocking (default). Note a blocking socket will have massive adverse effects when further used in the event system |
SLI_CLOSE_ON_FREE | when set the listening socket will be freed when the instance is destroyed |
SLI_CLOSE_ON_EXEC | when set the listening socket will have the close-on-exec flag set(see sockfd.setcloseonexec) |
SLI_REUSEABLE | the listening socket will have SO_REUSEABLE set see sockfd.setreuseable), allowing faster re-binding to the same socket. Very useful when you're debugging and need to start over |
SLI_THREADSAFE | the socketlistener will be threadsafe. At the time of this writing it is unclear whether socketlistener instances can be passed to foreign threads,but if that's actually or will be possible in the future , you should set this flag to avoid hard crashes. |
Beginners should always set (SLI_CLOSE_ON_FREE | SLI_CLOSE_ON_EXEC | SLI_REUSEABLE | SLI_THREADSAFE)
socketlistener(evbase,addr_or_fd[,flags])
create an activated socketlistener, assign it to en eventbase and activate it
where
evbase
is theeventbase
instance to assign. The socketlistener requires the event system for asynchroneously accepting incoming connections
addr_or_fd
is either a network address string or asockfd
instance (see notes below) and
flags
is an optional integer bitflag, ORed from the constants described above. When omitted defaults toSLI_CLOSE_ON_FREE|SLI_CLOSE_ON_EXEC|SLI_REUSEABLE|SLI_THREADSAFE
The socketlistener is automatically enabled.
Note the constructor will bind a socket when you pass a network address string (for example "0.0.0.0:80"), but in case you pass a sockfd
instance you need to manually call bind
on it (if possible BEFORE creating the socketlistener). Additionally the flags
will not affect the sockfd instance and its socket in this case, so you will have to configure thease manually, too.
function enable()
enables the socketlistener to accept new incoming connections. Note on construction, a socketlistener instance already is enabled.
function disable()
disables the socketlistener, and it will not accept further incoming connections.
function getfd()
return a sockfd
for the socket the listener acts on
INPUTS: none
RETURNS: asockfd
instance ornull
THROWS: no
function onaccept(fn[,arg])
sets the callback function for accepting incoming connections, including an optional user argument to pass
INPUTS:
fn
the handler function, mandatory to be of prototype described below
arg
optional, any object or variable to pass to the handler as user argument. defaults tonull
when omitted
function onAccept(listener,fd,peeraddr,arg)
where
listener
is the socketlistener instance accepting an incoming connection,
fd
is asockfd
instance of the newly created socket connected to the peer
peeraddr
is a string holding the peer network address (and port) and
arg
is the user defined argument
this class handles http connections.
… HERE BE DRAGONS ::[TODO]::
virtual prototype:
class httpevent { constructor( ... function setgencallback( ... function setcallback( ... function encode( ... }
…
Note: This class is not considered safe. Peers may abuse implementation flaws.
… HERE BE DRAGONS ::[TODO]::
virtual prototype:
class httprequest { constructor( ... function getURI(); function getcommand(); function gethost(); function getpath( ... function getheaders( ... function addreplyheader( ... function addreply( ... function discardreply(); function reply( ... function error( ... function cancel(); function encode(string); function getinputstring( ... function getinputblob( ... function getinputarray( ... }
The random class provides much more secure random number generation than the rand function included from Squirrels stdlib, which is required by many crypto actions.
However, it provides two different generators to gives the user a a speed-vs-security tradeoff choice when used manually (Cryptologic classes and functions will always use the more secure Yarrow generator though)
virtual prototype:
class random { constructor() ; function seed(data) ; function number(upto); function yarrownumber(upto); function buffer(size); function yarrowbuffer(size); }
random() ;
no parameters. The constructor will seed both random generators automatically with some entropy (about 128 bytes, partly derived from system time)
function seed(data)
seeds both random generators with the provided data. Use to increase entropy. may be used any time, even recursively (that is, provide seed from the same or other random generators)
INPUTS:
data
any blob, buffer, sockbuf or string to seed from.
RETURNS: none
THROWS: no
function number(modulo)
get a single random integer from the fast random generator
INPUTS:
modulo
integer, the modulo value.
RETURNS: a random integer in the range of 0 tomodulo-1
TROWS: no
function yarrownumber(modulo)
get a single random integer from the Yarrow random generator
INPUTS:
modulo
integer, the modulo value.
RETURNS: a random integer in the range of 0 tomodulo-1
TROWS: no
function buffer(size)
gets a buffer of given size filled from the fast random generator
INPUTS:
size
integer, buffer size in bytes
RETURNS: abuffer
instance of sizesize
holding the random data
TROWS: no
function yarrowbuffer(size)
gets a buffer of given size filled from the Yarrow random generator
INPUTS:
size
integer, buffer size in bytes
RETURNS: abuffer
instance of sizesize
holding the random data
TROWS: no
Class providing ECC and AES operations
virtual prototype:
class crypt { constructor(); //ECC functionality function createECC(type,rnd); function getECCprivkey(); function getECCpubkey(); function setECCkey(key); function ECCdecrypt(enckeydata); function ECCencrypt(keydata); function ECCsignhash(hash,rnd); function ECCveryfysig(sig,hash); //AES functionality function setAESkey(key,iv); function getAESIV(); function encrypt(data); //note: inline encryption, altering data! function decrypt(data); //note: inline decryption, altering data! }
ECC112
, ECC127
, ECC160
, ECC192
, ECC224
, ECC256
, ECC384
, ECC512
used for ECC key creation (see createECC
)
crypt()
Dead simple. Just create, no parameters. No keys assigned
function createECC(keysize,rnd)
create a new random ECC key pair you may read using getECCprivkey
and getECCpubkey
INPUTS:
keysize
integer defining the key size, pass any of aboveECC112
toECC512
constants
rnd
arandom
instance the function will use for entropy
RETURNS:true
when the key pair was created,false
on error
THROWS: on input parameter errors
function getECCprivkey()
gets the current private ECC key (if any)
INPUTS: none
RETURNS: abuffer
instance holding the key ornull
when no private ECC key is present
THROWS: on input parameter errors
function getECCprivkey()
gets the current public ECC key (if any)
INPUTS: none
RETURNS: abuffer
instance holding the key ornull
when no public ECC key is present
THROWS: on input parameter errors
function setECCkey(key)
sets the ecc private and public or just public key
INPUTS:
key
abuffer
instance holding either private or public key. Note a private key always also contains the corresponding public key!
RETURNS: true when the key(s) was/were set, false when any error occured (e.g.key
did not contain valid key data)
THROWS: on input parameter errors
function ECCdecrypt(data)
decrypts a key or hash (for example an aes key) encrypted using ECCencrypt
, using the current private key
ECCEncryption is NOT meant to encrypt any data, but rather other keys, hashes or initialization vectors.
INPUTS:
data
abuffer
instance holding encrypted data to decrypt
RETURNS: abuffer
instance holdeing decrypted data ornull
on error
THROWS: on input parameter error
function ECCencrypt(data)
encrypts a key or hash (for example an aes key) encrypted using ECCencrypt
, using the current public ECC key. Only holders of the corresponding private key may decrypt the data.
ECC encryption is not suitable for encryption of any data, but rather to encrypt other keys, hashes or initialization vectors.
INPUTS:
data
abuffer
instance holding the binary data to encrypt, usually an AES key, a hash or the like.
RETURNS: abuffer
instance holding the ECC encrypted data ornull
on error
THROWS: on input parameter error
function ECCsignhash(hash,rnd)
signs a hash (or key) using the current private ECC key
INPUTS:
hash
abuffer
instance holding the hash to sign
rnd
arandom
instance the function derives necessary entropy from
RETURNS: abuffer
instance holding the ECC signature for the provided hash
THROWS: on input parameter error
function ECCveryfysig(sig,hash);
checks a signature created by ECCsignhash for validity against a given hash using the current public ECC key, veryfying the hash and signature match and the signature was created using the proper private key.
INPUTS:
sig
abuffer
instance holding the signature as created byECCsignhash
hash
abuffer
instance holding the hash that was signed
RETURNS: integer 0 when hash, signature and key match, integer 1 when they do not match or -1 in case of any error
THROWS: on input parameter error
function setAESkey(key,iv)
sets an AES key and initialization vector.
INPUTS:
key
abuffer
instance holding the AES key to set ornull
to not set a new key. Minimum size 16 bytes
iv
abuffer
instance holding the initialization vector to set, or null to not set a vector. when provided, vector size must match key size
RETURNS:true
on success,false
on any error (usually key size mismatch)
THROWS: on input parameter mismatch
Important note: only 16,24 or 32 bytes data sizes supported for key and iv, any excess bytes will not be taken into account. The iv must be at least of the same size as key.
function getAESIV()
returns the current IV state. encrypt and decrypt will change the iv, so this function does not literally return the "initialization" vector, but the current crypt vector state
INPUTS: none
RETURNS: abuffer
instance holding the current vector state between any encryption or decryption step
THROWS no
function encrypt(data)
performs an AES encryption on data using the current AES key and IV. Progresses IV state internally to provide chaining capabilitie
INPUTS:
data
anybuffer
,blob
orsockbuf
instance or string to encrypt
RETURNS:null
on error, otherwise the input object
THROWS: when no AES key is set, parameter errors
Note this function encrypts in place, the input buffer data is altered!
(including any passed string. It is important to know that a null character at any position may occur and cause inadvertent string termination to some functions the string is applied to later)
function decrypt(data)
performs an AES decryption on data using the current AES key and IV. Progresses IV state internally to provide chaining capabilitie
INPUTS:
data
anybuffer
,blob
orsockbuf
instance or string to encrypt
RETURNS:null
on error, otherwise the input object
THROWS: when no AES key is set or on parameter errors
Note this function decrypts in place, the input buffer data is altered!
(including any passed string. It is important to know that a null character at any position may occur and cause inadvertent string termination to some functions the string is applied to later).
while the hash
function (see below) is easy and convenient to use for single blocks of data, you may encounter situations where you want to hash data chunks one by one for a single hash.
This class allows you to process any sequential data in arbitrary steps.
virtual prototype:
class hashstate { constructor(algo); function process(data); function done(); }
hashstate(algo)
create a hashstate instance for the given hash type, where
algo
is a string identifying the hash function to use. Note it does NOT support the"integer"
hash type and will throw an exception when the string parameter does not match any of the other standard hash types as set out in the table below (supported hash types).
function process(data)
continue processing input data
INPUTS:
data
anyblob
,buffer
,sockbuf
or string input
RETURNS: none
THROWS: on input parameter error
function done()
end processing and return the hash object resulting from all preceding process
operations. Resets the state, subsequent process operation will be input to a new hash
INPUTS: none
RETURNS: abuffer
instance holding the hash
THROWS: no
function hash(data,algo)
calculates a hash of data using the provided algorithm
INPUTS:
data
anyblob
,buffer
,sockbuf
instance or string to build the hash for
algo
a string indicating the hash algorithm (see below)
RETURNS: type depends on hash algorithm, most hashes return abuffer
instance.
THROWS: no
Note: in current gonuts, when the indicated hash algorithm is not available or mistyped, the hash algorithm defaults to "integer"
as a fallback and the function returns an integer hash.
These hash types are currently supported by gonuts:
"sha512"
(recommended for encryption usage)
"sha384"
"sha256"
"sha224"
"sha1"
(do not use for encryption)
"md5"
(do not use for encryption)
"whirlpool"
"rmd128"
(RIPEMD)
"rmd320"
(RIPEMD)
and
"integer"
, an ultra fast hashing algorithm returning an integer hash (64 or 32 bit, depending on platform), optimized to hash strings. It significantly (but linearly) loses precision for every 32 bytes of input data. Not available to the hashstate
class
sha256(buf)
is simply a convenience shortcut for Hash(buf,”sha256”)
function PKCS5(passw,salt[,hash[,iterations]])
performs a PBKDF2 operation to derive a cryptographic key or IV from a password, a salt and a hash.
INPUTS:
passw
a string, buffer, sockbuf or blob defining the password
salt
a string, buffer, sockbuf or blob defining the salt
hash
string, optional hash type (see table above), defaults to sha256 when omitted
iterations
integer, optional number of iterations, defaults to 5000
RETURNS: abuffer
holding the generated key
THROWS: on any error
As it's performing PKCS#5 as PBKDF2, the output may longer than the hash type output, and the salt is of any size (where salts below 8 bytes are not considered safe against rainbow table attacks)
The number of iterations, put simpliflied, defines the overall security and is meant to adjust to increasing computational power over time. See https://en.wikipedia.org/wiki/PBKDF2 for more details.
Creates an HMAC from a key and data using specified hash algorithm
Attention: You can set the key ONCE in constructor, and when calling done()
the key will change to the calculated HMAC while the hash is reset. This means, for he current HMAC instance calling done()
is similar to creating a new instance with the result of the last as its key, similar to hmacinstance=HMAC(algo,hmacinstance.done())
. This is very useful for concatenations like for example Amazons AWS APIs / SP-API requires. However, you cannot change the key on an instance manually after creation. To do so, create a new instance (and throw away the old one if no longer needed)
virtual prototype:
class HMAC { constructor(hashalgo,key); function process(data); function done(); }
HMAC(hashalgo,key)
create a HMAC instance for the given hash type and key, where
hashalgo
: a string identifying the hash function to use. See hashstate class or hash function. Note it does NOT support the"integer"
hash type and will throw an exception when the string parameter does not match any of the other standard hash types as set out in the table below (supported hash types).
key
: string, buffer, blob or evbuffer holding the key for HMAC creation
function process(data)
feed input data (chunks)
INPUTS:
data
: anyblob
,buffer
,sockbuf
or string input
RETURNS: none
THROWS: on input parameter error
function done()
end processing and return the HMAC resulting from the key and all preceding process
operations. Resets the state, subsequent process operation will be input to a new HMAC, where it's is the HMAC just returned (for concatenation). See remarks above
INPUTS: none
RETURNS: abuffer
instance holding the hash
THROWS: no
Squirrel has implemented threads in a fully cooperative way, allowing you to create a thread, where the thread can suspend itself and you need to resume it manually. Those built-in threads basically represent a separate stack, operating on the same root table.
However, they’re no real threads in terms of CPU/Operating system threads, but rather provide functionality for generators (see Squirrel language documentation about generators)
Gonuts introduces an additional “Real thread” mechanism, allowing you to leverage the full power of multitasking.
Usually based on pthreads, real threads represent a totally independent Squirrel context (completely separated memory, root table, stack and state).
To exchange data between real threads you will need the rtsync
class providing the one and only mean to exchange data and interact (unless you want to use sockets for that, see below).
This mechanism can not interoperate between different gonuts processes, to have different gonuts processes communicate you will need to use localhost sockets (or when spawning a gonuts child you may use pipes as well)
function osyield()
Yields the current “real” thread (preemptive multitasking), giving control back to the OS, which then usually immediately schedules any thread it deems viable. Do not confuse with Squirrels' yield
keyword
Creates an OS thread and absolutely separate context (including memory) to load a nut/cnut and run its threadmain function.
Creaton and starting the thread is a single action performed upon instanciation.
Virtual prototype:
class realthread { constructor(script[,prio][,...]) ; }
Create and start a new thread
realthread(script[,prio][,...]) ;
Parameters:
script
- string. A script name/filename as described forscript.require
.
prio
- integer, optional. Thread priority, clamped from -2 to 2. Default is 0. Lower values are lower priority.
...
- any number of arguments to pass to the thread. Arguments must be intrinsic primitive or strings, the only allowed class instance as an argument is anRTSync
instance. See below for the threads main function prototype and how parameters will be passed.
Notes:
prio
is optional, that means when the first "argument" parameter is an integer, it won't be passed to the thread as an argument but taken as thread priority.NOT
terminate the thread on its own, it will just wait infinitely for the "child thread" to be terminated.script
may also be a function object instead of a string. The thread then won't load a script and execute its threadmain, but simply will execute the passed function. When the function returns, the thread is terminated.
When the calling thread terminates immediately after creating the new thread, there is a probability for a hard crash terminating the gonuts process.
function threadID();
returns the (yet pretty much useless) thread ID
INPUTS: none
RETURNS: integer thread ID
THROWS: when parameters passed only
returns whether the thread is running or terminated
function running();
INPUTS: none
RETURNS: bool (false=thread is terminated)
THROWS: when parameters passed only
infinitely waits for a thread to terminate
function join();
INPUTS: none
RETURNS: none
THROWS: when parameters passed only
to be exact, it waits for the thread to not run. Hence immediately returns when the thread already terminated.
Note that this method will not cause the thread to terminate, user has to provide thread termination mechanisms separately. This function simply will block as long as the thread is running.
The script loaded and executed in the newly created has to define an entry point:
function threadmain(params)
INPUTS:
param
array, where each element is an argument taken from the arguments passed to theRealThread
command that started the current thread.
RETURNS: any return value will be discarded. Thread terminates upon return
A class to create synch objects for communication between RThreads.
This is the one and only object type in gonuts referencing an internal memory object accessible for different threads and handles signalling, locking and inter-thread data transfers.
Data transfer is somewhat limited but still versatile, and can be performed both using indexes or FIFO pipelines.
Note all data transfers involve internal data copies and mutex accesses (spin locking allowed).
Also note not all class instance types can be transferred, only cloneable instances can be transferred.
Most important though, you may safely transfer a buffer
or sockfd
instance.
At the time of this writing it is not clear wether a sockbuf
or bufferedevent
can be transferred - however, in case that is actually possible or become possible in the future, you MUST take manual multithreading precautions (utilize BEV_THREADSAFE
flag for bufferedevents, for example, and the enablelocking', lock
and unlock
Methods of sockbuf instances, or utilise rtsync.lock
, trylock
and unlock
manually to protect them against concurrent access from several threads)
All data copying will happen when data is read.
When a thread shuts down, all squirrel objects in any RTSync object will be set to null unless they're intrinsic primitives (e.g.integer, float, bool, but not including strings in this case!).
Users should regard any subsequent read of such data in another thread hence will be null.
Also note, on Win32 the signalling mechanism may produce weird results when several threads are listening to the same signal. We therefore recommend to use RTSync objects in a thread-to-thread manner and not for broadcasting, though gonuts widely mitigates any such issues arising from Windows' WaitForSingleObject implementation.
Important note:
When you registered a signal event using setsignalevent
the registered event will not survive the case that no thread references the channel (= no VM has an rtsync object for the channel). Though it is convenient to be able to "summon" and discard an rtsync at will, all of its data will be lost when noone currently "holds" an rtsync object for that channel. Hence, when at least one thread holds a permanent reference, then for other threads it seems like magically its event, slots, pipe and and signal state persist beyond the lifetime of an rtsync
Squirrel instance.
virtual prototype:
class rtsync { constructor(name) ; function getname() ; function cleanup() ; //indexed transfers function setvar(idx,obj) ; function getvar(idx,obj) ; //FIFO transfers function push(obj,int) ; function pop() ; //signalling function setsignalevent(eventobj, arg); function signal() ; function wait(ms) ; //mutex functionality function lock() ; function trylock() ; function unlock() ;
rtsync(name)
Create an rtsync instance in squirrel for a channel identified by name
(which must be a string). When creating, the instance will either create or refer an already existing rtsync of the same name, depending whether an instance already exists for that very name. Note name is case sensitive, exact match is required. You may define as many channels as you like, and there are no predefined channels.
function getname()
returns the channel name of the instance
INPUTS: none
RETURNS: string, sync channel identifier (name)
THROWS: no
function cleanup
performs a garbage collection, freeing all memory still allocated by objects that are not accessible anymore.
Note that setvar
, getvar
, push
and pop
also internally perform a cleanup, as a VM shutdown (Thread termination) will.
INPUTS: none
RETURNS: none
THROWS: no
You won't need this function unless you want to avoid too much excess data is piling up in certain situations, slowing down any data transfer causing method.
function setvar(idx,obj)
stores a value or object in a specified channel slot. Slots may be accessed by all threads (provided they have instanciated an rtsync for that channel name)
INPUTS:
idx
: either an integer index or a string (for a named slot) indicating the slot to use
obj
: Any primitive value (integer, float, bool, null), string, table, array or gonuts instance, esp.buffer
orsockfd
.
ATTENTIONobj
must not include circular references, or a hard crash definitely WILL happen
function getvar(idx)
retrieves a value or object from the specified channel slot. This might be a costly operation not only involving mutexing, but also cloning especially for large buffer
objects, tables and arrays.
The content of the slot is read, but not changed.
INPUTS:
idx
: either an integer index or a string (for a named slot) indicating the slot to use
RETURNS: the stored object or value, ornull
when the slot does not exist. Note you cannot tell the difference from a slot actually storingnull
or not present.
THROWS: no
Note: To be exact about nonexisting slots, getvar will indeed CREATE a null object in an unused slots, which effectively uses up some small amount of memory.
function push(int,obj)
Enqueues a pair of an integer value and a Squirrel object (or primitive) on the channel FIFO
INPUTS:
int
integer, meant as the type of entry. If possible do not use 0 or negative numbers, which are dedicated to pxlib.pnut functionalities
obj
Any primitive value (
integer, float, bool, null), string, table, array or gonuts instance, esp.buffer
andsockfd
.
RETURNS: none
THROWS: on wrong input parameters
ATTENTION obj
must not include circular references, or a hard crash definitely WILL happen
the integer value is for the receiving thread to distinguish different types of obj interpretation.Anyways, rtsync does not care about any interpretation, it simply provides a FIFO queue to pass pairs to other threads (or even the same)
function pop()
Tries to pop a integer-object pair from the channels' FIFO queue.
INPUTS: none
RETURNS:false
when there is nothing to pop, or a table containing two entries, keyed in slots "t" holding the integer value of the pair and "o" holding a deep clone of theobj
of the pair.
THROWS: no
function setsignalevent(eventobj)
disables or sets an event instance to trigger when the channel is signalled (see below)
INPUTS: eventobj either null to disable or an event instance to fire on receiving a channel signal
RETURNS: none
THROWS: no
function signal()
signals the rtsync, waking up threads wait
ing for a signal on the same channel, and fire/trigger all events in all channels linked to the channel via setsignalevent
in their respective thread
Note on win32 the wait mechanism is somewhat limited and may show odd behaviour.
INPUTS: none
RETURNS: none
THROWS: no
function wait(ms)
waits for a signal on the sync channel. Premature return may occur, check return value
INPUTS:
ms
maximum time in millisecond to wait. infinite when negative
RETURNS: false on a spurious wakeup, true for ONE thread waiting when this channel was signalled
THROWS: no
Note that wait will also return true when the channel was signalled before calling this method, however such a stored signal will not abort waiting time. So be careful (you may want to call passing 0ms first to check for any "premature" signals first)
Remember on win32 the signal/wait mechanism is somewhat limited and may show odd behaviour, like waking up only SOME threads.
For that very reason we recommend using this mechanism on a "thread-to-thread" basis to avoid any such platform differences. When only two threads involved (one waiting, one signalling) all platforms will deliver the expected results.
However, wait entsures in case several threads are waiting on the same channel only one single thread gets the return value of true
.
Note that this is not true for signal events, which are triggered for all threads having registered an event for the channel. So when you use rtsync to receive for example a global signal (like a shutdown signal) you should catch the signal using an event, while in case you need to distribute workload on worker threads you should use something along while (!syncobj.wait(-1)){};
to ensure only one worker thread wakes.
Passing zero will simply clear the signal flags and check wether the calling thread is the one and only meant to wake up, without any delays involved (for use in event handlers, to check wether to take action or not, for example).
The documentation below this point is to be considered work in progress and surely is inaccurate and incomplete. However, all functionality indicated is implemented and proven to work.
The PXProtocol is highly versatile. Each Packet has a single “token”(Identifier) and zero to literally unlimited data blocks of several types and again literally unlimited size, while having nearly no overhead. It can be used over any line, be it I2C, packet driven layers like CAN or UDP or within any stream, e.g. TCP. Ultimately, it allows for CPU-less dedicated network or endpoint hardware devices (using latches, shifters and comparators).
However, as it is highly (mis-)configurable and proprietary the PXProtocol class is not documented here directly. pxlib.pnut offers an API to use the protocol.
The following table shows mandatory token usages and restrictions when using one of the default configurations schemes.
Token(s) Usage/Restriction
0 to 15 Reserved for internal protocol use (private/peer use)
16-8191 registered protocols (#1) (public + private)
8192-16383 Publicly free for applications (#2) (private, public with special care)
16384-32767 Reserved for internal protocol extensions (public use)
(#1) Registered protocols have predefined public functionality, for example ping, file transfer or RPC.
Note: not mandatory to support them.
(#2) When using them in public networks or when interoperating with foreign party peers you need to provide additional measures to mitigate unmet expectations, for example by using the logon/application id.
Application identifiers should be registered with us to avoid coincidental matches. This is mandatory for these types of identifiers:
• Positive 64 bit integer values (MSB not set)
• Positive 32 bit integer values (MSB not set)
• 16 bit integer values < 32768 (0x7FFFF and below)
• strings that do not begin with “VOID” or “A”
• 8 bit values below 240 (note: functional grouping applies)
You may use the following identifiers without registration, but risking coincidental matches and hence you need to provide additional measures to mitigate unmet expectations about received and sent protocols. Note: You will be unable to interoperate with registered applications.
• Negative 64bit integer values
• Negative 32bit integer values
• Strings starting with “A”
For development purposes you may use any 8 bit value from 240 to 249 or any string starting with “VOID” as long as you ensure there is no connection/interoperation with production systems, especially 3rd party ones and when using 8bit identifiers (which, by the way, are dedicated to busses like I2C).
…Besides buffer
this is the only class that can be serialized natively or sent through pxprotocol in Squirrel Object Mode. As the name indicates, it is a marker only with the simple functionality to just exist.
use pxlib.nut to access the protocol
use pxlib.nut to configure the protocol
…
…
Classes to read, create and process XML files
Note: Due to the complex nature and long method names, naming conventions deliberately differ on capitalization.
However, pxlib.pnut provides far less complex accessibility to XML files by mapping them to Squirrel tables.
You may want to use the library and have a look at its doc first, unless you need to handle specialized functionality not supported by the pnut library.
Most classes are inherited from CMLNode and hence share its methods
The general node class most XML classes inherit from
Extends SQXMLNode
Represents an XML Document
This is an independent class NOT extending a node.
It holds a single attribute of an element
extends SQXMLNode
…
extends SQXMLNode
extends SQXMLNode
extends SQXMLNode
extends SQXMLNode
XML_ELEMENT, XML_UNKNOWN, XML_COMMENT, XML_TEXT, XML_DECLARATION, XML_DOCUMENT
Data Compression (inflate&deflate)
Utilities for gonuts installations. gonuts has no need for installation, but when it is these utilities can be used for automation of include paths and overall installation environment handling.
Doc: TODO
Additional convenience classes and functions optionally can be loaded at runtime from the pxlib.pnut archive by manually adding the archive and loading the “pxp” script from it.
WIP - still lots of documentation work to do...
...
function base32encode(input) function base32decode(input) function FindSquirrelFunction(path,start=null) function IterateSquirrelPath(path,create=true,start=null) function CopyFile(sourcefilepath,targetfilepath,chunksize=1048576) function strreplace(source,what,by) function FixPath(input,asfolder=false) function GetFilePath(inputpath) function GetFileName(inputpath) function GetFileExtension(inputpath) function StripExtension(inputpath) function Serialize(obj) function DeSerialize(inpstring) function wiperoottable() function findadirectory(searchpath,dirspec,recursive=false) function findafile(searchpath,filespec,recursive=false) function GetAllFiles(searchpath,filespec,recursive=false) function FileHash(filepath,chunksize=65536) function MergeTables(destination,source,except=null) function deepclone(obj) function Sleep(ms,loop=null,loopms=100) function ReadCSV(filepath,linesep="\n",columnsep=";",stringdelimiter="\"",headerline=false)
…
function generic(data,parent) ; function XML2SQ(node,generic=1) ; function Document(rootelement) ; function WriteXML(xmlin,filepathout) ;
…
function readjson(string_input) function tojson(elem,indent="",cr="\n")
…
class pnutarchive { constructor(filename); function rebuildindex(); function addnutfile(filepath,encrypt=false,compress=2047,nameinarchive=null) function pxloadfile(name,debug=false) function pxexecfile(name,debug=false) }
…
...
class ThreadLoop { constructor(); function run(); eventloop=null ; //the eventbase object } class AppMainLoop extends ThreadLoop {}
…
...
...
gonuts comes with some standard modules to import. Note that when gonuts is installed, these modules can be automatically loaded using the systemtools (see above)
Documentation for dens is separated and not covered in detail here.
Available standard dens:
denguuid - create GUUIDs using a common algorithm (replaces built-in GUUID function)
densqlite - sqlite3 support
denserial - direct serial device support (without using files)
densqratthread - cooperative pseudo-threading support
denlfiles - large files support (for 32bit systems,but also 64bit compatible for common API)
and for linux systems only:
dendbi - libdbi support, currently tested for mysql, postgresql (and sqlite).