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 /** Allocated heap memory in bytes. */ 74 size_t memUsage() const pure nothrow @property @trusted 75 { 76 return limb.sizeof*_capacity; 77 } 78 79 /** make sure this structure can contain given number of elements without further allocs */ 80 void reserve(size_t newCap, bool overEstimate = false) @trusted 81 { 82 // bit-count -> limb-count 83 newCap = (newCap + limbBits - 1) / limbBits; 84 85 if(newCap <= _capacity) 86 return; 87 88 if(overEstimate) 89 newCap = max(newCap, 2*_capacity); 90 91 auto newPtr = jiveMalloc!limb(newCap); 92 newPtr[0..limbCount] = _ptr[0..limbCount]; 93 newPtr[limbCount..newCap] = 0; 94 95 jiveFree(_ptr); 96 _ptr = newPtr; 97 _capacity = newCap; 98 } 99 100 inout(limb)* ptr() inout 101 { 102 return _ptr; 103 } 104 105 /** number of limbs in use */ 106 size_t limbCount() const @property 107 { 108 return (_length + limbBits - 1) / limbBits; 109 } 110 111 /** returns limbs in use */ 112 inout(limb)[] limbs() inout @property @trusted 113 { 114 return _ptr[0 .. limbCount]; 115 } 116 117 /** either cuts of or fills new elements with false */ 118 void resize(size_t size) 119 { 120 reserve(size); 121 122 // reset cut of elements. TODO: optimize 123 for(size_t i = size; i < _length; ++i) 124 this[i] = false; 125 126 _length = size; 127 } 128 129 /** count number of elements equal to v */ 130 size_t count(bool v) const @trusted 131 { 132 // NOTE: relies on unused bits being zero 133 size_t c; 134 foreach(x; (cast(uint*)_ptr)[0..(_length+31)/32]) 135 c += popcnt(x); // why is there only a 32 bit version of popcnt in core.bitop? 136 if(v) 137 return c; 138 else 139 return _length-c; 140 } 141 142 /** set all elements to false */ 143 void reset() 144 { 145 limbs[] = 0; 146 } 147 148 /** indexing */ 149 bool opIndex(string file = __FILE__, int line = __LINE__)(size_t i) const @trusted 150 { 151 if(boundsChecks && (i >= length)) 152 assert(false, boundsCheckMsg!(file, line)); 153 return bt(ptr, i) != 0; // why does bt return an int (and not a bool)? 154 } 155 156 /** ditto */ 157 void opIndexAssign(string file = __FILE__, int line = __LINE__)(bool v, size_t i) @trusted 158 { 159 if(boundsChecks && (i >= length)) 160 assert(false, boundsCheckMsg!(file, line)); 161 if(v) 162 bts(ptr, i); 163 else 164 btr(ptr, i); 165 } 166 167 /** toggle element i, returns old value. */ 168 bool toggle(string file = __FILE__, int line = __LINE__)(size_t i) @trusted 169 { 170 if(boundsChecks && (i >= length)) 171 assert(false, boundsCheckMsg!(file, line)); 172 return btc(ptr, i) != 0; 173 } 174 175 // TODO: efficient bitwise operations and some kind of iteration 176 } 177 178 @nogc nothrow pure @safe unittest 179 { 180 auto a = BitArray(5); 181 a[1] = true; 182 a.toggle(2); 183 a.resize(6); 184 assert(a[5] == false); 185 assert(a.count(true) == 2); 186 assert(a.count(false) == 4); 187 }