[update 2014.07.08]
I’ve written a javascript NPM module for performing these operations – bitflag-utils
[note to self]
given the following:
const FLAG_A:uint = 1 << 0; // dec = 1, bit = 0001 const FLAG_B:uint = 1 << 1; // dec = 2, bit = 0010 const FLAG_C:uint = 1 << 2; // dec = 4, bit = 0100 const FLAG_D:uint = 1 << 3; // dec = 8, bit = 1000 var flags:uint;
so if flags = 0, then we have no flags set, however if flags >= 1 then we have some flags assigned. This makes it quite easy to check by simply saying:
flags = 0; trace( Boolean( flags )); // false flags = FLAG_A | FLAG_C; //dec = 5, bit = 0101 trace( Boolean( flags )); // true
adding flags w/ bitwise OR
let’s do some adding of some values to our flags:
flags = 0; flags = FLAG_A | FLAG_C | FLAG_D; //dec = 13, bit = 1101
this basically says in decimal type notation:
flags = FLAG_A + FLAG_C + FLAG_D;
And indeed you could use regular decimal operators and come to the same value, but where decimal operations fail is in being able to continually assign a flag of the same value multiple times as in this case:
//initialize the flags var bitflag:uint = FLAG_A | FLAG_C | FLAG_D; //dec = 13, bit = 1101 var decflag:uint = FLAG_A + FLAG_C + FLAG_D; //dec = 13, bit = 1101
Let’s assume that logic elsewhere isn’t aware of the initial value and decides to add an additional flag, in this case, one that was previously assigned. Think of the invalidation mechanisms, setting one prop might call invalidateProps, and then subsequent props settings also calls invalidateProps… sorta analogous right?
bitflag = bitflag | FLAG_A; //dec = 13, bit = 1101
Note that the flag remains the same value. This is due to the fact that the bitwise OR does not carry over like traditional addition. In fact this could be viewed more along the lines of a logic operation for each radix. Now let’s see if the same holds true for traditional decimal operations:
decflag = decflag + FLAG_A; //dec = 14, bit = 1110
Not only did the decimal operation fail us, it also set another flag for us unintentionally. Note that 1110 is the same as:
FLAG_B | FLAG_C | FLAG_D; //dec = 14, bit = 1110
Ok so we’ve seen how the bitmath operations allow us to add flags without having to worry about the whole carry over issue.
Now let’s look at the Bitwise OR operation in more depth. In logic, if A OR B then TRUE (forgive me, I have since forgotten the proper syntax for logic statements). In the case of bitmath, for a given radix, if either one of them is a value of 1, then the radix for the output is 1. The only time the radix to the output is 0 is if both values for that radix are zero. A picture is worth a 1000 words so:
0001 - FLAG_A (or) 0010 - FLAG_B ------------ 0011 - flags
let’s now show that adding FLAG_B again does nothing to the output:
0011 - flags (or) 0010 - FLAG_B ------------ 0011 - flags
let’s try a more complex example:
0001 - FLAG_A (or) 0010 - FLAG_B (or) 1000 - FLAG_D ------------ 1011 - flags
next up is what I’d call a HAS FLAG operation
check for a flag w/ bitwise AND
Bitwise OR operations are great for setting flags, but it doesn’t get you very far when you need to check your flags for a particular value. Just like the OR operation, the AND operation is a logic operation. If A AND B then TRUE, or in other words, for a given radix, if both values in a given radix are 1 then the output for that radix is 1, else it’s 0. Another look at this is:
0001 (and) 0001 ------------ 0001 - both were 1 so output to 1 0001 (and) 0000 ------------ 0000 - the logical AND statement fails, thus 0 is returned
One thing to note is that the AND statement only evaluates for the 1s in the binary values (or at least how I understand it to operate).
So let’s apply this to our flags to see how we might be able to determine if a value is present in our flags var. Let’s assume our flags have already been set to:
flags = FLAG_A | FLAG_D; //dec = 9, bit = 1001
Now we’re going to check to see if FLAG_C has been set.
var hasFlagC:Boolean = FLAG_C == ( flags & FLAG_C );
Let’s first examine the statement in the parenthesis:
flags & FLAG_C; 1001 - flags (and) 0100 - FLAG_C ------------ 0000 - output
And then we evaluate the output against the flag we’re checking for:
0000 == 0100 = FALSE or output != FLAG_C
Ergo FLAG_C was not set. So let’s try another test that we know should return true:
flags = FLAG_A | FLAG_D; //dec = 9, bit = 1001 (from before) var hasFlagD:Boolean = FLAG_D == ( flags & FLAG_D );
Evaluate the parenthesis first:
flags & FLAG_D; 1001 - flags (and) 1000 - FLAG_D ------------ 1000 - output output == FLAG_D
Lastly there is the need to remove a flag
removing a flag w/ bitwise AND plus bitwise NOT
Obviously this post wouldn’t be complete without showing you how to remove a flag from a given bitflag. This entails two types of bitwise operations. One you are already familiar with: the bitwise AND. The other is the bitwise NOT statement. Like the above 2 logical operations the concept of the bitwise NOT is to return an inverse of the input:
1001 - input NOT of input 0110
The common syntax is:
~input
So now we need to combine the AND and NOT operations to remove a flag. Let’s dive in shall we?
0000 - flags (starting fresh) (and) 0001 - FLAG_A ------------ 0001 - flags (and) 0010 - FLAG_B ------------ 0011 - flags
We’ve just added a few flags to our value. Now let’s remove them and try to arrive at our initial state of 0.
0011 - flags (and) 1110 - ~FLAG_A ------------ 0010 - flags (see how the AND statement preserve FLAG_B?, now let's remove B) (and) 1101 - ~FLAG_B ------------ 0000 - flags (back to default state of no flags set)
Now I will show you that whole process in actionscript:
const FLAG_A:uint = 1 << 0; // dec = 1, bit = 0001 const FLAG_B:uint = 1 << 1; // dec = 2, bit = 0010 const FLAG_C:uint = 1 << 2; // dec = 4, bit = 0100 const FLAG_D:uint = 1 << 3; // dec = 8, bit = 1000 var flags:uint; //adding A, you can also say flags |= FLAG_A; flags = flags | FLAG_A; //add flag b, this time using the shorthand flags |= FLAG_B; //now to remove A flags = flags & ~FLAG_A; //remove B using shorthand flags &= ~FLAG_B; trace(flags.toString()); // 0
and the surprise is…
Adobe made a nifty little utility class just for these types of operations. Check it out:
mx.utils.BitFlagUtil;
It’s an excluded class so you will need to import & instantiate manually, THEN code hints will appear. Here is the documentation for it:
Pingback: JS: bitflag-utils | explorers' club