marc walter

Using Bitmasks in JavaScript

2016-03-05

Bitmasks are useful (e.g. for State Machines) and also usable in JavaScript.

Attention: The maximum value for the mask in JS is 2^31-1 (maximum value of int in JS).

Initial usage

No enums available, so use an object instead to create a Bitmask:

var States = {
    HINT: 1,
    NOTICE: 2,
    WARNING: 4,
    ERROR: 8,
    FATAL: 16
};

And use it like this:

var a = States.NOTICE;

a & States.HINT // returns 0

a & States.NOTICE // returns 2 (States.NOTICE)

Checking values against the mask

Two ways of checking the mask:

if ((a & States.NOTICE) === States.NOTICE) console.log(true);

if (a & States.NOTICE) console.log(true);

As long as the value 0 is not part of the Bitmask, the second way works as expected.

Working with the bitmask

Set combined values:

var a = States.NOTICE | States.FATAL;

a & States.FATAL // returns 16 (States.FATAL)
a & States.NOTICE // returns 2 (States.Notice)

Subtract a value:

var a = States.NOTICE | States.FATAL;
var b = a ^ States.FATAL;

a & States.FATAL # returns 0

The operators |= and ^= work as expected.

Pretty printing bitmask values

function StateToString(state) {
    if (isNaN(state)) {
        return '<isNaN("' + state + '")>';
    }

    if (state < 1) return '<NONE>';
    else {
        let iter, result = "";
        for (iter in States) {
            if (state & States[iter]) result += iter + '&';
        }

        if (result.length > 0) result = result.substr(0, result.length-1);
        else result = '<NONE>';
        return result;
    }
}

Full example

var States = {
    HINT: 1,
    NOTICE: 2,
    WARNING: 4,
    ERROR: 8,
    FATAL: 16
};

function StateToString(state) {
    if (isNaN(state)) {
        return '<isNaN("' + state + '")>';
    }

    if (state < 1) return '<NONE>';
    else {
        let iter, result = "";
        for (iter in States) {
            if (state & States[iter]) result += iter + '&';
        }

        if (result.length > 0) result = result.substr(0, result.length-1);
        else result = '<NONE>';
        return result;
    }
}

function printState() {
    console.log(state + ' => ' + StateToString(state));
}

var state = 'NOTICE'; state= null;
printState(); // <isNaN("NOTICE")> 

state = States.NOTICE;
printState(); // 2 => NOTICE 

state |= States.WARNING;
printState(); // 6 => NOTICE&WARNING 

state ^= States.NOTICE; 
printState(); // 4 => WARNING