Blog

Portfolio CV

ActionScript BitArray

Published:
2008-09-15 00:14:42
Posted from:
Home
Tags:
ActionScript,

A couple of weeks ago I decided to take the plunge and implement a BitArray in ActionScript 3 for use in GAV Flash. I had designed a event based infrastructure to communicate (boolean) item status changes between a bunch of graphical components. But I found myself again and again storing the item status in each of these components and decided that I had to do something about this and have the storage in one place.

Before I implemented it I took a look around the internets for other implementations, but didn't find any. So I thought I'd make other people in my situation happy by posting here.

So, here is the BitArray.as for download or view:

package gav.utils
{
	/**
	 * @author markus <markus.johnsson.84@gmail.com>
	 */
	public class BitArray
	{
		private static const LAST_BIT: uint = 0x80000000;
		private static const FIRST_BIT: uint = 1;
		
		/**
		 *  Constructs a BitArray of the given length
		 */
		public function BitArray(length: uint=0)
		{
			var fields: uint = uint(Math.ceil(Number(length)/32.0)) || 1;
			
			_bitFields = new Array(fields);
			
			for (var f:uint=0; f<fields; f++)
				_bitFields[f] = uint(0);
			
			_bitLength = length;
		}
		
		private var _bitFields: Array;
		
		//-----------------------------------------------------------------------------------------
		//
		//  Properties
		//
		//-----------------------------------------------------------------------------------------
		
		//-----------------------------------------------------------------------------------------
		//  length
		//-----------------------------------------------------------------------------------------
		
		private var _bitLength: uint;
		
		/**
		 *  The length of the array. 
		 *
		 *  Will add zeros to the end if the new length is longer than the previous.
		 */
		public function get length(): uint
		{
			return _bitLength;
		}
		
		public function set length(s: uint): void
		{
			if (s == _bitLength) return;
			
			if (s < _bitLength) 
			{
				while (s < _bitLength)
					remove(_bitLength-1);
			}
			
			var fields: uint = uint(Math.ceil(Number(s)/32.0)) || 1;
			
			while (fields > _bitFields.length)
			{
				_bitFields.push(uint(0));
			}
			
			_bitLength = s;
		}
		
		//-----------------------------------------------------------------------------------------
		//  all
		//-----------------------------------------------------------------------------------------
		
		/**
		 *  Returns true if all bits are 1.
		 */
		public function get all(): Boolean
		{
			for (var i:uint=0; i<_bitFields.length-1; i++)
			{
				if (uint(_bitFields[i]) != uint.MAX_VALUE) 
					return false;
			}
			
			var lastBitIndex: uint = (_bitLength & 31);
			
			var mask:uint = ~(uint.MAX_VALUE & ((1 << lastBitIndex)-1)); 
			
			// if lastBitIndex is 3, mask = (11111111 11111111 11111111 11111000) 
			// if lastBitIndex is 6, mask = (11111111 11111111 11111111 11000000)
			// etc
			
			var word: uint = _bitFields[_bitFields.length-1];
			var result: uint = word | mask;
			
			return result == uint.MAX_VALUE;
		}
		
		//-----------------------------------------------------------------------------------------
		//  any
		//-----------------------------------------------------------------------------------------
		
		/**
		 *  Returns true if any bit is 1.
		 */
		public function get any(): Boolean
		{
			for (var i:uint=0; i<_bitFields.length; i++)
			{
				if (_bitFields[i] > 0) return true;
			}
			return false;
		}
		
		//-----------------------------------------------------------------------------------------
		//
		//  Public methods
		//
		//-----------------------------------------------------------------------------------------
		
		/**
		 *  Adds a bit to the end of the array. 
		 */
		public function push(value: Boolean): void
		{
			var lastFieldIndex: uint = _bitFields.length-1;   // == (_bitLength / 32)-1;
			var lastBitIndex: uint = (_bitLength - 1) & 31;   // == (_bitLength-1) % 32;
			
			if ( _bitLength == 0 )
			{
				lastFieldIndex = 0;
				lastBitIndex = 0;
			}
			else if ( lastBitIndex == 31 )
			{
				_bitFields.push(uint(0));
				lastFieldIndex++;
				lastBitIndex = 0;
			}
			else
			{
				lastBitIndex++;
			}
			
			var mask: uint = (1 << lastBitIndex);
			var word: uint = _bitFields[lastFieldIndex];
			
			_bitFields[lastFieldIndex] ^= (-uint(value) ^ word) & mask;
			
			//
			//  above is same as below, without branching (i.e. cooler).
			//
			// if (value)
			//     _bitFields[lastFieldIndex] = word | mask;
			// else
			//     _bitFields[lastFieldIndex] = word & ~mask;
			//
			
			_bitLength++;
		}
		
		/**
		 *  Removes the bit at the given position.
		 */
		public function remove(index: uint): void
		{
			var fieldIndex: uint = uint(index / 32);
			var lastFieldIndex: uint = _bitFields.length-1;
			
			if (fieldIndex > lastFieldIndex)
				throw new RangeError("index");
			
			var bitIndex: uint = index & 31;
			
			var word: uint;
			var next: uint;
			var shift: uint;
			var mask: uint;
			
			word = uint(_bitFields[fieldIndex]);  //  1110 1011 1010 1101 1110 1011 1010 1101
			shift = (word >>> 1);                 // 0111 0101 1101 0110 1111 0101 1101 0110
			
			mask = (1 << bitIndex)-1;             // 0000 0000 0000 0000 0000 0000 0001 1111
			
			// shift the last part of the word one bit right
			_bitFields[fieldIndex] = shift ^ ((shift ^ word) & mask);
			
			//                                       0111 0101 1101 0110 1111 0101 1100 1101
			
			//
			//  above is same as below, with one op less.
			// 
			// _bitFields[fieldIndex] = (word & mask) | (shift & ~mask);
			//
			
			while (fieldIndex < lastFieldIndex)
			{
				word = uint(_bitFields[fieldIndex]);
				next = uint(_bitFields[fieldIndex+1])
				
				// copy first bit in next word to last bit in the current word
				var firstbit: uint = next & FIRST_BIT;
				//_bitFields[fieldIndex] ^= (-uint(firstbit>0) ^ word) & LAST_BIT;
				
				if (firstbit > 0)
					_bitFields[fieldIndex] = word | LAST_BIT;
				
				// shift the next word one bit
				_bitFields[fieldIndex+1] = (next >>> 1);
				
				fieldIndex++;
			}
			
			_bitLength--;
		}
		
		/**
		 *  Returns the value at the given position.
		 */

		public function getAt(index: uint): Boolean
		{
			var bitIndex: uint = index & 31; 			// index % 32
			var fieldIndex: uint = uint(index / 32);
			var mask: uint = (1 << bitIndex);
			var word: uint = _bitFields[fieldIndex];
			return Boolean(uint(word & mask));
		}
		
		/**
		 *  Sets the value at the given position.
		 */
		public function setAt(index: uint, value: Boolean): void
		{
			var bitIndex: uint = index & 31; // index % 32
			var fieldIndex: uint = uint(index / 32);
			
			var mask: uint = (1 << bitIndex);
			var word: uint = _bitFields[fieldIndex];
			
			_bitFields[fieldIndex] ^= (-uint(value) ^ word) & mask;
		}	
	}
}

Comments

PriepeLymnLer http://ports.my-addr.com/ 2010-08-29 08:44:55
thanks
evefemberwems http://member.my-addr.com/public_profile.php?id=Z0JRMg== 2011-06-17 17:06:50
Tack for intressant blogg
Name
Email
Website
Text

© 2008 Markus Johnsson