|
Coldfusion takes a byte! Coldfusion developers have the ability to do some 'old school' style binary development with the BitAnd, BitOr, BitNot and BitXOr logical bit functions, along with the BitSHLN, BitSHRN, BitMaskSet, BitMaskRead and BitMaskClear functions. In this article we'll explore the use of BitAnd and a 32-bit long byte for manipulation of access controls. For the uninitiated, however, let's have a first look at bits and bytes. What is a bit and a byte? A bit is a single boolean true (1) or false (0) part of a larger structure known as a byte. The bit position and value within a string of bits (e.g. a byte) determine the total value of that byte. Each bit can have a logical (true/false) value of 1 or 0. Bit position affects the value of the byte, starting from the right with a value of 1, and with each bit in the byte multiplied of the previous bit by two as you move left. This is best illustrated with examples: Example 1 Let's look at the simplest byte, made up of only two bits. The rightmost bit, with a positional value of 1, the leftmost bit with a positional value of 2 (1*2). This two-bit byte has four possible combinations: 00 = Zero. No bit positions are set, so the value remains zero. 01 = One. Since the rightmost bit position has a value of 1, and the rightmost bit is set true (1) while the leftmost bit is set false (0), the byte has a value of 1. 10 = Two. Since the leftmost bit position has a value of 2, and the leftmost bit is set true (1) while the rightmost bit is set false (0), the byte has a value of 2. 11 = Three. Huh? This is where most people begin to get confused. Since the leftmost bit position has a value of 2 (1*2) and is set true (1) and the rightmost bit position has a value of 1 and is also set true (1), they are added together and the byte has a value of 3. Example 2 Now let's look at the next simplest byte, made up of four bits (bytes are almost always an even number of bits for the purpose of web development discussions - bits at the hardware level behave a bit differently (pardon the pun), and are frequently odd-numbered). In this case, the positional values of the rightmost bit is still 1 (always), the second bit (from the right) retains it's positional value of 2 (1*2). The next bit over, third from the right, has a positional value of 4 (2*2), and the leftmost bit (fourth from the left) has a positional value of 8 (4*2). This byte has 16 possible combination's: | Binary | Hexadecimal
| Decimal | Positional Math
| | 0000 | 0 | 00 | (0 + 0 + 0 + 0) | | 0001 | 1 | 01 | (0 + 0 + 0 + 1) | | 0010 | 2 | 02 | (0 + 0 + 2 + 0) | | 0011 | 3 | 03 | (0 + 0 + 2 + 1) | | 0100 | 4 | 04 | (0 + 4 + 0 + 0) | | 0101 | 5 | 05 | (0 + 4 + 0 + 1) | | 0110 | 6 | 06 | (0 + 4 + 2 + 0) | | 0111 | 7 | 07 | (0 + 4 + 2 + 1) | | 1000 | 8 | 08 | (8 + 0 + 0 + 0) | | 1001 | 9 | 09 | (8 + 0 + 0 + 1) | | 1010 | A | 10 | (8 + 0 + 2 + 0) | | 1011 | B | 11 | (8 + 0 + 2 + 1) | | 1100 | C | 12 | (8 + 4 + 0 + 0) | | 1101 | D | 13 | (8 + 4 + 0 + 1) | | 1110 | E | 14 | (8 + 4 + 2 + 0) | | 1111 | F | 15 | (8 + 4 + 2 + 1) |
As you continue to move the bit position left, the positional value continues to double the previous value. In this way, an eight bit byte has positional values, from right to left, of 1, 2, 4, 8, 16, 32, 64 and 128: 1
| 1 | 1
| 1
| 1
| 1
| 1
| 1 | | 128 | 64 | 32 | 16 | 8 | 4 | 2
| 1 |
Telling the truth
With binary you're dealing with a boolean value or true (1) or false (0). In this way, bit manipulation deals with truth - what is true about this bit in this byte in comparison to another bit, usually. There are multiple good articles on truth tables online so I will not get into all of them here, however I will illustrate the logical AND and logical OR operations.
Logical AND operations compare two boolean values and return true only if the comparison is true, in other words, only if the same bit is true in each byte being compared. So, logical AND only returns true when both truths being tested are also true, producing the following truth table: TRUTH 1
| TRUTH 2
| RESULT
| | T | T | T | | T | F | F | | F | T | F | | F | F | F |
Logical OR operations also compare two boolean values, however only return true is the comparison is false, in other words, only if the same bit is false in each byte being compared. So, logical OR only returns true when both truths being tested are false, producing the following truth table:
TRUTH 1
| TRUTH 2
| RESULT
| | T | T | F | | T | F | F | | F | T | F | | F | F | T |
Putting it in code The following source code demonstrates how to utilize BitAnd for access control systems - storing up to 32 simultaneous access levels per user. An example of this code in action can be accessed at: http://askjimbob.com/coldfusion_demo_code/using_bit_based_access_control.cfm <!--- Name: using_bit_based_access_control.cfm Author: Denard Springle (
This e-mail address is being protected from spambots. You need JavaScript enabled to view it
) Description: Demonstrate using binary objects and bit manipulation Created: 05/06/2009 License: Creative Commons Attribution-Non Commercial 3.0 ---> <!--- initialize a string to hold the binary representation of our 32-bit zero byte ---> <cfset zeroByte = ""> <!--- initialize the zero byte ---> <cfset zeroByte = RepeatString('0',32)> <!--- set up a row color variable to alternate colors ---> <cfset rC = 0> <!--- set up a table to output the progress and results of this operation --->
<h1>32 Bit Combinations</h1> <p>The following table represents the 32 bit positions available in a 32-bit byte. By using these positions you can assign up to 32 access control levels to the same user without depending on role lists (i.e. IsUserInRole() style). Storage and processing of a single 32-bit byte is demonstrated here in it's most basic form for easy digestion, however the use of single 32-bit byte is an optimal mathmatic calculation and storage method for tiered access controls.</p> <table cellpadding="2" cellspacing="0" width="50%"> <tr> <th style="text-align:center;">Bit Pos</th> <th style="text-align:center;">Bin Val</th> <th style="text-align:center;">Dec Val</th> <th style="text-align:center;">Hex Val</th> </tr> <!--- set up the first row (of zero's) ---> <tr style="background-color:#FFFFEE;"> <td style="text-align:center;">0</td> <td style="text-align:center;">[<cfoutput>#zeroByte#</cfoutput>]</td> <td style="text-align:center;">0</td> <td style="text-align:center;">0</td> </tr> <!--- create a new structure to hold the access controls ---> <cfset thisStruct = StructNew()> <!--- put the zero byte in the structure (for later use) ---> <cfset structInsert(thisStruct, 'this0', zeroByte)> <!--- loop through the 31 assignable bits (e.g. excluding zero [0]) ---> <cfloop from="1" to="31" index="iX"> <!--- initialize a string to hold the binary representation of our 32-bit byte ---> <cfset binaryByte = ""> <!--- set the current column to 32 bits minus the current index of iX, so, bit 31, 30, 29, etc. ---> <cfset thisCol = 32-iX> <!--- loop through the zero bits which make up the beginning (left) of this byte (e.g. thisCol value) ---> <cfloop from="1" to="#thisCol#" index="iY"> <!--- add this bit positions false (0) bit to the byte string ---> <cfset binaryByte = binaryByte & '0'> <!--- loop through the remaining begininng (left) bit positions that are zero ---> </cfloop> <!--- add this bit positions true (1) bit ---> <cfset binaryByte = binaryByte & '1'> <!--- init remaining columns ---> <cfset newCol = 32-(thisCol+1)> <!--- check if we're at the last element in the list ---> <cfif newCol GT 0> <!--- loop through and pad the right with zero (0) ---> <cfloop from="1" to="#newCol#" index="iZ"> <!--- add a zero (1) to the binary string ---> <cfset binaryByte = binaryByte & '0'> <!--- loop through the rest of the padded zero's ---> </cfloop> <!--- end checking if we're at the last element ---> </cfif> <!--- put this byte in the structure (for later use) ---> <cfset structInsert(thisStruct, 'this#iX#', binaryByte)> <cfif rC EQ 0> <cfset rowCol = "##DDDDEE"> <cfset rC = 1> <cfelse> <cfset rowCol = "##FFFFEE"> <cfset rC = 0> </cfif> <!--- output the current bit position and byte values to the screen ---> <cfoutput> <tr style="background-color:#rowCol#;"> <td style="text-align:center;">#iX#</td> <td style="text-align:center;">[#binaryByte#]</td> <td style="text-align:center;">#InputBaseN(binaryByte,2)#</td> <td style="text-align:center;">#FormatBaseN(InputBaseN(binaryByte,2),16)#</td> </tr> </cfoutput> <!--- loop through the rest of the possible byte values ---> </cfloop> <!--- end the table ---> </table> <br> <br> <!--- dump the structure ---> <h1>The structure</h1> <cfdump var="#thisStruct#"> <br> <br> <h1>The Test Case</h1> <p>In this test case we are going to add existing members of the structure (bytes) together. By adding these bytes together and then converting back to base 2 (binary) output you will see how multiple levels could be assigned to the same user.</p> <!--- create a test case by adding together random bits from the 31 bytes created and stored in the structure ---> <cfset testCase = InputBaseN(structFind(thisStruct, 'this11'),2) + InputBaseN(structFind(thisStruct, 'this16'),2) + InputBaseN(structFind(thisStruct, 'this19'),2) + InputBaseN(structFind(thisStruct, 'this2'),2)> <!--- initialize an output string ---> <cfset testCaseOutput = ""> <!--- loop through the missing begining (left) zero bit's ---> <cfloop from="1" to="#32-Len(FormatBaseN(testCase,2))#" index="iX"> <!--- add the missing zero bits ---> <cfset testCaseOutput = testCaseOutput & '0'> <!--- loop through the rest of the zero bit's ---> </cfloop> <!--- set the output string to the zero's concantenated with the binary output of the test case ---> <cfset testCaseOutput = testCaseOutput & FormatBaseN(testCase,2)> <!--- initalize a role list to capture the role names ---> <cfset roleList = ""> <table cellpadding="2" cellspacing="0"> <tr> <th style="text-align:center;">Test Case Binary</th> <th style="text-align:center;">Test Case Decimal</th> <th style="text-align:center;">Current Bit Binary</th> <th style="text-align:center;">Current Bit Decimal</th> <th style="text-align:center;">Result</th> </tr> <tr style="background-color:#FFDDDD;"> <td style="text-align:center;">[<cfoutput>#testCaseOutput#</cfoutput>]</td> <td style="text-align:center;"><cfoutput>#testCase#</cfoutput></td> <td style="text-align:center;">[<cfoutput>#structFind(thisStruct, 'this0')#</cfoutput>]</td> <td style="text-align:center;"><cfoutput>#InputBaseN(structFind(thisStruct, 'this0'),2)#</cfoutput></td> <td style="text-align:center;">False</td> </tr> <!--- loop through the 31 bytes in the structure ---> <cfloop from="1" to="31" index="iX"> <!--- output the current test byte itteration (iX) information ---> <cfoutput> <!--- perform BitAnd against the current byte (iX) and the testCase byte ---> <cfif BitAnd(InputBaseN(structFind(thisStruct, 'this#iX#'),2),testCase)> <!--- if BitAnd returns true, then the bit represented by the current byte (iX) is present in the testCase byte ---> <tr style="background-color:##BBFFBB;"> <td style="text-align:center; border-left:1px solid ##000000; border-top:1px solid ##000000; border-bottom:1px solid ##000000;">[#testCaseOutput#]</td> <td style="text-align:center; border-top:1px solid ##000000; border-bottom:1px solid ##000000;">#testCase#</td> <td style="text-align:center; border-top:1px solid ##000000; border-bottom:1px solid ##000000;">[#structFind(thisStruct, 'this#iX#')#]</td> <td style="text-align:center; border-top:1px solid ##000000; border-bottom:1px solid ##000000;">#InputBaseN(structFind(thisStruct, 'this#iX#'),2)#</td> <td style="text-align:center; border-top:1px solid ##000000; border-bottom:1px solid ##000000; border-right:1px solid ##000000;">True</td> </tr> <!--- set the value of this byte in the role list ---> <cfset roleList = ListAppend(roleList,structFind(thisStruct, 'this#iX#'))> <!--- otherwise, BitAnd returned false, so the bit represented by the current byte (iX) is *not* present in the testCase byte ---> <cfelse> <tr style="background-color:##FFDDDD;"> <td style="text-align:center;">[#testCaseOutput#]</td> <td style="text-align:center;">#testCase#</td> <td style="text-align:center;">[#structFind(thisStruct, 'this#iX#')#]</td> <td style="text-align:center;">#InputBaseN(structFind(thisStruct, 'this#iX#'),2)#</td> <td style="text-align:center;">False</td> </tr> </cfif> <!--- end checking current test byte ---> </cfoutput> <!--- loop through to the next byte ---> </cfloop> </table> <br> <br> <h1>The result</h1> <p>Listed below are the binary roles detected by the BitAnd checking routine.</p> <!--- output the roles randomly picked ---> <cfoutput>#roleList#</cfoutput> In Part II of this series I will break this down further into a component that can be used for level validation within your applications. |