Перевод: DJ_WEST
Распакованные строки
Распакованные строки - это один из типов строк, который мы обычно используем в AMXX плагинах. Это массив из 4-ех байтовых ячеек, где каждая ячейка хранит индивидуальный символ строки. Каждый символ строки (как у упакованной, так и у распакованной) представляются ввиде числа от 0 до 255. Для представления числа 0-255 нужен только 1 байт. Но распакованные строки для хранения одного символа используют 4 байта, поэтому получается, что в ячейки не используются остальные 3 байта памяти. Вот пример:
Байт состоит из 8 битов, а ячейка памяти состоит из 4 байтов, следовательно из 32 битов. Данный пример показывает, как у распакованной строки хранится символ в ячейке памяти. Только биты 1-8 используются, потому что это все, что необходимо для хранения значения от 0 до 255.
0000 0000 0000 0000 0000 0000 0000 0000 = 0
0000 0000 0000 0000 0000 0000 1111 1111 = 255
Красные биты - это память, которая не используется. Не нужная память составляет 3 байта (24 бита) в ячейке.
Упакованные строки
Упакованные строки - это также массив из 4-ех байтовых ячеек, но схема размещения памяти другая, где вся память ячейки испольуется. В результате для размещения каждого символа необходим 1 байт (8 бит) вместо 4 байтов (32 битов) у распакованных строк. Когда вы используете упакованную строку, то в ячейки будет уже 4 символа, вместо одного у распакованных.
Примечание: Упакованная строка может хранить символы только одно-байтовой кодировки, такие как ASCII, или один из расширенных наборов ASCII.
Например:
Данный пример показывает представление 4-ех байтовой ячейки у упакованной строки. Каждый цветной байт хранит один символ строки. Вся память используется. Упакованный вариант слова "pack":
0111 0000 0110 0001 0110 0011 0110 1011
Когда нужно использовать упакованные строки, а когда распакованные?
Упакованные:
- Хранение численных значений в диапозоне 0-255
- Хранение ASCII текста
Распакованные:
- Хранение численных значений больше 255
- Хранение юникод текста или стандарта не ASCII
Как использовать упакованные строки в AMXX скриптинге?
Использование упакованных строк очень похоже на использование распакованных за исключением доступа к строке. Вы не можете выводить (печатать) или делать манипуляцию с упакованной строкой, как бы мы это делали с распакованной. Вы сначала должны ее распаковать, а затем уже выводить/форматировать и так далее.
Здесь приведены две простые функции для упаковки и распаковки строк:
- Код: Выделить всё
public strpack(const s_Unpacked[], s_Packed[])
{
new i
while((s_Packed{i} = s_Unpacked[i++])) {}
}
public strunpack(const s_Packed[] , s_Unpacked[])
{
new i
while((s_Unpacked[i] = s_Packed{i++})) {}
}
При объявления переменной нужно указывать желательный размер и добавлять 'char' идентификатор после него. Вы также должны указывать символ '!' перед присвоением строки переменной.
Пример:
- Код: Выделить всё
new s_String[11 char] = !"The string"
Доступ к ячейке строки похож на доступ к элементу распакованной строки, только вместо [] нужно использовать {}.
Пример:
- Код: Выделить всё
Распакованная строка = s_String[5]
Упакованная строка = s_String{5}
Сколько памяти я съэкономлю при использовании упакованных строк?
Количество сохраненной памяти меняется в зависимости от числа и размера строк, используемых в плагине. Для определения используемой памяти был написан плагин. По умолчанию, он использует упакованные строки. Для того чтобы получить результат использования распакованных строк нужно закомментировать строчку:
- Код: Выделить всё
#defined PACKED_STRINGS
- Код: Выделить всё
#include <amxmodx>
#define PLUGIN "Packed vs Unpacked"
#define VERSION "1.0"
#define AUTHOR "bugsy"
#define PACK_STRINGS
#define NUM_STRINGS 20
#define MAX_STRING_SIZE 512
#if defined PACK_STRINGS
new g_szUnpackBuffer[MAX_STRING_SIZE];
new g_szStrings[NUM_STRINGS][MAX_STRING_SIZE char]
#else
new g_szStrings[NUM_STRINGS][MAX_STRING_SIZE]
#endif
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
}
public plugin_cfg()
{
for ( new iTest = 0 ; iTest < NUM_STRINGS ; iTest++ )
{
#if defined PACK_STRINGS
g_szStrings[iTest] = !"Packed strings"
strunpack( g_szStrings[iTest] , g_szUnpackBuffer );
server_print( "%d = %s" , iTest , g_szUnpackBuffer );
#else
g_szStrings[iTest] = "Unpacked strings";
server_print( "%d = %s" , iTest , g_szStrings[iTest] );
#endif
}
}
#if defined PACK_STRINGS
public strunpack( szPacked[] , szUnpacked[] )
{
new i;
while((szUnpacked[i] = szPacked{i++})){}
}
#endif
Вы можете увидеть результат использования разного количество строк размером 512:
- Код: Выделить всё
Кол-во строк = 1
Упакованная строка = 19756 байт
Распакованная строка = 19088 байт
Сохраненная память = -668 байт [Использует больше памяти, когда строка упакована]
Кол-во строк = 5
Упакованная строка = 21820 байт
Распакованная строка = 27296 байт
Сохраненная память = 5476 байт
Кол-во строк = 10
Упакованная строка = 24400 байт
Распакованная строка = 37556 байт
Сохраненная память = 13156 байт
Кол-во строк = 20
Упакованная строка = 29560 байт
Распакованная строка = 58076 байт
Сохраненная память = 28516 байт
Кол-во строк = 30
Упакованная строка = 34720 байт
Распакованная строка = 78596 байт
Сохраненная память = 43876 байт
Кол-во строк = 50
Упакованная строка = 45040 байт
Распакованная строка = 119636 байт
Сохраненная память = 74596 байт
Кол-во строк = 100
Упакованная строка = 70840 байт
Распакованная строка = 222236 байт
Сохраненная память = 151396 байт
Дополнение
Как уже говорилось раньше упакованная строка использует все 4 байта ячейки. Рассмотрим пример, где нам нужно упаковать строку из 5 символов. Теоретически, для хранения этих символов нам нужно 5 байтов. Все верно, для хранения строки нам нужно 5 байтов, но нам нужно будет 2 ячейки памяти, потому что одна ячейка может хранить только 4 символа. Поэтому мы будем использовать первые 4 символа строки в первой ячейки, используя все ее 4 байта, а затем использовать один байт второй ячейки для хранения последнего символа. А также один байт для нулевого символа, который сообщает о конце строки. Левая часть второй ячейки будет пустая и называется она "дополнением". Это не будет происходить в том случае, если ваш размер строки кратен 4.
Примеры скриптов, использующих упакованные строки
- Код: Выделить всё
// Данный скрипт объявляет упакованные и распакованные строки, а затем
// показывает значения каждой ячейки и в конце показывает размер каждой строки (в ячейках).
// Результат: Result: 0 = 0 -> Result: 31 = 31
// Упакованный размер = 9 ячеек (смотри "Дополнение" выше)
// Распакованный размер = 33 ячейки
new s_Packed[33 char]
new s_Unpacked[33]
for (new i = 0 ; i < 33; i++)
{
s_Packed{i} = i
s_Unpacked[i] = i
server_print("Result: %d = %d", s_Packed{i}, s_Unpacked[i])
}
server_print("Packed Size = %d cells", sizeof szPacked)
server_print("Unpacked Size = %d cells", sizeof szUnpacked)
- Код: Выделить всё
// Данный скрипт выводит ASCII символ каждой ячейки
// в строке "Packed"
new s_Packed[7 char] = !"Packed"
for (new i = 0; i < 7; i++)
server_print("%d" , s_Packed{i})
// Результат: 80, 97, 99, 107, 101, 100, 0
// P=80 a=97 c=99 k=107 e=101 d=100 NULL=0
- Код: Выделить всё
// Объявление упакованного массива для хранение булевых значений игроков
new g_s_Online[33 char]
public client_putinserver(id)
{
g_s_Online{id} = 1
}
public client_disconnect(id)
{
g_s_Online{id} = 0
}
public YourFunc(id)
{
if (!g_s_Online{id})
// Игрок не на сервере
}