Array Module (AMX Mod X)

From AlliedModders Wiki
Revision as of 23:27, 13 January 2006 by Twilight Suzuka (talk | contribs)
Jump to: navigation, search

Array module, created and maintained by Twilight Suzuka and Anpheus, brings fast, easy, and effecient 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.


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 implimentation, 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 implimentation.

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. Sparce, 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 ineffecient linked list design.

While searching for an appropriately sparce vector design for the new ArrayX, Anpheus chanced upon the Judy library. It offered unparrallel speed, effeciency, and sparce capabilities compared to any other system we could find. Implimenting Judy's trie design, they created ArrayX 2.0, which was basically the same then as it is now. It was eventually implimented into the CVS.

Description

Array module, originally ArrayX, provides dynamic storage units in the form of three generic types: Array, Keytable, 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 implimentation to allow for sparce and effecient data structures.

In effect, all three units allow for sparce settings, as well as providing huge speed and memory increases over other implimentations.

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.
  1. Advantages:
    1. They do not have upper bounds; you can write to any index.
    2. They can be sparce; you may write to any index, anywhere, and not waste the space between the two.
    3. Easy to use natives for several important features, such as searching and saving/loading.
  1. Disadvantages:
    1. Slightly more difficult to use.
    2. Slightly slower, slightly less memory effecient 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.
  1. Advantages:
    1. Allows for simplified storage of vault-like values.
    2. Are extremely fast, and can be searched.
    3. Has searching and saving/loading natives.
  1. Disadvantages:
    1. Less effecient 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 assosiative array properties.

Basics:

Method 
bind a value to a string key, decompiled through a hash.
Allows
Fastest possible retreival.
Useful for vault-like programming and fast retreival
  1. Advantages:
    1. Allows for simplified storage of vault-like values.
    2. Are extremely fast, faster than typical vectors with amortized cost
    3. Did we mention they are fast?
  1. Disadvantages:
    1. No searching.
    2. Potential for memory leaks.
    3. 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 
Vault

Persistance

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 nessasary 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 inadvised 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 nessasarily 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 are 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 indexs from the unit, but leave it intact, allowing you to reuse it.

Memory

The basic native for retreiving 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. Useful for debugging.

Count

The basic native for retreiving the amount of units in existance is:

*_count( start = 0, stop = -1);

This will state the amount of units of the specified type in existance; as they are created in sequence, this can also be used to find out the ids of the availiable 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 indexs 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 indexs 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 indexs 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 indexs in arrays and keytables.

*_first (arrayid, index, &success = 0);

  • _next (arrayid, index, &success = 0);
  • _prev (arrayid, index, &success = 0);
  • _last (arrayid, index, &success = 0);


Note: in keytables and hashtables, index will be a string.

Note: These functions do not garantee success, so it is recommended that the success parameter be checked.

Note: The index parameter should be filled with the index one wishes to start from.

Empty Index Searching

These functions allow the coder to search through empty indexs in arrays.

*_firstempty (arrayid, index, &success = 0);

  • _nextempty (arrayid, index, &success = 0);
  • _prevempty (arrayid, index, &success = 0);
  • _lastempty (arrayid, index, &success = 0);

Note: These functions do not garantee success, so it is recommended that the success parameter be checked.

Note: The index parameter should be filled with the index one wishes to start from.

Array Information

Arrays have two additional natives, which allow for retreival 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 retreival
    • Use of a keytable and hashtable in congunction to enhance th eproperties 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.