Wednesday, June 4, 2014

Bits, Bit-fields, and Bit-wise Operators

Hướng dẫn này bao gồm các vấn đề cơ bản của các bit, bit-fields, và toán tử bit-wise và được viết trên một mức độ rất cơ bản mà tất cả mọi người có thể hiểu được. Tôi biết một hướng dẫn tương tự đã tồn tại nhưng trong quan điểm của tôi nó không phải là rất rõ ràng nếu người đọc không có bất kỳ kinh nghiệm với chủ đề. Tôi cũng bao gồm các ví dụ cơ bản về cách bit-fields có thể được sử dụng trong plugins. Xin vui lòng cho tôi biết về bất kỳ lỗi \ lỗi chính tả hoặc đề nghị. Ngoài ra, nếu bạn đã đọc và vẫn không chắc chắn về một cái gì đó, hãy cho tôi biết để tôi có thể giải thích thêm.

What is a bit-field?

A bit trường là một loạt các bit liền kề bên trong ô bộ nhớ (không mảng). Số bit trong một bit-field nhất định được xác định bởi số lượng các byte được phân bổ cho các dữ liệu loại đang được sử dụng. Theo mặc định, tất cả các bit trong một bit -field là 0. Khi không bit là 1 trong một bit-field nhất định, giá trị kết quả sẽ là 0. Nếu một hoặc nhiều bit là 1 trong một bit-field, số lượng kết quả sẽ được khác không. AMX-X chỉ có 1 dữ liệu kiểu đó là một ô 4-byte, vì thế nó được giới hạn đến 32-bit trong một bit-field duy nhất. Nếu bạn cần thao tác bit trên 32, xem các bit-lĩnh vực mảng ví dụ dưới đây.

Ví dụ về kích thước của bit-field:

  • 1-bytes = 8 bits == 0000 0000
  • 2-bytes = 16 bits = 0000 0000 0000 0000
  • 4-bytes = 32 bits = 0000 0000 0000 0000 0000 0000 0000 0000
How are numbers stored on a computer? - Làm thế nào con số được lưu trữ trên một máy tính?

Tất cả các thông tin trên một máy tính được lưu trữ trong bộ nhớ (RAM, ROM, cứng đĩa, bộ nhớ flash, vv). Ở mức thấp nhất, bộ nhớ bao gồm nhiều nhiều nhiều công tắc on-off (được gọi là bit). Một số nguyên là kết quả của một sự kết hợp của bit (s) được thiết lập để 1 hoặc 0. Nếu bạn thấy tôi đề cập đến một số nhị phân, điều này có nghĩa là các bit đại diện của một số nguyên.

Mỗi bit cooresponds đến một lũy thừa của 2. Trong ví dụ này tôi sẽ sử dụng 8 bit chỉ là một ví dụ nhanh chóng như thế nào bit tạo thành một số. Bit đặt hàng (tôi không nhận được vào endianness) như sau: 7654 3210

Các giá trị dưới đây tương ứng với mỗi của các bit. Như bạn có thể thấy, nếu một bit cho là 1 trong một bitfield, giá trị số nguyên sẽ tăng giá trị tương ứng cho lũy thừa  tương ứng của 2.

1 === 2 power of 0 -- Stored in bits: 0000 0001 (binary representation)
2 === 2 power of 1 -- Stored in bits: 0000 0010
4 === 2 power of 2 -- Stored in bits: 0000 0100
8 === 2 power of 3 -- Stored in bits: 0000 1000
16 == 2 power of 4 -- Stored in bits: 0001 0000
32 == 2 power of 5 -- Stored in bits: 0010 0000
64 == 2 power of 6 -- Stored in bits: 0100 0000
128 = 2 power of 7 -- Stored in bits: 1000 0000

Suppose we have a variable holding the number 97, our bit-field would appear as: 0110 0001

Notice bits 1, 6, and 7 are set. We can then add each corresponding number (or 2 power of bit#) to our resulting number.

1 == 2 power of 0 -- 1
2
3
4
5
6 == 2 power of 5 -- 32
7 == 2 power of 6 -- 64
8 = 

Or 2^0 + 2^6 + 2^7 = 1 + 32 + 64 = 97

What are the bit-wise operators and how do I use them?

  • & - And
  • | - Or
  • ^ - Xor (Exclusive Or)
  • ~ - Not (Or Ones-complement)
  • << - Left bit-shift
  • >> - Right bit-shift

Operator: &
Function: And
Description:
 This operator is used to check if and only if both of the corresponding bits in both operands are 1. The resulting bit-field will consist of bits (1's) that were present at the same location within both operands.

15 & 3 = 3

15 = 0000 1111
03 = 0000 0011
03 = 0000 0011
_________________

145 & 97 = 1

145 = 1001 0001
097 = 0110 0001 
001 = 0000 0001
_________________

255 & 234 = 234

255 = 1111111
234 = 1111010
234 = 1111010

Operator: |
Function: Or
Description:
 This operator is used to check if one or both bits are 1 in the corresponding bit positions of the two operands. The resulting bit-field will consist of bits (1's) that were present in either of the operands for each bit location.

15 | 3 = 15

15 = 0000 1111
03 = 0000 0011
15 = 0000 1111
_________________

145 | 97 = 241

145 = 1001 0001
097 = 0110 0001
241 = 1111 0001
_________________

255 | 234 = 255

255 = 1111 1111
234 = 1111010
255 = 1111 1111

Operator: ^
Function: Xor (Exclusive Or)
Description: 
This operator is used to check where the corresponding bits in the two operands are different. The resulting bit-field will consist of bits (1's) that were opposite (0 & 1 or 1 & 0) at the same location within both operands.

15 ^ 3 = 12

15 = 0000 1111
03 = 0000 0011
12 = 0000 1100
_________________

145 ^ 97 = 240

145 = 1001 0001
097 = 0110 0001
240 = 1111 0000
_________________

255 ^ 234 = 21

255 = 1111 1111
234 = 1110 1010
021 = 0001 0101

Operator: ~
Function: Not
Description: 
Not takes the binary representation of a number, and turns all zeros into ones, and ones into zeros. Unlike the previous 3 bitwise operators [AND, OR, XOR], NOT takes just one operand

0000 1111 = 15
1111 0000 = ~15 = 240
_________________

1010 1010 = 170
0101 0101 = ~170 = 85
_________________

Operator: <<
Function: Left bit-shift
Description: 
Shift bits in operand to the left by specified bit positions.

X << Y

This will shift bits in the X operand Y bits to the left. Any bits to the left that fall out of the bit-range will be dropped. Any bits added to the right due to the shift are 0. This example assumes a 1-byte memory cell so any bits shifted left past the 8th bit are dropped; if you are using a larger memory cell (in AMXX, a cell is 4-bytes or 32-bits) these bits will not be dropped but moved past the 8th bit to bits 9-32).

15 << 4

015 = 0000 1111
030 = 0001 1110 [All bits shifted 1 position]
060 = 0011 1100 [All bits shifted 2 positions]
120 = 0111 1000 [All bits shifted 3 positions]
240 = 1111 0000 [All bits shifted 4 positions]
_________________

209 << 2

209 = 1101 0001 
162 = 1010 0010 [All bits shifted 1 position]
068 = 0100 0100 [All bits shifted 2 positions]

Operator: >>
Function: Right bit-shift
Description: 
Shift bits in operand to the right by specified bit positions.

X >> Y

This will shift bits in the X operand Y bits to the right. Any bits to the right that fall out of the bit-range will be dropped. Any bits added to the left due to the shift are 0.

15 >> 4

015 = 0000 1111
007 = 0000 0111 [All bits right-shifted 1 position]
003 = 0000 0011 [All bits right-shifted 2 positions]
001 = 0000 0001 [All bits right-shifted 3 positions]
000 = 0000 0000 [All bits right-shifted 4 positions]
_________________

209 >> 2

209 = 1101 0001 
104 = 0111000 [All bits right-shifted 1 position]
052 = 0011 0100 [All bits right-shifted 2 positions]

Common bit-field operations

Setting bit(s) to 1\true in a bit-field 

This is done using the bit-wise OR operator |. As explained above, this will set the bit to true if it exists in either operand. 

Assume an empty variable which we want to set bit 4 to true.

Set a single bit
iVariable = iVariable | ( 1 << 4 );

0000 0000 = iVariable (freshly declared, no value set)
0001 0000 = 1 << 4
0001 0000 = iVariable | ( 1 << 4 )

Set multiple bits in the same expression
iVariable = iVariable | ( ( 1 << 4 ) | ( 1 << 5 ) )

0000 0000 = iVariable
0001 0000 = 1 << 4
0010 0000 = 1 << 5
0011 0000 = iVariable | ( ( 1 << 4 ) | ( 1 << 5 ) )

Setting bit(s) to 0\false in a bit-field

This is done using a combination of bit-wise AND & and the NOT ~ operator. 

Assume a bit-field with bits 4 and 5 set.

iVariable = ( ( 1 << 4 ) | ( 1 << 5 ) )

Suppose we need to set bit 4 to 0\false.

iVariable = iVariable & ~( 1 << 4 )

0011 0000 = ( ( 1 << 4 ) | ( 1 << 5 ) ) 
1111111 = ~( 1 << 4 )
0010 0000 = ( ( 1 << 4 ) | ( 1 << 5 ) ) & ~( 1 << 4 )

The same can be done for setting multiple bits to 0\false in a single expression. You only need to bit-wise OR the bits you want set to 0.

iVariable = iVariable & ~( ( 1 << 4 ) | ( 1 << 5 ) )

Using bit-fields
In a bit-field you can set bit(s) to 1, set bit(s) to 0, check if a particular bit(s) is 0/1, and utilize the integer that results from the bit-field. This makes bit-fields very useful and efficient.

Example 1, using bit-fields as a boolean

A useful application is if you want to store if a player is admin when he connects to the server. Common practice is to create an array of cells, using one cell for each player's true\false value.

Boolean array method:

PHP Code:
new boolg_IsAdmin[33];

public 
client_putinserverid )
{
    
g_IsAdminid ] = boolis_user_adminid );
}

public 
client_disconnectid )
{
    
g_IsAdminid ] = false;
}  
If player id's 2 and 6 are admins, elements g_IsAdmin[2] and g_IsAdmin[6] will be true.

A cell is 4-bytes so using an array for this purpose requires 132 bytes of memory (33-cells times 4-bytes).

If a bit-field is used instead, you will only declare a single cell and that can store the same info but using only 4 bytes! 

Bit-field method

PHP Code:
new g_IsAdmin//Only need a single cell of memorypublic client_putinserverid )
{
    if ( 
is_user_adminid ) )
            
g_IsAdmin |= ( << ( id 31 ) );
}

public 
client_disconnectid )
{
    
g_IsAdmin &= ~( << ( id 31 ) );
}  
You may be wondering why 1 is being shifted left by ( id & 31 ). This is because a 4-byte data-type is limited to 32 bits which means the maximum shift left that can be done without losing data is 31. Since player-ids in Half-Life can range from 1-32, we can not do 1 << 32 so doing a ( id & 31 ) will give us a usable value. When ( # & 31 ) is used, the value will be equal to the player id except when id == 32, when id == 32, the resulting value is 0. In a nutshell, player-id's 1-31 are returned as normal but when the player id is 32, 0 is returned so we are essentially using 0-31 instead of 1-32 which is exactly what we need.

In the situation explained above where player id's 2 and 6 are online admins, our bit-field would look like the below. If you are wondering why the bits are not at the shift position based on player id, refer to the ( id & 31 ) explained above.

0000 0000 0000 0000 0000 0000 0100 0100

A bonus for using bit-fields for storing boolean values is you can quickly check if any bits are set, or in this case, if any admins are online. Remember, if no bits are 1 in a given bit-field, the resulting value is 0; like-wise, if there are 1 or more bits set to 1 in the bit-field, the resulting number is non-zero.

PHP Code:
if ( g_IsAdmin )
    
//there are 1 or more admins onlineelse
    
//there are no admins  
If an array was used, you would need to use a loop to iterate through all elements in the array to check if each value is 1. This is what makes bit-fields not only more memory efficient but also CPU efficient and easier (less code).

Example 2, boolean for weapon index(s)

For this example I will show how you can flag various weapons in a bit-field. This can be used in a weapon restriction plugin or any plugin that needs a true\false value for each weapon. This is possible because weapons in CS have indexes that range from 1-32. In this example we will be creating a bit-field to represent weapons CSW_SG550 [13], CSW_AWP [18], & CSW_G3SG1 [24]. The highest index for a weapon (primary\secondary) is 30 (P90) so you do not need to worry about using ( # & 31) or anything like that.

g_Weapons = ( 1 << CSW_SG550 ) | ( 1 << CSW_AWP ) | ( 1 << CSW_G3SG1 );

This is what will occur with the above operation

0000 0000 0000 0000 0000 0000 0000 0001 = 1 (How 1 is represented on the bit-level. We are going to shift this bit to each weapon index bit position)

0000 0000 0000 0000 0010 0000 0000 0000 = 1 << CSW_SG550 (1 left-shifted by CSW_SG550 [13])
0000 0000 0000 0100 0000 0000 0000 0000 = 1 << CSW_AWP (1 left-shifted by CSW_AWP [18])
0000 0001 0000 0000 0000 0000 0000 0000 = 1 << CSW_G3SG1 (1 left-shifted by CSW_G3SG1 [24])
0000 0001 0000 0100 0010 0000 0000 0000 = g_Weapons (The result)

Remember, we are using Or for the above operation which will set each bit-position to 1 if it exists in either operand. This is why g_Weapons now has each bit set in the appropriate bit-position.

Now that we have a bit-field with our desired weapons, we can then check the weapon bit with the following:

PHP Code:
if ( g_Weapons & ( << CSW_AWP ) )
    
//weapon is in bitfield  
What is occurring with this statement is we are taking 1 and pushing the bit to the left by CSW_AWP [18] bits. This will set the 18th bit to 1 so we will be able to check it against g_Weapons to see if both bits exist in the position. Remember that the & operator checks if the bit is 1 at bothcooresponding bit positions in each operand. 

0000 0000 0000 0000 0000 0000 0000 0001 = 1 (Number 1 represented in bits)

0000 0000 0000 0100 0000 0000 0000 0000 = 1 << CSW_AWP (Left bit-shift of 18 bits)
0000 0001 0000 0100 0010 0000 0000 0000 = g_Weapons
0000 0000 0000 0100 0000 0000 0000 0000 = Bits in the CSW_AWP [18] position do both exist so this would return true

Suppose we want to check if scout is in our bit-field. CSW_SCOUT = index 3

PHP Code:
if ( g_Weapons & ( << CSW_SCOUT ) )
    
//That weapon is NOT in bitfield  
0000 0000 0000 0000 0000 0000 0000 0001 = 1

0000 0000 0000 0000 0000 0000 0000 1000 = 1 << CSW_SCOUT (Left bit-shift of 3 bits)
0000 0001 0000 0100 0010 0000 0000 0000 = g_Weapons
0000 0000 0000 0000 0000 0000 0000 0000 = Bits in the 4th bit (1<<3) position do not both exist so this would return false

Example 3, using a bit-field for your plugins flags\options.

PHP Code:
#define MAX_PLAYERS    32

//Create a list of options that are supported by the plugin
enum ( <<=_Options{
    
Speed=1// 1 << 0 or 1
    
AutoAim,  // 1 << 1 or 1 << 1 (as value is calculated by enum)    
    
HighJump// 1 << 2 or 2 << 1
    
ExtraHP,   // 1 << 3 or 4 << 1
    
ExtraArmor // 1 << 4 or 8 << 1}//Define an array that will keep track of each option a player currently hasnew g_OptionsMAX_PLAYERS+];//Examples of how to apply option for playerg_Optionsid ] |= Speed;g_Optionsid ] |= HighJump;g_Optionsid ] |= ExtraArmor;//Example how to check if a player has an optionif ( g_Optionsid ] & Speed )
    
//player has speed  
If you were to do the above with an array of booleans you would need to create a 2-dimension array as demonstrated below:
PHP Code:
#define MAX_PLAYERS    32

//Create options as normal enum (no bitshift)
enum _:Options{
    
Speed,   //0
    
AutoAim,   //1
    
HighJump,  //2
    
ExtraHP,   //3
    
ExtraArmor //4}

new 
g_bOptionsMAX_PLAYERS+][ Options ];//Examples of how to apply option for playerg_Optionsid ][ Speed ] = true;g_Optionsid ][ HighJump ] = true;g_Optionsid ][ ExtraArmor ]  true;//Example how to check if a player has an optionif ( g_Optionsid ][ Speed ] )
    
//player has speed  
Useful Macros
To easily manipulate bits in your plugin to work with players (or any 1-32 indexes) without having to re-write the bit operations each time, here are some macros. Remember, the max bit you can manipulate is 32 in AMX-X since the only data-type available is a 4-byte cell [32 bits]. 

PHP Code:
//If you are storing a player index or anything that has an index up to 32.
//Example: SetPlayerBit( BitFieldVar , # )
#define SetPlayerBit(%1,%2)      (%1 |= (1<<(%2&31)))
#define ClearPlayerBit(%1,%2)    (%1 &= ~(1 <<(%2&31)))
#define CheckPlayerBit(%1,%2)    (%1 & (1<<(%2&31)))

//Safe for using values 0-31.
//Example: SetBit( BitFieldVar , # )
#define SetBit(%1,%2)      (%1 |= (1<<%2))
#define ClearBit(%1,%2)    (%1 &= ~(1<<%2))
#define CheckBit(%1,%2)    (%1 & (1<<%2))  
The benefits of using bit-fields
A major benefit is conserving memory and CPU but IMO they are, in most cases, easier than using an array of bools. As you saw towards the beginning of this tutorial, you save 131 bytes of memory by using a bit-field over an array for storing player boolean (true\false) values. It also can result in less code because you can quickly check if bits exist or not with a simple if-statement; using an array you would need some type of loop to check each element.

Manipulating bits outside of the bit-range of a memory cell
This can be done through the usage of a bit array. This will simply use the first array element for bits 1-32, the second element for bits 33-64, and so on. There is not max-bit limitation on a bit-array as there is with a normal bit-field.

PHP Code:
g_BitField[0//bits 1-32g_BitField[1//bits 33-64g_BitField[2//bits 65-96  
Here's an example on how to setup your plugin to work with unlimited bits:
PHP Code:
#define SetBit(%1,%2)      (%1[%2>>5] |= (1<<(%2 & 31)))
#define ClearBit(%1,%2)    (%1[%2>>5] &= ~(1<<(%2 & 31)))
#define CheckBit(%1,%2)    (%1[%2>>5] & (1<<(%2 & 31)))  
PHP Code:
//Set the highest bit that we need to access
#define MAX_BIT    515

//Define an array large enough to contain all of the needed bits (1 -> MAX_BIT)
new iBitfieldArray[ ( MAX_BIT 32 ) + ];//or you can donew iBitfieldArray[ ( MAX_BIT >> ) + ];//Set some random bitsSetBitiBitfieldArray 256 );SetBitiBitfieldArray 500 );//Check if the above worked successfullyif ( CheckBitiBitfieldArray 256 ) )
    
ClearBitiBitfieldArray 256 );

if ( 
CheckBitiBitfieldArray 20 ) )//falseClearBitiBitfieldArray 256 );  
If you want to practice your knowledge, you can do bit-operations with calculators that exist on the net, here's one, many others exist. Also, the calculator that ships with Windows can do bit operations and even convert an integer value to its binary representation. To access it, click View->Scientific. Enter a number, and towards the top left, under the white number display box, click the Bin option and it will display the bits representing that integer number. You can also do all of the explained bit operators with the exception of bit-shifting (can be done mathematically, though).

Experimenting with binary numbers
Pawn has a constant (0b) that allows you to work with values in binary directly. To use it, prefix a set of bits with 0b; the number 10 in bits is represented by 1010 so to store this value in a variable, you would do iVar = 0b1010. You can practice your knowledge using this if you are still uncomfortable after reading this tut. This functions identically to the hexadecimal prefix 0x that you may have seen used elsewhere.

A few more examples:

PHP Code:
i1 0b1;i2 0b10;i10 0b1010;i15 0b1111;i255 0b11111111;  
Operation example:
PHP Code:
new iNum1 0b1011//11new iNum2 0b1111//15iResult iNum1 iNum2;0b1011 iNum1//110b1111 iNum2//150b1011 iResult//15  
Interesting reading once you have an understanding of bits: Link

0 nhận xét:

Post a Comment