Garry's Mod Wiki

Revision Difference

bitwiseguide#562063

<cat>Dev.Lua</cat>⤶ <title>Bitwise Guide</title>⤶ ⤶ # Bitwise Explanation⤶ Hopefully as you know, everything in computers in made of binary at its lowest level. Every 1 and 0 in binary is called a bit, and every 8 bits is called a byte. This structure forms everything on the computer representing character and numbers to more complex operations to advanced to cover here. ⤶ ⤶ [Bitwise Operations](https://en.wikipedia.org/wiki/Bitwise_operation) are functions designed to manipulate these bits. This can be used for various reason but commonly its used to manually flip the binary 1's and 0's in order for the programmer to store new information in the same amount of space. You can see these functions here in the <page>bit</page> library.⤶ ⤶ # Explanation⤶ Now that you have some fundamental understanding of what bitwise operations lets walk through and example to reinforce your understanding.⤶ ⤶ The following is a code snippet that "packs" a RGBA value into a 32 bit long int value (represented by [Hexadecimal](https://en.wikipedia.org/wiki/Hexadecimal) form).⤶ ```⤶ -- These are forward declarations while useful, are not needed for bitwise operations. ⤶ local band = bit.band⤶ local bor = bit.bor⤶ local lshift = bit.lshift⤶ local rshift = bit.rshift⤶ ⤶ -- Packing the RGBA value⤶ local function rgb_to_hex(r,g,b, a)⤶ a = a or 255⤶ return bor(lshift(r, 24), lshift(g, 16), lshift(b, 8), a)⤶ end⤶ ⤶ -- Unpacking the RGBA value⤶ local function hex_to_rgb(hex)⤶ return rshift(hex, 24), rshift(band(hex, 0x00ff0000), 16), rshift(band(hex, 0x0000ff00), 8), band(hex, 0x000000ff)⤶ end⤶ ⤶ ```⤶ ⤶ Now lets break this down operation by operation so you can understand how we use bitwise operations to manually flip these bits.⤶ ⤶ ## Packing Process⤶ before we begin lets first visualize the length of binary that we wanna return. because we are wanting to return a number, we will want to return 32 bits:⤶ ```⤶ 00000000 00000000 00000000 00000000⤶ ```⤶ Each space represent the 8 bits that make up 1 byte.⤶ ⤶ For this example our `r` value will be 255 (in binary its `11111111`) lets add this to our int from step 1:⤶ ```⤶ 00000000 00000000 00000000 00000000 --Empty Int⤶ ⤶ 00000000 00000000 00000000 11111111 -- 255 ⤶ ```⤶ ⤶ now in order to add more bits to this int we will first need to move our value of 255 out of the way, we do this using the <page>bit.lshift</page> function which will take the value we wanna use (`255 or 11111111`) and move it to the left but `n` amount of bits.⤶ ⤶ since we are wanting to move our value to the beginning byte we will need to move it left by 24 bits⤶ ```⤶ 00000000 00000000 00000000 00000000 --Empty Int⤶ ⤶ 00000000 00000000 00000000 11111111 -- 255 ⤶ ⤶ bit.lshift(255,24)⤶ ⤶ 11111111 00000000 00000000 00000000⤶ ⤶ ```⤶ ⤶ we will do this operation for every subsequent RGBA component:⤶ ```⤶ r = 255 or 11111111⤶ g = 200 or 11001000⤶ b = 150 or 10010110⤶ a - 255 or 11111111⤶ ⤶ --Pseudo Code⤶ 00000000 00000000 00000000 00000000 --Empty Int⤶ ⤶ bit.lshift(r,24) -- Adding the r value of 255 and moving it left 24 bits⤶ ⤶ 11111111 00000000 00000000 00000000⤶ ⤶ bit.lshift(g,16) -- Adding the g value of 200 and moving it left 16 bits⤶ ⤶ 11111111 11001000 00000000 00000000⤶ ⤶ bit.lshift(b,8) -- Adding the b value of 150 and moving it left 8 bits⤶ ⤶ 11111111 11001000 10010110 00000000⤶ ```⤶ ⤶ Now because we only have the last 8 bits (or byte) left we can just add our `a` value to the end with our final bitwise operation.⤶ ⤶ - The `bit.bor` operation only exists here to encompass everything as a bitwise number, it servers no logical benefit TO THIS SNIPPET SPECIFCALLY.⤶ ```⤶ -- Adding our A value of 255 which naturally takes place at the end⤶ bit.bor(bit.lshift(r, 24), bit.lshift(g, 16), bit.lshift(b, 8), a) ⤶ ⤶ 11111111 11001000 10010110 11111111⤶ ⤶ -- Note: The binary sequence we just put together can still be represented in decimal or hexadecimal form like any other number⤶ 4291335935 -- Decimal form⤶ FFC896FF -- Hexadecimal ⤶ ```⤶ ⤶ And that's it for the packing process, what we just completed was the process of taking 4 numerical values and representing them using 1 larger value. Now for the ⤶ ⤶ ### The Why ⤶ Its mainly to save on storage, this can easily be seen if we just compare the actual bits of information the computer sees:⤶ ⤶ ```⤶ -- If our goal is to store our 4 numbers (255, 200, 150, 255) Here is how the computer would normally store them⤶ 11111111 00000000 00000000 00000000⤶ 00000000 11001000 00000000 00000000 ⤶ 00000000 00000000 10010110 00000000⤶ 00000000 00000000 00000000 11111111⤶ ⤶ -- You see all the extra bits each number takes just to be represented properly...⤶ -- What we accomplished using our bitwise functions was turn all those numbers into:⤶ 11111111 11001000 10010110 11111111⤶ ⤶ -- Now as you can see we are not wasting extra bits of information ⤶ ```⤶ ⤶ ⤶ ## Unpacking Process⤶ Now that you know what we are doing the unpacking process is a lot easier. we just simply need to extract our information back out.⤶ ⤶ All we have to do to is isolate the values out that we wanna extract, we can do this by using the hexadecimal values ⤶ ```⤶ 0x00ff0000 == 11111111 00000000 00000000⤶ 0x0000ff00 == 11111111 00000000⤶ 0x000000ff == 11111111⤶ ⤶ -- to extract the our first byte from:⤶ 11111111 11001000 10010110 11111111⤶ ⤶ we will need to align the bits so our comparison works⤶ ⤶ so we will need to shift our hex by 24 bytes to the right⤶ ⤶ ```⤶ ⤶ ⤶ ⤶ ⤶