1 module format.rulevalues; 2 3 struct RuleValues { 4 private: 5 import core.bitop; 6 enum Bits = 8 * size_t.sizeof; 7 enum Mask = Bits - 1; 8 enum DirectBits = 2 * Bits; 9 enum DirectCapacity = DirectBits - bsf(DirectBits); 10 enum DirectShift = DirectCapacity - Bits; 11 12 union { 13 struct { 14 size_t* uptr; 15 size_t ulength; 16 } 17 18 size_t[2] direct; 19 } 20 21 bool isDirect() const { 22 return direct[0] & 0x01; 23 } 24 25 public: 26 this(size_t frozen, size_t capacity) in { 27 assert(frozen > 0 && capacity >= frozen); 28 } do { 29 if (capacity > DirectCapacity) { 30 size_t length = 1 + (capacity + Bits - 1) / Bits; 31 32 indirect = new size_t[length]; 33 indirect[0] = frozen; 34 indirect[1] = 0x01; 35 } else { 36 direct[0] = 0x01; 37 direct[1] = frozen << DirectShift; 38 } 39 } 40 41 RuleValues clone() const { 42 RuleValues ret = void; 43 if (isDirect()) { 44 ret.direct = direct; 45 } else { 46 ret.indirect = indirect.dup; 47 } 48 49 return ret; 50 } 51 52 RuleValues withFrozenSplit(size_t i) const in { 53 assert(i >= frozen && i < capacity); 54 } do { 55 RuleValues ret = clone(); 56 57 ret[i] = true; 58 ret.frozen = i + 1; 59 return ret; 60 } 61 62 @property 63 size_t capacity() const { 64 return isDirect() ? DirectCapacity : (indirect.length - 1) * Bits; 65 } 66 67 @property 68 size_t frozen() const { 69 return isDirect() ? (direct[1] >> DirectShift) : indirect[0]; 70 } 71 72 @property 73 size_t frozen(size_t f) in { 74 assert(f > 0 && f <= capacity); 75 assert(this[f - 1]); 76 } do { 77 if (isDirect()) { 78 // Replace the previous frozen value. 79 direct[1] &= (size_t(1) << DirectShift) - 1; 80 direct[1] |= f << DirectShift; 81 } else { 82 *uptr = f; 83 } 84 85 return frozen; 86 } 87 88 bool opIndex(size_t i) const { 89 return (values[word(i)] >> shift(i)) & 0x01; 90 } 91 92 void opIndexAssign(bool v, size_t i) in { 93 assert(i >= frozen && i < capacity); 94 } do { 95 auto w = word(i); 96 auto m = size_t(1) << shift(i); 97 98 if (v) { 99 values[w] |= m; 100 } else { 101 values[w] &= ~m; 102 } 103 } 104 105 bool opEqual(const ref RuleValues rhs) const { 106 const d = isDirect(); 107 if (rhs.isDirect() != d) { 108 return false; 109 } 110 111 return d ? direct == rhs.direct : indirect == rhs.indirect; 112 } 113 114 int opCmp(const ref RuleValues rhs) const { 115 // We don't really use this, but let's be throurough. 116 if (capacity != rhs.capacity) { 117 return cast(int) (capacity - rhs.capacity); 118 } 119 120 // Explore candidate with a few follow up first. 121 if (frozen != rhs.frozen) { 122 return cast(int) (rhs.frozen - frozen); 123 } 124 125 foreach_reverse (i; 0 .. capacity) { 126 if (this[i] != rhs[i]) { 127 return rhs[i] - this[i]; 128 } 129 } 130 131 return 0; 132 } 133 134 private: 135 @property 136 inout(size_t)[] values() inout { 137 return isDirect() ? direct[] : indirect[1 .. $]; 138 } 139 140 @property 141 inout(size_t)[] indirect() inout { 142 return uptr[0 .. ulength]; 143 } 144 145 @property 146 size_t[] indirect(size_t[] v) { 147 uptr = v.ptr; 148 ulength = v.length; 149 return indirect; 150 } 151 152 static word(size_t i) { 153 return i / Bits; 154 } 155 156 static shift(size_t i) { 157 return i & Mask; 158 } 159 } 160 161 unittest { 162 foreach (i; 0 .. RuleValues.DirectCapacity) { 163 const c = i + 1; 164 const rv = RuleValues(1, c); 165 assert(rv.isDirect()); 166 assert(rv.capacity == RuleValues.DirectCapacity); 167 } 168 169 foreach (i; RuleValues.DirectCapacity .. 16 * size_t.sizeof) { 170 const c = i + 1; 171 const rv = RuleValues(1, c); 172 assert(!rv.isDirect()); 173 assert(rv.capacity == 16 * size_t.sizeof); 174 } 175 176 foreach (i; 16 * size_t.sizeof .. 24 * size_t.sizeof) { 177 const c = i + 1; 178 const rv = RuleValues(1, c); 179 assert(!rv.isDirect()); 180 assert(rv.capacity == 24 * size_t.sizeof); 181 } 182 } 183 184 unittest { 185 auto r0 = RuleValues(1, 10); 186 auto r1 = r0.clone(); 187 assert(r0 == r1); 188 assert(!(r0 < r1)); 189 assert(!(r1 > r0)); 190 191 r0[5] = true; 192 assert(r0 != r1); 193 assert(r0 < r1); 194 assert(r1 > r0); 195 196 r1[5] = true; 197 assert(r0 == r1); 198 assert(!(r0 < r1)); 199 assert(!(r1 > r0)); 200 201 r1.frozen = 6; 202 assert(r0 != r1); 203 assert(r0 > r1); 204 assert(r1 < r0); 205 206 r0.frozen = 6; 207 assert(r0 == r1); 208 assert(!(r0 < r1)); 209 assert(!(r1 > r0)); 210 211 auto r2 = RuleValues(1, 1000); 212 assert(r0 != r2); 213 assert(r0 < r2); 214 assert(r1 != r2); 215 assert(r1 < r2); 216 } 217 218 unittest { 219 auto rv = RuleValues(1, 10); 220 221 assert(rv[0]); 222 foreach (i; 1 .. 10) { 223 assert(!rv[i]); 224 } 225 226 rv[9] = true; 227 228 assert(rv[0]); 229 assert(rv[9]); 230 foreach (i; 1 .. 9) { 231 assert(!rv[i]); 232 } 233 234 rv[7] = true; 235 236 assert(rv[0]); 237 assert(rv[7]); 238 assert(!rv[8]); 239 assert(rv[9]); 240 foreach (i; 1 .. 7) { 241 assert(!rv[i]); 242 } 243 244 rv[9] = false; 245 246 assert(rv[0]); 247 assert(rv[7]); 248 assert(!rv[8]); 249 assert(!rv[9]); 250 foreach (i; 1 .. 7) { 251 assert(!rv[i]); 252 } 253 254 rv[7] = false; 255 256 assert(rv[0]); 257 foreach (i; 1 .. 10) { 258 assert(!rv[i]); 259 } 260 }