1 /** 2 License: public domain 3 Authors: Simon Bürger 4 */ 5 6 module jive.bitarray; 7 8 private import jive.internal; 9 private import core.bitop; 10 private import std.algorithm : max; 11 12 /** 13 * Efficient version of Array!bool using only one bit per entry. 14 * But note that the interface is not compatible with Array!bool. In 15 * particular no ranges are provided. This is a design choice because using 16 * a bit-range in general algorithms is often very inefficient. In contrast 17 * specialized algorithms working on BitArray are typically very fast. 18 */ 19 struct BitArray 20 { 21 @nogc: nothrow: pure: @safe: 22 23 alias limb = size_t; 24 enum limbBits = limb.sizeof*8; 25 26 private limb* _ptr; // unused bits are always 0 27 private size_t _capacity; // allocated limbs (not bits) 28 private size_t _length; 29 30 /** constructor for given length */ 31 this(size_t size) 32 { 33 _length = size; 34 _capacity = limbCount; 35 _ptr = jiveMalloc!limb(limbCount); 36 reset(); 37 } 38 39 /** post-blit that does a full copy */ 40 this(this) @trusted 41 { 42 auto newPtr = jiveMalloc!limb(limbCount); 43 newPtr[0 .. limbCount] = _ptr[0 .. limbCount]; 44 _capacity = limbCount; 45 _ptr = newPtr; 46 } 47 48 /** destructor */ 49 ~this() 50 { 51 jiveFree(_ptr); 52 _ptr = null; 53 } 54 55 /** check for emptiness */ 56 bool empty() const @property 57 { 58 return _length == 0; 59 } 60 61 /** number of elements */ 62 size_t length() const @property 63 { 64 return _length; 65 } 66 67 /** number of elements this structure can hold without further allocations */ 68 size_t capacity() const @property 69 { 70 return _capacity * limbBits; 71 } 72 73 /** make sure this structure can contain given number of elements without further allocs */ 74 void reserve(size_t newCap, bool overEstimate = false) @trusted 75 { 76 // bit-count -> limb-count 77 newCap = (newCap + limbBits - 1) / limbBits; 78 79 if(newCap <= _capacity) 80 return; 81 82 if(overEstimate) 83 newCap = max(newCap, 2*_capacity); 84 85 auto newPtr = jiveMalloc!limb(newCap); 86 newPtr[0..limbCount] = _ptr[0..limbCount]; 87 88 jiveFree(_ptr); 89 _ptr = newPtr; 90 _capacity = newCap; 91 } 92 93 inout(limb)* ptr() inout 94 { 95 return _ptr; 96 } 97 98 /** number of limbs in use */ 99 size_t limbCount() const @property 100 { 101 return (_length + limbBits - 1) / limbBits; 102 } 103 104 /** returns limbs in use */ 105 inout(limb)[] limbs() inout @property @trusted 106 { 107 return _ptr[0 .. limbCount]; 108 } 109 110 /** either cuts of or fills new elements with false */ 111 void resize(size_t size) 112 { 113 reserve(size); 114 115 // reset cut of elements. TODO: optimize 116 for(size_t i = size; i < _length; ++i) 117 this[i] = false; 118 119 _length = size; 120 } 121 122 /** count number of elements equal to v */ 123 size_t count(bool v) const @trusted 124 { 125 // NOTE: relies on unused bits being zero 126 size_t c; 127 foreach(x; (cast(uint*)_ptr)[0..(_length+31)/32]) 128 c += popcnt(x); // why is there only a 32 bit version of popcnt in core.bitop? 129 if(v) 130 return c; 131 else 132 return _length-c; 133 } 134 135 /** set all elements to false */ 136 void reset() 137 { 138 limbs[] = 0; 139 } 140 141 /** indexing */ 142 bool opIndex(string file = __FILE__, int line = __LINE__)(size_t i) const @trusted 143 { 144 if(boundsChecks && (i >= length)) 145 assert(false, boundsCheckMsg!(file, line)); 146 return bt(ptr, i) != 0; // why does bt return an int (and not a bool)? 147 } 148 149 /** ditto */ 150 void opIndexAssign(string file = __FILE__, int line = __LINE__)(bool v, size_t i) @trusted 151 { 152 if(boundsChecks && (i >= length)) 153 assert(false, boundsCheckMsg!(file, line)); 154 if(v) 155 bts(ptr, i); 156 else 157 btr(ptr, i); 158 } 159 160 /** toggle element i, returns old value. */ 161 bool toggle(string file = __FILE__, int line = __LINE__)(size_t i) @trusted 162 { 163 if(boundsChecks && (i >= length)) 164 assert(false, boundsCheckMsg!(file, line)); 165 return btc(ptr, i) != 0; 166 } 167 168 // TODO: efficient bitwise operations and some kind of iteration 169 } 170 171 @nogc nothrow pure @safe unittest 172 { 173 auto a = BitArray(5); 174 a[1] = true; 175 a.toggle(2); 176 a.resize(6); 177 assert(a[5] == false); 178 assert(a.count(true) == 2); 179 assert(a.count(false) == 4); 180 }