[Tutorial] Linked lists
#1

Intro
As I've released an include called "Linked lists in PAWN", also an plugin called "Memory access plugin", which is required for the "Linked lists in PAWN" include, and obviously the majority of this community don't know the use and benefits of those releases, so I've decided to write this tutorial.




Index

Quote:
  • Definition of a linked list
  • More about the "Linked lists in PAWN" include
  • What is "dynamic memory"?
  • More about the "Memory access" plugin
  • "Pro"s and "Con"s of the "Linked lists in PAWN" include
  • What can I do for my server by using linked lists?
  • How to use the "Linked lists in PAWN" include
  • Script examples
  • "Linked lists in PAWN" include - Trivia


Definition of a linked list
A linked list is basicly something, which can store nodes aka "blocks of memory" inside a list. Each node has links aka "pointers" to its next and previous nodes.
By default nodes inside linked lists do not have IDs!

Abstract image of a linked list:

Quote:


More about the "Linked lists in PAWN" include
This include allows you to store almost infinite (of course depends on your server's memory) amount of nodes inside linked lists.
Also it comes with various functions to manipulate your lists, or store them each as a file. Those files are not meant to be human readable, hence should only be modified, if you know what you are actually doing!
This include relies on the "Memory access" plugin, to handle dynamic memory.




What is "dynamic memory"?
Actually "dynamic memory" is memory you can allocate and free in runtime.
It's highly recommended, if the size of requested blocks of memory becomes unpredictable.




More about the "Memory access" plugin
There will be a proper tutorial about the "Memory access" plugin in the future.
Meanwhile you can check this thread: https://sampforum.blast.hk/showthread.php?tid=451381




"Pro"s and "Con"s of the "Linked lists in PAWN" include
Pro:

Quote:
  • No static amount of nodes
  • Possibility to create different nodes with different sizes (decreases memory usage, if size is completly different, compared to nodes, which have all the same size inside a linked list)
  • Iterating through for example 5 nodes within 10000 nodes is equaly fast than iterating through 5 nodes within 10 nodes
  • Can be used as a stack aswell
  • Linked lists can be nested
Con:
Quote:
  • No IDs
  • Use of low level features (such as pointers) inside a high level scripting language
  • Iterations through nodes may be slower than iterating through static sized arrays
  • It has the same problems as dynamic memory has


What can I do for my server by using linked lists?
Some examples:

Quote:
  • Count and list kills, where you can store important information each kill, and call them whenever you like pretty efficient and fast.
  • Record player stats or lets you make your own player/NPC recorder much easily and faster.
  • Create lists of instructions or routines for your server.
  • Maps can be stored into lists and can be saved into a file very easily. Loading becomes very easy aswell.
  • Creating a streamer script.
  • Server logs can be stored into lists, and can be called fast back.
  • Create a dynamic map loading/saving system, where you can modify your map information just on the fly.
  • Database like structures can be created aswell.
  • Handle multiple linked lists inside linked lists.

How to use the "Linked lists in PAWN" include
You've probably asked yourself, how to use this include and what functions does it give us.
I've already spend a lot of time to create a proper documentation at the "Linked lists in PAWN" release thread. You should take a bite from it. https://sampforum.blast.hk/showthread.php?tid=451962

Here I'll explain you some basics this include gives us:

Creating a valid linked list

pawn Код:
new List::init<my_list>; // Declares an empty linked list

Creating nodes inside a linked list (simple)
pawn Код:
// Declares an empty linked list
new LIST::init<my_list>;

// Creates a node and adds the number 5 in it
LIST::push_back(my_list, 5);

// Creates a node and adds the number 7 in it
LIST::push_back(my_list, 7);

Creating nodes inside a linked list (blocks of memory)
pawn Код:
// Example structure
MEM::struct example_struct
{
    example_struct_data_1,
    Float:example_struct_data_2,
    example_struct_data_3[5],
    Float:example_struct_data_3[200]
}

//...

// Declares an empty linked list
new LIST::init<my_list>,

    // Declares an array
    example_buffer[example_struct];

// Creates a node and adds an constant array
LIST::push_back_arr(my_list, {20, 5, 8, 100});

// Creates a node and adds a valid PAWN string
LIST::push_back_arr(my_list, "Hello world!");

// Set "example_buffer" with different values

// Creates a node and adds an array
LIST::push_back_arr(my_list, example_buffer);

Clear a linked list
pawn Код:
// Declares an empty linked list
new LIST::init<my_list>;

// Do some operations...

// Clears the whole list
LIST::clear(my_list);

Iterate through nodes inside a linked list, get and set data
pawn Код:
// Declares an empty linked list
new LIST::init<my_list>,

    // Declares a pointer
    Pointer:data_ptr;

// Do some operations...

// Iterates through the list
LIST::foreach<my_list_it>(my_list)
{
    // Stores data pointer
    data_ptr = LIST_IT::data_ptr(my_list_it);

    // Increments value by 1
    MEM::set_val(data_ptr, MEM::get_val(data_ptr)+1);
}

Iterate through nodes inside a linked list, get and set data (arrays)
pawn Код:
// Example structure
MEM::struct example_struct
{
    example_struct_data_1,
    Float:example_struct_data_2,
    example_struct_data_3[5],
    Float:example_struct_data_3[200]
}

// Declares an empty linked list
new LIST::init<my_list>,

    // Declares a pointer
    Pointer:data_ptr,

    // Declares a variable to store the size of an array
    data_size,

    // Placeholder
    data_buffer[example_struct];

// Do some operations...

// Iterates through the list
LIST::foreach<my_list_it>(my_list)
{
    // Stores data pointer
    data_ptr = LIST_IT::data_ptr(my_list_it);

    // Stores the data size
    if((data_size = LIST_IT::data_size(my_list_it)) != sizeof data_buffer)
    {
        print("The data is not %d units long. | Node: 0x%x | Data Pointer: 0x%x | Size: %d", sizeof data_buffer, _:my_list_it, _:data_ptr, data_size);
        continue;
    }

    // Gets an array
    MEM::get_arr(data_ptr, _, data_buffer);

    // Do some operations...

    // Zero sets memory (just an example)
    MEM::zero(data_ptr, data_size);
}

Handle linked lists inside linked lists
pawn Код:
new LIST::init<my_list>, LIST::init<nested_list>;

// Push data
LIST::push_back(nested_list, 2);
LIST::push_back(nested_list, 4);
LIST::push_back(nested_list, 8);
LIST::push_back(nested_list, 10);

// Stores the linked list into a linked list
LIST::push_back_arr(my_list, _:nested_list);

// Zero set pointers, makes now "nested_list" handle as a different list.
MEM::zero(MEM::get_addr(_:nested_list[0]), sizeof nested_list);

// Push data
LIST::push_back(nested_list, 200);
LIST::push_back(nested_list, 400);
LIST::push_back(nested_list, 800);
LIST::push_back(nested_list, 1000);
LIST::push_back_arr(my_list, _:nested_list);

// Zero set pointers, makes now "nested_list" handle as a different list.
MEM::zero(MEM::get_addr(_:nested_list[0]), sizeof nested_list);

// Push data
LIST::push_back(nested_list, 2000);
LIST::push_back(nested_list, 4000);
LIST::push_back(nested_list, 8000);
LIST::push_back(nested_list, 10000);
LIST::push_back_arr(my_list, _:nested_list);

//...

new LIST::init<buffer>, count;

// Iterate through "my_list"
LIST::foreach<my_list_it>(my_list)
{
    // Get array from a node
    MEM::get_arr(LIST_IT::data_ptr(my_list_it), _, _:buffer);

    // Print node count
    printf("%d. List:", ++count);

    // Iterate through a nested linked list, and print its values
    LIST::foreach<buffer_it>(buffer) printf(" Value: %d", LIST_IT::data_val(buffer_it, 0));
}

Handle mixed data (with nested linked lists)
pawn Код:
MEM::struct my_data_struct
{
    my_data_1,
    Float:my_data_2,
    my_data_3[10],
    LIST::init<my_data_4>
}

//...

// Declares a buffer
new LIST::init<my_list>, my_data[my_data_struct];
my_data[my_data_1] = 10;
my_data[my_data_2] = 10.0;
format(my_data[my_data_3], 10, "Something");

// Push data
LIST::push_back(my_data[my_data_4], 10);
LIST::push_back(my_data[my_data_4], 20);
LIST::push_back(my_data[my_data_4], 30);
LIST::push_back(my_data[my_data_4], 40);
LIST::push_back(my_data[my_data_4], 50);


// Push mixed data into "my_list"
LIST::push_back_arr(my_list, my_data);

// Zero sets buffer
MEM::zero(MEM::get_addr(my_data[0]), sizeof my_data);

my_data[my_data_1] = 1;
my_data[my_data_2] = 1.0;
format(my_data[my_data_3], 10, "Hello");

// Push data
LIST::push_back(my_data[my_data_4], 1);
LIST::push_back(my_data[my_data_4], 2);
LIST::push_back(my_data[my_data_4], 3);
LIST::push_back(my_data[my_data_4], 4);
LIST::push_back(my_data[my_data_4], 5);

// Push mixed data into "my_list"
LIST::push_back_arr(my_list, my_data);

//...
new buffer[my_data_struct];

// Iterate through "my_list"
LIST::foreach<my_list_it>(my_list)
{
    // Get array from node
    MEM::get_arr(LIST_IT::data_ptr(my_list_it), _, buffer);

    // Print data
    printf("my_data_1 = %d | my_data_2 = %.1f | my_data_3 = \"%s\"", buffer[my_data_1], buffer[my_data_2], buffer[my_data_3]);

    // Iterate through a nested linked list, and print its values
    LIST::foreach<inner_list_it>(buffer[my_data_4]) printf(" Value: %d", LIST_IT::data_val(inner_list_it, 0));
}
Script examples



"Linked lists in PAWN" include - Trivia

Quote:
  • Completly empty/Null lists are possible
  • A linked list causes leaking memory, if not cleared, before loosing reference to it


Best regards

~ BigETI
Reply
#2

Isn't Pawn the lightest and quickest programming language? How is it high level? Or.. what other language are you referring it to?
Reply
#3

Quote:
Originally Posted by Sublime
Посмотреть сообщение
Isn't Pawn the lightest and quickest programming language? How is it high level? Or.. what other language are you referring it to?
PAWN is pretty much high level, because you can compile P-Code with it, which can be executed inside an AMX, which is running with the server itself, which runs on an operating system, which runs natively on hardware or a hardware virtualizer such as a virtual machine.
Reply
#4

Quite a good tut for the include you made.
This is an easier definition for those who do not understand what this is:

* This is almost like an infinitely long array which are possible in some languages but not in 'static' languages like PAWN.
* These have two parts - one part stores the VALUE for that index and the other part stores the ADDRESS to the next index. (node)
* The only differences between arrays and linked lists are that array cells are arranged one by one while linked lists' cells are not - the second difference is that linked lists are practically infinite
* It's very awesome for things like situations like:
pawn Код:
new object_INFO[ MAX_OBJECTS ][ CURRENT_PLAYERS ] ;

CURRENT_PLAYERS = ?
In such large arrays each added index adds a lot of memory, so you can create CURRENT_PLAYERS linked lists and then do stuff like this.

Make sure you know what the hell you're doing else you can mess up with memory.

@ Sublime - PAWN is quite high level considering languages like ASM85
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)