As a follow up to my post from yesterday, here is the C# extension version of the integer bit manipulation. In order to facilitate the code I post, I have created a Codeplex project. The project name is Pickle, from my last name. It's going to be a kitchen sink project with all sorts of utilities in it.
As it stands now, I have one solution with five projects in it. I have a Core project where I'm keeping my extension methods and various utilities, a database project where I'm keeping things like PrintMax and the SQL bit manipulation, a SQLCLR project where I've got my double byte encoding function, since I'm trying to get into TDD I also have a Unit Test project and finally I have a web project where I'm splashing around with various things. That was a run-on sentence to end all run-on sentences. Perhaps codeplex is overkill, but it just seemed to make sense. Instead of posting code, from now on I'll post screenshots and links to the source code on codeplex.
Extension Methods
As promised, I have created C# extension methods for the bit manipulation. The can be used to test integer values throughout your project. I have created the same set of functions (IsBit, ReadBit, SetBit, IsBitUnion).
Here is a look at the SetBit extension method:
And here is a look at the IsBitUnion usage:

For these two calls IsBitUnion returned true then false as expected.
Here is a link to the source code file in my codeplex project that contains the bit and other extension methods.
By the way. I'm not sure what version of TFS Codeplex is running, but I thought I saw that they were using 2005. I'm running the VS.Net 2008 Team Suite with the TFS 2008 plug-in and connecting with no issues. This is exciting for me, I wasn't sure if the VS.Net 2k8 worked with TFS 2k5. Really, I'm still not sure, but I'm feeling better :D
All feedback is encouraged.
Thanks,
Ben
Introduction
Hopefully my topic isn't too far off. In this post I'm going to cover what I like to think of as easy bit manipulation. Well, it's easier anyway. The basic concept is that an integer is comprised of bits. Integers are easier to look at and play with than bits, especially if we're storing bit values in SQL and we want to update them... Using logical bitwise operators, we can query one integer with another to see which bits they have in common. When performing the query, if one value is strictly a bit, we can see if that bit is 'on' in the second value, which is a sum of one or more bits. Alternatively, we can test two summed bits to see if one is a union of the other. To me this means all of one exists completely in the other.
I plan to post a C# extension method version of the SQL functions below in a day or two. Since I'm not using VS.Net 2008 full time yet, I need to convert what I have to extension methods. Well, I guess I don't need to, but since I think extension methods are cool, I'm going to:)
Example
I'll start with a security example, but the context can be anything at all. We need two values, one is the test bit and one is a sum of bits.
Since we're doing permissions, our test bits might look like the following.
- CanView = 1
- CanEdit = 2
- CanSubmit = 4
- CanApprove = 8
- CanReject = 16
What about that sum of bits? Sure, lets say we have an administrator with all bits. Their sum of bits would be 1 + 2 + 4 + 8 + 16 = 31. Perhaps we have a contributor, they can only view edit and submit, they would have 1 + 2 + 4 = 7.
| | Admin | Contributor |
| Integer | 31 | 7 |
| Bits | 0001 1111 | 0000 0111 |
Ok, that was easy, let's say that users with Admin permissions have a 31 and contributors have a 7. Suddenly we have the need to perform a check, can the user Approve? Can the user Edit?
Let's see, we need another table to consider this, let's examine the bits of CanApprove and CanEdit.
| | CanApprove | CanEdit |
| Integer | 8 | 2 |
| Bits | 0000 1000 | 0000 0010 |
So, can the Admin approve or edit? How about the Contributor?
| | Admin | | Contributor |
| Summed Bits | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
| CanApprove | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
| CanEdit | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
I hope this is legible and understandable so far. This last table is trying to line up the summed bits of the admin and contributors juxtaposed with the bits of CanApprove and CanEdit to simplify comparison. The green shows a match and the red shows a miss. Above we can see that the integer value for admin has both the CanApprove and CanEdit bits. At the same time, we see that the Contributor bits do not contain the CanApprove bit, but they do contain the CanEdit bits.
Pretty exciting stuff isn't it :)
Uses
Hopefully at this point, I have successfully demonstrated how integers can be used to define flags and then store them in aggregate. Where I work we primarily use them in two instances.
First in granular page permissions such as the example above, allowing functionality based on permissions. After authorizing the user on a page, we then enable UI components based on the bits of the component compared to the bits of the user.
Next, we use this as a way to store multiple bit fields. Some might warn about performance, no we haven't tested performance here, because we haven't seen an issue. For example, let's say we send out a form with multiple checkboxes on it. If we have eight bit fields on a form, instead of persisting to eight bit columns we persist to one integer called FormBits. When we need to know if something was checked or not, we simply examine the integer for the bits we need.
Code
Enough talk, what about this SQL stuff? Below are methods to test and read bit values SQL. There are some comments in the code, but I'm not going to elaborate on the bitwise operators.
SQL
- CREATE FUNCTION [Utilities].[SetBit]
- (
- -- the bits to check as an integer
- @iBits INT
- -- this bit to test
- -- this must be a bit value
- -- e.g. 1, 2, 4, 8, 16, 32, 64, etc
- -- non bit values will produce unknown results
- -- e.g. 3, 5, 6, 7, 9, 10, 11, etc
- ,@iTestBit INT
- -- should the bit be on or off
- ,@iBitState BIT
- )
- RETURNS INT
- AS
- BEGIN
- DECLARE @BitState BIT;
- -- we are validating that the testbit paramater is a bit
- -- if it isn't, we return null
- IF (Utilities.IsBit(@iTestBit) = 0)
- RETURN NULL;
- -- determine the current state of the test bit
- SET @BitState = Utilities.ReadBit(@iBits, @iTestBit);
- -- leave if the value is correct
- IF (@BitState = @iBitState)
- RETURN @iBits;
- IF (@BitState = 0 AND @iBitState = 1)
- RETURN @iBits + @iTestBit;
- RETURN @iBits - @iTestBit;
- END;
-
- CREATE FUNCTION [Utilities].[ReadBit]
- (
- -- the bits to check as an integer
- @iBits INT
- -- this bit to test
- -- this must be a bit value
- -- e.g. 1, 2, 4, 8, 16, 32, 64, etc
- -- non bit values will produce unknown results
- -- e.g. 3, 5, 6, 7, 9, 10, 11, etc
- ,@iTestBit INT
- )
- RETURNS BIT
- AS
- BEGIN
- DECLARE @ComparisonResults INT;
- -- we are validating that the testbit paramater is a bit
- -- if it isn't, we return null
- IF (Utilities.IsBit(@iTestBit) = 0) BEGIN
- RETURN NULL;
- END;
- -- bitwise and the bits and the test bit
- SET @ComparisonResults = @iBits & @iTestBit;
- -- if the result equals the test bit, the bit is present
- IF (@ComparisonResults = @iTestBit) BEGIN
- RETURN 1;
- END;
- ELSE BEGIN
- RETURN 0;
- END;
- -- this return is required by the function
- -- it should never be hit
- RETURN 0;
- END;
- CREATE FUNCTION [Utilities].[IsBit]
- (
- @iTestBit INT
- )
- RETURNS BIT
- AS
- BEGIN
- -- This function will take an integer and return true if it is a bit value, i.e. 1, 2, 4, 8
- DECLARE @Result BIT;
- IF @iTestBit = 1
- -- Special case of TestBit = 1
- SET @Result = 1;
- ELSE IF ROUND(LOG(@iTestBit)/LOG(2),1) = LOG(@iTestBit)/LOG(2)
- -- if the LOG Base 2 of the TestBit value is an INTEGER, you have found 2, 4, 8, 16
- SET @Result = 1;
- ELSE
- SET @Result = 0;
- RETURN @Result;
- END;
- CREATE FUNCTION Utilities.IsBitUnion
- (
- -- the bits to check as an integer
- @iBits INT
- -- this bits to test
- ,@iTestBits INT
- )
- RETURNS BIT
- AS
- BEGIN
- DECLARE @ComparisonResults INT;
- -- bitwise and the bits and the test bits
- SET @ComparisonResults = @iBits & @iTestBits;
-
- -- if the input bits equals the test bits, we have a union
- IF (@ComparisonResults = @iTestBits) BEGIN
- RETURN 1;
- END;
-
- RETURN 0;
- END;
- -- CanView = 1
- -- CanEdit = 2
- -- CanSubmit = 4
- -- CanApprove = 8
- -- CanReject = 16
- -- Admin = 31
- -- Contributor = 7
- SELECT Utilities.ReadBit(31, 8) -- 1 : Admin has CanApprove
- SELECT Utilities.ReadBit(31, 2) -- 1 : Admin has CanEdit
- SELECT Utilities.IsBitUnion(31, 10) -- 1 : Admin has CanApprove & CanEdit
- SELECT Utilities.ReadBit(7, 8) -- 0 : Contributor does not have CanApprove
- SELECT Utilities.ReadBit(7, 2) -- 1 : Contributor has CanEdit
- SELECT Utilities.IsBitUnion(7, 10) -- 0 : Contributor does not have CanApprove & CanEdit
- SELECT Utilities.SetBit(7, 8, 1) -- 15 : Add CanApprove to Contributor
- SELECT Utilities.ReadBit(15, 8) -- 1 : Contributor has CanApprove
- SELECT Utilities.IsBitUnion(15, 10) -- 1 : Contributor has CanApprove & CanEdit
The IsBit function can be used as a constraint on columns that hold the individual bits that make up permissions and to validate that the test bit input to the ReadBit function is a bit. If a non-bit were passed to readbit, bad things happen.
Thank you for reading my post, any and all feedback is encouraged!
Ben
P.S. As a quick SQL rant/question, what is up with synonyms not being allowed for schema bound objects? Where I work, we have a shared database that multiple project databases reference in one way or another. The Utilities.IsBit function above is employed in databases to ensure that any configured bit values are bits. Makes sense right? Well, since we cannot use a schema bound object in a check constraint, we have to have multiple copies of the function. This is also the case with other entities that we would otherwise use a synonym to reference. Is it me, or is this an unnecessary inconvenience?