Контакты Поиск

[ вход ]
[ последние сообщения ]

  • Страница 1 из 1
  • 1
Форум » SourceMod >> CS:Source >> CSGO » Уроки SourceMod (SourcePawn) Скриптинга » adt_array.inc ArrayList (Динамический массив для хранения разных типов данных)
adt_array.inc ArrayList
_wS_ Дата: Воскресенье, 21.10.2012, 18:56:49 | Сообщение # 1
Класс: ArrayList (adt_array.inc)
https://sm.alliedmods.net/new-api/adt_array/ArrayList

Код
// Конструктор:
ArrayList(int blocksize=1, int startsize=0);


Медленнее обычных массивов, но умеет увеличиваться/уменьшаться, когда вы добавляете/удаляете элементы (динамический).
Каждый элемент в массиве имеет фиксированный/одинаковый размер (blocksize), который нельзя изменить после создания массива (объекта).
Размер (blocksize) это количество ячеек (по умолчанию 1).
Каждая ячейка занимает по 4 байта.
Если создать массив с blocksize 1 и добавить в него, например, число 5, то данные будут иметь вид: [05 00 00 00].

Если нужно хранить тип any/int/bool/float/Handle (Function не поддерживается), то достаточно 1 ячейки.
Если, например, цвет rgba[4], то нужно 4 ячейки.
Но если нужно хранить строку, например, s[12], то в качестве размера используют ByteCountToCells(sizeof(s)) или ByteCountToCells(12).
Это stock функция в adt_array.inc, которая имеет вид:

Код
stock int ByteCountToCells(int size)
{
    if (!size) {
        return 1;
    }
    return (size + 3) / 4;
}


startsize это количество элементов в массиве после его создания. Обычно это всегда 0, т.к. хотят создать пустой список. Но если вы укажете значение > 0, то массив будет заполнен неинициализированными (случайными) данными.

Создаём пустой массив. Для каждого элемента в массиве выделяем 1 ячейку (4 байта). Этот размер мы уже изменить не сможем.
Если массив больше не нужен, уничтожьте: delete array;

Код
ArrayList array = new ArrayList(); // Или так:
ArrayList array = new ArrayList(/* blocksize */1, /* startsize */0);


Свойства:

Код
// Количество элементов в массиве (только чтение).
int array.Length

// Размер (blocksize), который был указан при создании массива (объекта) (только чтение).
int array.BlockSize


Удалить все элементы из массива (очистить список).

Код
void array.Clear(); // Или можно void array.Resize(0)


Создать копию массива, которая никак не связана с оригиналом. Не путайте с CloneHandle, т.к. это не тоже самое.
Уничтожьте, если копия уже не нужна: delete array_clone;

Код
ArrayList array_clone = array.Clone();


Изменить количество элементов в массиве. Например, у нас в массиве два элемента: 5, 7.
Если сделать Resize(1), то останется лишь 5. А если сделать Resize(3), то: 5, 7, x, где x это неинициализированные (случайные) данные.

Код
void array.Resize(int newsize);


Добавить в конец списка новый элемент.
Тип any это int/bool/float/Handle (Function не поддерживается).
К Handle типу относятся все типы, на которых можно сделать CloseHandle или delete, например, StringMap, ArrayList, File.
Этим типам достаточна одна ячейка (blocksize), но если размер больше, то это всё равно сработает.
Возвращает индекс (позицию) добавленного элемента (как и другие Push..).
Индексы всегда от 0 до array.Length - 1, как и с обычными списками.

Код
int array.Push(any value);


Добавить в конец списка строку. Для хранения строк лучше подходит StringMap,
т.к. он хранит строки такими, какие они есть (без лишнего размера), да и поиск в StringMap работает намного быстрее.

Код
int array.PushString(const char[] value);


Добавить массив в конец списка. Можно и enum struct.
Если размер (size) не указан, или он > blocksize, то будет использован blocksize, указанный при создании объекта array.

Код
int array.PushArray(const any[] values, int size=-1);


Возвращает элемент массива по его индексу (index), который всегда от 0 до array.Length - 1.
Обычно используют для элементов, которые были добавлены с помощью int array.Push(any value);
В основном указывают лишь index, а остальные параметры оставляют по умолчанию.

asChar: если true, то будет прочитан 1 байт (число от -128 до 127), а не вся ячейка (4 байта).
block: если asChar false, то это число от 0 до blocksize-1, если asChar true, то это число от 0 до blocksize * 4 - 1.

Если вы записали массив чисел, то block позволяет получить одно конкретное число, вместо того, чтобы получать весь массив.
Примеры вы найдёте ниже.

Код
any array.Get(int index, int block=0, bool asChar=false);


Получить строку.
Возвращает количество байт, которые были записаны в buffer. Например, для "т" вернёт 2, а для "t" 1.

Код
int array.GetString(int index, char[] buffer, int maxlength);


Получить массив. Можно и enum struct.
Возвращает количество ячеек, которые были записаны в buffer.
Если размер (size) не указан, или он > blocksize, то будет использован blocksize, указанный при создании объекта array.

Код
int array.GetArray(int index, any[] buffer, int size=-1);


Перезаписать данные. Любая перезапись не работает, если данные ещё не были добавлены с помощью Push..
Про block и asChar написано выше, а примеры будут ниже.

Код
void array.Set(int index, any value, int block=0, bool asChar=false);


Перезаписать строку.

Код
void array.SetString(int index, const char[] value);


Перезаписать массив. Можно и enum struct.

Код
void array.SetArray(int index, const any[] values, int size=-1);


Сдвинуть все элементы, начиная с index, вправо на один шаг.
Т.е. если, например, у вас в массиве данные: 3, 5 и вы делаете array.ShiftUp(1), то данные теперь имеют вид: 3, x, 5.
Теперь index содержит какое-то случайное значение x, которое вы должны изменить на своё (если нужно).

Код
void array.ShiftUp(int index);


Удалить элемент из массива. Правые элементы смещаются влево на 1 шаг.
Например, если в массиве было 3, 5, 7, 9 и вы удалили index 1 (число 5), то теперь массив имеет вид: 3, 7, 9.

Код
void array.Erase(int index);


Поменять местами 2 элемента.
Если массив имеет вид 3, 5, 7 и сделать array.SwapAt(0, 2), то теперь: 7, 5, 3.

Код
void array.SwapAt(int index1, int index2);


Найти строку. "zx" и "zX" это разные строки. Возвращает index, или -1, если данные не найдены.

Код
int array.FindString(const char[] item);


Найти элемент. Возвращает index, или -1, если данные не найдены. block может быть полезен, если blocksize > 1.

Код
int FindValue(any item, int block=0);

// Пример с block.
// Числа 7 и 5 находятся в block 0, а 3 и 9 в block 1.

ArrayList array = new ArrayList(2);

array.PushArray({7,3}, 2);
array.PushArray({5,9}, 2);
    
PrintToServer("%d", array.FindValue(9, 0)); // -1 (не найдено)
PrintToServer("%d", array.FindValue(9, 1)); //  1 (найдено). index 1 указывает на массив {5,9}


Сортировать элементы в массиве.

Код
void array.Sort(SortOrder order, SortType type);

// Порядок сортировки.
enum SortOrder
{
    Sort_Ascending = 0, // По возрастанию (1, 2, 3).
    Sort_Descending, // По убыванию (3, 2, 1).
    Sort_Random // Случайный порядок (3, 1, 2).
};

// Тип данных.
enum SortType
{
    Sort_Integer = 0, // int (3).
    Sort_Float, // float (3.0)
    Sort_String // char ("3")
};


Если такая сортировка вас не устраивает, то API предоставляет ещё один вариант:

Код
public void OnPluginStart()
{
    ArrayList array = new ArrayList();
    
    array.Push(2);
    array.Push(7);
    array.Push(5);
    array.Push(9);
    array.Push(0);
    
    array.SortCustom(SortFuncADTArray_Callback, /* Handle hndl */null);
    
    for (int i = 0, length = array.Length; i < length; i++) {
        PrintToServer("%d", array.Get(i));
    }
    
    delete array;
}

// array - массив, в котором происходит сортировка (странно, что тип не ArrayList, наверно для совместимости).
// hndl - можно передать какие-то данные, но мы передали null.
// return: -1 = index1 должен быть левее, 0 = index1 и index2 равны, 1 = index1 должен быть правее.

int SortFuncADTArray_Callback(int index1, int index2, Handle array, Handle hndl)
{
    int v1 = view_as<ArrayList>(array).Get(index1);
    int v2 = view_as<ArrayList>(array).Get(index2);
    return (v1 < v2 ? -1 : (v1 == v2 ? 0 : 1));
}

// Результат:
// 0
// 2
// 5
// 7
// 9


Некоторые примеры:

Код
enum struct T
{
    int x;
    char s[4];
    int y;
}

public void OnPluginStart()
{
    ArrayList array = new ArrayList();
     
    int index = array.Push(5);
    PrintToServer("%d", array.Get(index)); // 5
    // 5 = [05, 00, 00, 00] (4 байта).
     
    array.Set(index, 25, /* block */1, /* asChar */true);
    PrintToServer("%d", array.Get(index)); // 6405
     
    // Как получилось число 6405?
    //
    // Когда мы записали 1 байт (char, 25) в block 1, то ячейка приняла вид:
    // [05, 19, 00, 00]
    // 25 = 0x19 (hex).
    // Склеиваем байты справа налево и получаем:
    // 00 00 19 05 = 0x00001905 = 6405
     
    PrintToServer(" ");
     
    // Смотрим чему равен каждый байт в ячейке. Значения от -128 до 127 (char).
    for (int byte = 0, bytes = array.BlockSize * 4; byte < bytes; byte++) {
        PrintToServer("byte %d = %d",
            (byte + 1),
            array.Get(index, /* block */byte, /* asChar */true));
    }
     
    PrintToServer(" ");
     
    // Результат:
    // byte 1 = 5
    // byte 2 = 25
    // byte 3 = 0
    // byte 4 = 0
     
    delete array;
     
    /////////////////////////////////
     
    array = new ArrayList(sizeof(T));
     
    T t;
    t.x = 3;
    strcopy(t.s, sizeof(t.s), "ab");
    t.y = 5;
    index = array.PushArray(t, sizeof(t));
     
    PrintToServer("t.x = %d", array.Get(index, /* block */0)); // 3
    PrintToServer("t.s = %s", array.Get(index, /* block */1)); // ab (для "abcdef" вернуло бы "abcd", т.к. ячейка это 4 байта).
    PrintToServer("t.y = %d", array.Get(index, /* block */2)); // 5
     
    array.Set(index, 7, /* block */0);
    array.Set(index, 0x746163, /* block */1); // cat = 636174 = 63 61 74 = 0x746163
    array.Set(index, 9, /* block */2);
     
    PrintToServer(" ");
     
    if (array.GetArray(index, t, sizeof(t))) {
        PrintToServer("t.x = %d\nt.s = %s\nt.y = %d", t.x, t.s, t.y);
    }
     
    // Результат:
    // t.x = 7
    // t.s = cat
    // t.y = 9

    delete array;
}


Элемент в ArrayList, например, enum struct объект, может содержать большое количество данных.
Если нужно получить какие-то конкретные данные, то нет смысла извлекать весь элемент.
Примеры ниже показывают, как можно извлечь конкретные данные.
Но максимальное количество данных, которые можно извлечь таким способом, это 4 байта (одна ячейка).

Код
enum struct T
{
    Handle h;
    bool b;
    int i[MAXPLAYERS + 1];
    char s[128];
}

public void OnPluginStart()
{
    T t;
    int client  = 5;
    t.i[client] = 7;
    
    ArrayList array = new ArrayList(sizeof(t));
    int index = array.PushArray(t,  sizeof(t));
    
    // Задача: проверить, является ли t.i[client] числом 7.
    
    // Вариант 1:
    if (array.GetArray(index, t, sizeof(t)) && t.i[client] == 7)
    {
        PrintToServer("Да, это 7.");
        // Плохо, т.к. нужно было извлечь лишь значение t.i[client], а мы извлекли всю структуру T.
    }
    
    // Вариант 2:
    if (array.Get(index, /* block */(4 + 4 + (client * 4)), /* asChar */true) == 7)
    {
        PrintToServer("Да, это 7.");
        
        // Хорошо, т.к. мы извлекли лишь 1 интересующий нас байт (t.i[client]).
        // Не забываем, что это char (от -128 до 127), поэтому подходит не для каждой ситуации.
        // Но мы были уверены, что число входит в этот промежуток, поэтому всё хорошо.
        
        // Почему 4 + 4 + (client * 4):
        // Это зависит от расположения переменных в T структуре.
        // Handle h; 4 байта (block = 0, 1, 2, 3).
        // bool b  ; 4 байта (block = 4, 5, 6, 7).
        // int i[MAXPLAYERS + 1]; (MAXPLAYERS + 1) * 4 байтов (block = 8, 9, ...).
    }
    
    // Вариант 3:
    if (array.Get(index, /* block */(1 + 1 + client), /* asChar */false) == 7)
    {
        PrintToServer("Да, это 7.");
        
        // Хорошо, т.к. мы извлекли лишь 4 интересующих нас байта (одну ячеку) (t.i[client]).
        // Теперь НЕТ ограничения "от -128 до 127", это оригинальное int значение.
        
        // Т.к. мы указали asChar false, то читается вся ячейка (4 байта), а не 1 байт, как в варианте 2.
        // Поэтому числа в block (1 + 1 + client) это ячейки, а не байты:
        // Handle h; 1 ячейка (block = 0)
        // bool b  ; 2 ячейка (block = 1)
        // int i[MAXPLAYERS + 1]; (MAXPLAYERS + 1) ячеек (block = 2, 3, ...).
    }
    
    delete array;
}


Сообщение отредактировал _wS_ - Среда, 08.12.2021, 17:35:49
 
Valeriks Дата: Суббота, 27.09.2014, 14:33:39 | Сообщение # 2
Сообщений: 100
Репутация: 3 [ +/- ]
Благодарю за разъяснения. Буду пробовать.
 
_wS_ Дата: Среда, 08.12.2021, 12:21:32 | Сообщение # 3
Пока обновлял урок, и сам нового узнал =)
 
Форум » SourceMod >> CS:Source >> CSGO » Уроки SourceMod (SourcePawn) Скриптинга » adt_array.inc ArrayList (Динамический массив для хранения разных типов данных)
  • Страница 1 из 1
  • 1
Поиск: