Matt Snider JavaScript Resource

Understanding JavaScript and Frameworks

Saturday, June 14, 2008

Using Bitmasks to Efficiently Store Data

There are many times when programing that you need to store state, and when this state is simply on or off, one usually uses Booleans. As your projects group, there tends to be collections of similar boolean states, such as turned on features, or user hidden/displayed content. On Mint.com we are (re)adding the ability to hide/show certain account types on the Overview page. I will be using this as a test case for todays article. There are 4 account types in Mint: bank, credit card, investment, and loans; each of these accounts types has a section in the sidebar that can be hidden or displayed. When the user hides/shows these sections we want to remember it between sessions, so I am using a binary number to store the state and a bitmask to modify it.

First, lets discuss several technologies for storing the state, each of which have associated costs and benefits: cookies, session, database, or some combo thereof. In the Mint case, we are storing a visual preference for the user, so I initially want to use cookies. By using cookies, we have a light-weight solution remembers the users preferences on their main, or do we? If they use two computers, have multiple people using the same computer, or occasionally clear out there cookies, then there is no way to ensure that the state is remembered. However, for something minor, such as a visual preference, this might be acceptable, as cookies are fast and light-weight (unless you have lots of them). Alternatively, you could store the state in the database, and load it into the users session whenever they login. This is better than cookies, because it fixes all the above mentioned issues, and is still relatively light-weight, as you only need to make 1 DB call to fetch the preference. The only issue would be if the user is is using two simultaneous computers, which could have two separate sessions. That means setting and getting from the database is the only way to ensure that the users experience is the same on all computers, all the time. The drawback is making frequent database calls is rather expensive. On Mint we solve this by using a database caching layer, but if you don’t have a caching layer, then I recommend storing data in the session.

Now, I’ve written a JavaScript BitWriter Object that manages writing/reading bitmasks on a binary number, so I will be doing a simple cookie example. For more information on the methods I use to write/read cookies, see the post Cookies.

Lets define 4 constants to be our bitmasks, where the bitmask is the 2^n value, and n represents the binary position we are masking. So, 4 values would be represented by the binary number 2^4 (or ‘1111′). Having a ZERO in any position, indicates that the value is not set (false), and having a 1 indicates that the value is set (true). The bitmask is the value of 2 to the power of the position you are masking. Here is an example:

Example 1: Defining Bitmask

var BITMASK = { BANK: 1, // 2^0 CREDIT: 2, // 2^1 INVESTMENT: 4, // 2^2 LOAN: 8 // 2^3 };

So in our example, we use these constants to update the binary value stored in the cookie anytime the state changes. One simply fetches the binary value that is stores in the cookie, and updates it by removing, or adding the desired bitmask:

Example 2: Updating the Cookies

var COOKIE_ACCOUNT_STATE = 'accountPreferences'; var update = function(name, b) { var n = parseInt(Core.Client.readCookie(COOKIE_ACCOUNT_STATE), 10); var bw = Core.Widget.BitWriter(n); bw[b ? 'addBitmask' : 'removeBitmask'](BITMASK[name]); Core.Client.createCookie(COOKIE_ACCOUNT_STATE, bw.getValue(), 365); };

Here we read the cookie called ‘accountPreferences’ and create a new BitWriter from the value. The sample method requires two parameters: the constant name of the bitamsk, and the boolean value of the state (true = on, false = off). Then we use the add/remove bitmask methods of BitWriter to update the value, before updating the cookie value.

The BitWriter Object has the following methods: ‘addBitmask’, ‘getValue’, ‘hasBitmask’, ‘removeBitmask’. Each method ensures that bitmask is valid (a value of 2^n), before attempting the operation. The ‘addBitmask’ method tests to see if the state of the bitmask is ‘0′, and adds the bitmask from value (this is like the binary operator ‘|’ in other languages). The ‘removeBitmask’ method tests to see if the state of the bitmask is ‘1′, and removes the bitmask from value (this is like the binary operator ‘^’ in other languages). The ‘hasBitmask’ method tests to see if the state of the bitmask is ‘1′, and returns the mask in that case or 0 otherwise (this is like the binary operator ‘&’ in other languages). The ‘getValue’ method just returns the current value of the binary number managed by BitWriter.

The logic that runs BitWriter is not anything fancy, just a lot of basic binary math (see the source code here). I have also thrown together a simple test page, so that you can experiment with values and validate the logic of BitWriter.

Following a user comment, I took another look at binary operations in JavaScript only to learn that it does properly support it. I have since upgraded BitWriter to properly use binary operators instead of manually doing it. Needless to say it is much faster, regardless of the size of your binary value and about 1k smaller.

posted by Matt Snider at 12:32 pm  

4 Comments »

  1. What’s the reason to “simulate ‘&’ (or ‘|’ or ‘^’) binary operator in other languages” instead of actually using binary operators?

    /* will work for up to 256 stored values.
    you can use any power of 2 if you need more/less “slots” */
    isValidBitmask = function(n){
    return (n>0) && !(256%n)
    }

    /* this is just an example of how I’d write hasBitmask function*/
    hasBitmask : function(n){
    return ( isValidBitmask(n) && n

    Comment by pawel — June 16, 2008 @ 6:07 am

  2. ouch, once ahgain:
    hasBitmask : function(n){
    return ( isValidBitmask(n) && n <= bit && ((bit & n) == n)) ? true : false
    }

    Comment by pawel — June 16, 2008 @ 6:16 am

  3. Thanks pawel. I thought that JavaScript supported binary operations, but when I setup a few test scripts, it didn’t work. I tried again today, and it worked just fine. I have upgraded the BitWriter script to properly use binary operators.

    Comment by Matt Snider — June 16, 2008 @ 10:34 pm

  4. http://www.racketmag.com/images/cookiecrisp.jpg

    Comment by atish — June 18, 2008 @ 2:11 pm

RSS feed for comments on this post. TrackBack URI

Leave a comment

Powered by WordPress