Array Module (AMX Mod X)
Array module, created and maintained by Twilight Suzuka and Anpheus, brings fast, easy, and efficient dynamic storage into PAWN coding.
It is essential for many tasks, such as copying entities and is useful in a great many applications, mainly because of how it overcomes PAWN's static nature.
Contents
History
The original Array module, first written by BAILOPAN for AMXx 1.0, used a vector (or dynamic array) system to create "lists" and "tables". Its implementation, a hard to understand and convoluted system which daunted many coders, would have nevertheless been very successful if not for a few key factors: It crashed on unload and reload.
While using this module, for the purposes of creating a customized vault system, Twilight Suzuka discovered these flaws, and investigated. She, along with her good friend Anpheus, pointed out said errors, and fixed a great deal of them. Unsatisfied, Twilight Suzuka created the ArrayX module, using a dual linked list system of implementation. File:Stink.png While the ArrayX module did work, and did work well, providing a simple (if "not professional" as Anpheus would say) interface to a simple dynamic array. Sparse, relatively quick, and offering a tri-unit storage (three different types: float, string, integer: per index), ArrayX worked relatively well. However, as BAILOPAN was quick to point out, it utilized an inefficient linked list design.
While searching for an appropriately sparse vector design for the new ArrayX, Anpheus chanced upon the Judy library. It offered unparalleled speed, efficiency, and sparse capabilities compared to any other system we could find. Implementing Judy's trie design, they created ArrayX 2.0, which was basically the same then as it is now. It was eventually implemented into the CVS.
Description
Array module, originally ArrayX, provides dynamic storage units in the form of three generic types: Array, Keytable, and Hashtable.
While each of these types are carefully disguised to make them easier to use, the back bone of Array module is the Judy library, which uses a trie implementation to allow for sparse and efficient data structures.
In effect, all three units allow for sparse settings, as well as providing huge speed and memory increases over other implementations.
Arrays
Arrays are the simplest unit provided by Array module. They function almost exactly like their static counterpart, the traditional array, but are dynamic.
Basics:
- Method : bind a value to an integer key.
- Allows: Advanced Searching, Saving/Loading
- Useful for array-like programming.
- Advantages:
- They do not have upper bounds; you can write to any index.
- They can be sparse; you may write to any index, anywhere, and not waste the space between the two.
- Easy to use natives for several important features, such as searching and saving/loading.
- Disadvantages:
- Slightly more difficult to use.
- Slightly slower, slightly less memory efficient than generic arrays.
Keytables
Keytables are Array modules counter-part to an advanced vault that allows searching. While they are not nearly as fast as hashtables, they allow for searching, and are significantly faster than any other vault like structure available through AMXx, over amortized time.
Basics:
- Method : bind a value to a string key.
- Allows: Searching, Saving/Loading
- Useful for vault-like programming.
- Advantages:
- Allows for simplified storage of vault-like values.
- Are extremely fast, and can be searched.
- Has searching and saving/loading natives.
- Disadvantages:
- Less efficient than hashtables.
Hashtables
Hashtables work as generic vaults; you cannot search through them. However, they are much faster than any other data structure available through AMXx that allows for associative array properties.
Basics:
- Method
- bind a value to a string key, decompiled through a hash.
- Allows
- Fastest possible retrieval.
- Useful for vault-like programming and fast retrieval
- Advantages:
- Allows for simplified storage of vault-like values.
- Are extremely fast, faster than typical vectors with amortized cost
- Did we mention they are fast?
- Disadvantages:
- No searching.
- Potential for memory leaks.
- No saving/loading
Usage
It is very simple to use Array Module, as the natives were created for ease of use. Think of them in terms of the generic units you already know, and you will be set:
Dynamic vs Generic:
- Array - Array
- Keytable - Vault
- Hashtable - Fastest Vault
Persistence
Dynamic units must be deallocated or deleted when you are done using them if the scope is local; they will not be deleted at the end of the function they were created in, and thus will be a memory leak if you no longer have a reference to them.
Generic Conventions
Array Module uses a few conventions which may be difficult to understand at first, but which are quite easy to understand once the reasons behind them are understood.
Disable Check
"disable_check" will disable internal checking, and is not recommended.
"disable_check = 0" is present as a parameter in a great deal of Array functions. Checking is done internally, and by changing this to 1, you turn off the internal checking. Typically, this is looked upon as bad form; however, it is necessary in a few situations, such as:
- Checking an index that does not yet exist for a value.
Most of these instances are bad form; a more air tight method is possible and recommended. As the default value is '0', it is highly recommended that it remain 0; however, it is true that setting the value to 1 will render a slightly higher speed of checking.
As disabling checking may result in a crash, it is unadvised that it be set to 1 unless ones code is flawless.
&success
The success parameter is set to 1 on success of internal functions. Do not set it to a constant.
If checking is enabled, this parameter may turn to 0 due to internal checking errors. If a value was returned, it will typically be NULL (0). Success does not necessarily have to be passed; leaving it as default is perfectly fine, though inadvisable. It is highly recommended that all of your code include success checks, just in case.
Generic functions
All Array Module functions use the same basic format for similar natives between types
By convention, similar functions between the three types have the same style of usage, format, and name:
new MyArray = array_create();
new MyKeytable = keytable_create();
new MyHashtable = hashtable_create();
To reduce the size of this page, these specifications have been created:
- The symbol '*' is used to represent 'array', 'keytable', and 'hashtable'.
The above code reduces to:
new My* = *_create();
Recognize these shortcuts will not work in actual code; they are used to reduce the size of this document. Always be sure to clean them up.
Unit manipulation
These natives allow for manipulation of the dynamic unit structures themselves.
Creation
The basic native for creating dynamic units is:
*_create( arrayid = 0, reserve_id = 0)
The parameters are for backwards compatibility only; they have no true purpose now. They were once used as a form of hard coded communication; you may create a unit with a specified id number, or reserve a specific id number for generic usage. Neither is needed anymore.
The id number that this returned must be stored in some fashion; if it is not, the unit will persist and become a memory leak.
Deletion
The basic native for deleting dynamic units is:
*_delete( arrayid )
This will completely destroy the unit; you will no longer be able to use it at all, and will receive an error if you attempt to.
Reset
The basic native for resetting dynamic units is:
*_clear( arrayid )
This will remove all indexes from the unit, but leave it intact, allowing you to reuse it.
Memory
The basic native for retrieving the amount of memory used by a unit is:
*_memory (arrayid, disable_check = 0);
This will tell you exactly how much memory has been allocated within the unit, including its own overhead. It is useful for debugging.
Count
The basic native for retrieving the amount of units in existence is:
*_count( start = 0, stop = -1);
This will state the amount of units of the specified type in existence; as they are created in sequence, this can also be used to find out the ids of the available units.
Index manipulation
All index manipulation natives include the disable_check parameter; it has been excluded for the purpose of simplification.
Set
The basic native for setting values to dynamic units is:
*_set_(type)(arrayid, index, (type)value)
Where (type) is the type of value you want to set.
Note: in keytables and hashtables, index will be a string.
Types available: int, float, string, vector. Note: With hashtables, types have been cut down for compatibility.
Get
The basic native for getting values to dynamic units is:
*_get_(type)(arrayid, index, (type)value, ret_val)
Where (type) is the type of value you want to set.
Note: in keytables and hashtables, index will be a string.
Types available: int, float, string, vector. Note: With hashtables, types have been cut down for compatibility. Note: For floats and integers, there is no ret_val parameter; the native returns the value directly.
Remove
The basic native for removing indexes completely is:
*_remove (arrayid, index)
Note: in keytables and hashtables, index will be a string.
Note: Removing an index will recover the memory used by it. That index must be reallocated to use again.
Filled
The basic native for getting conformation of indexes in dynamic units is:
*_isfilled(arrayid, index)
Note: in keytables and hashtables, index will be a string.
Will return 1 if the index exists.
Empty
The basic native for getting conformation of indexes in dynamic units is:
*_isempty(arrayid, index)
Note: in keytables and hashtables, index will be a string.
Will return 1 if the index does not exist.
Unit Specific Natives
Some functions are not available to some units, due to the incompatibilities between them.
Saving/Loading
Hashtables lack this feature entirely, while keytables and arrays support it fully.
Saving
This native allows an array or keytable to be directly saved to a file specified:
- _save (arrayid, filename[], disable_check = 0);
Loading
This native allows an array or keytable to be loaded directly from a file.
If an arrayid is provided, the file will be loaded into that unit; if not, it will load into a new unit, and return its id.
- _load (filename[], arrayid = 0, reserve_id = 0);
Convert
This native will convert a binary file into a human readable file:
- _save_ascii(inputfile[], outputfile[]);
Searching
Hashtables lack this feature entirely, while keytables have limited searching capabilities, and arrays has advanced capabilities in searching.
Filled Index Searching
These functions allow the coder to search through filled indexes in arrays and keytables. These functions do not guarantee success, so it is recommended that the success parameter be checked.
*_first (arrayid, index, &success = 0);
- _next (arrayid, index, &success = 0);
- _prev (arrayid, index, &success = 0);
- _last (arrayid, index, &success = 0);
Index parameter: Starting index
Note: in keytables and hashtables, index will be a string.
Empty Index Searching
These functions allow the coder to search through empty indexes in arrays. These functions do not guarantee success, so it is recommended that the success parameter be checked.
*_firstempty (arrayid, index, &success = 0);
- _nextempty (arrayid, index, &success = 0);
- _prevempty (arrayid, index, &success = 0);
- _lastempty (arrayid, index, &success = 0);
Index parameter: Starting index.
Array Information
Arrays have two additional natives, which allow for retrieval of additional information:
Get the size of the array from one index to another
- array_size (arrayid, start = 0, stop = -1, disable_check = 0);
Return the nth filled index, starting at start
- array_get_nth (arrayid, n, start = 0, disable_check = 0);
Unit Compression
As one might notice, an entire dynamic unit is referenced by an integer value. This allows one to create a number of significant structures:
- Units of Units:
- Storing the references to units into an array, allowing for two dimension dynamic unit storage and retrieval
- Use of a keytable and hashtable in conjunction to enhance the properties of both - faster gets + searching and saving -
- Dynamic storage of: functions, units, and much more
The most powerful of these is to create a global variable of type 'public', and use it to reference a dynamic unit. Now any plugin may access that unit as if it was their own, with little to no cost.