Русское сообщество по скриптингу

Упакованные и распакованные строки

Статьи или фрагменты кода для новичков и уже опытных скриптеров по AMXX.

Модератор: Chuvi

Правила форума
1. Запрещено материться и оскорблять других участников форума.
2. Запрещен флуд, оффтоп, дабл постинг во всех разделах форума, кроме раздела "Болтовня".
3. Запрещено взламывать сайт/форум или наносить любой вред проекту.
4. Запрещено рекламировать другие ресурсы.
5. Запрещено создавать темы без информативного названия. Название темы должно отображать ее смысл.

В данном разделе форума разрешено создавать темы, касающие только обучающему материалу по AMX Mod X.

Упакованные и распакованные строки

Сообщение DJ_WEST » 08 окт 2009, 15:13

Автор: Bugsy
Перевод: 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})
        // Игрок не на сервере
} 
 
Не пишите мне в ЛС: если вам нужна помощь на бесплатной основе. Любые вопросы на форум.
Аватара пользователя
DJ_WEST
Администратор
 
Сообщения: 3641
Зарегистрирован: 22 авг 2009, 00:38
Благодарил (а): 48 раз.
Поблагодарили: 2209 раз.
Опыт программирования: Больше трех лет
Языки программирования: Counter-Strike 1.6
Counter-Strike: Source
Left 4 Dead
Left 4 Dead 2

Re: Упакованные и распакованные строки

Сообщение PRoSToTeM@ » 03 мар 2011, 20:54

Такой вопрос для хранения для каждого игрока число от 0 до 255 лучше использовать запакованные строки чем обычные массивы?
Что-то типа:
[pawn]
new health[33 char]
 
[/pawn]
Будет оптимизированее чем:
[pawn]
new health[33]
 
[/pawn]
Аватара пользователя
PRoSToTeM@
Скриптер
 
Сообщения: 2498
Зарегистрирован: 26 мар 2010, 00:12
Благодарил (а): 438 раз.
Поблагодарили: 1125 раз.


Вернуться в Статьи / фрагменты кода

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 4