Thursday, May 14, 2015

Using bitmask to represent enum options

A bitmask can be used to represent enum options. This condenses information to an int, which take less space while serializing or transporting.

Idea is to use bitmask without making code dirty with int floating all around where enum is expected. This simple class wrap underlying mask which can be manipulated in terms of enum fields only. Without talking much lets see some code in action.

Example

enum UserPermission{READ,WRITE,ALL}

Usage

BitMaskEnum<UserPermission> mask1= new BitMaskEnum<UserPermission>();
mask1.set(UserPermission.READ); // [READ=true,WRITE=false,ALL=false]

BitMaskEnum<UserPermission> mask2= new BitMaskEnum<UserPermission>();
mask2.set(UserPermission.READ); // [READ=false,WRITE=true,,ALL=false]

mask1.setAllFrom(mask2.getMask()); // [READ=true,WRITE=true,ALL=false]


The utility

public class BitMaskEnum<E extends Enum<E>>
{
private long mask = 0;

public BitMaskEnum()
{
mask = 0;
}
public BitMaskEnum(long initValue)
{
mask = initValue;
}
public long setAllFrom(long value)
{
mask = mask | value;
return mask;
}
public long set(E e)
{
mask = mask | getIndexMask(e);
return mask;
}
public boolean isSet(E e)
{
long temp = mask;
return (temp & getIndexMask(e)) == getIndexMask(e);
}

public long flip(E e)
{
mask = mask & ~getIndexMask(e);
return mask;
}
public long flipAll()
{
mask = ~mask;
return mask;
}
public long unset(E e)
{
if(isSet(e))
{
flip(e);
}
return mask;
}
public long getMask()
{
return mask;
}
private long getIndexMask(E e)
{
return 1 << getBitPositionForEnum(e);
}

protected int getBitPositionForEnum(E e)
{
return e.ordinal();
}
}


Issues

If order of enum change stored bitmask will be corrupt. You can protect your enum sequence with unit testcase like following

@Test
public void testEnumIndexOrder()
{
for(UserPermission e : UserPermission.values())
{
switch(e.ordinal())
{
case 0: assertEquals("Incorrect ordering of enum:"+ UserPermission.class.getName(), UserPermission.READ, e); break;
case 1: assertEquals("Incorrect ordering of enum:"+ UserPermission.class.getName(), UserPermission.WRITE, e); break;
case 2: assertEquals("Incorrect ordering of enum:"+ UserPermission.class.getName(), UserPermission.ALL, e); break;
}
}

assertEquals("Missing validation for enum:"+ UserPermission.class.getName(), UserPermission.values().length, 3);
}

No comments:

Post a Comment