make range from l-value and r-value. element access is always const
alias K = uint; alias X = SSOHashMapOrSet!(K, void, null, FNV!(64, true)); immutable a = [11, 22].s; // mutable auto x = X.withElements(a); foreach (e; x.byElement) // from l-value { static assert(is(typeof(e) == const(K))); // always const access } // const const y = X.withElements(a); foreach (e; y.byElement) // from l-value { static assert(is(typeof(e) == const(K))); } foreach (e; X.withElements([11].s).byElement) // from r-value { static assert(is(typeof(e) == const(K))); // always const access }
test various things
1 immutable n = 600; 2 3 alias K = uint; 4 5 import std.meta : AliasSeq; 6 foreach (V; AliasSeq!(void, string)) 7 { 8 alias X = SSOHashMapOrSet!(K, V, null, FNV!(64, true)); 9 10 static if (!X.hasValue) 11 { 12 auto x = X.withElements([11, 12, 13].s); 13 14 import std.algorithm : count; 15 auto xr = x.byElement; 16 17 alias R = typeof(xr); 18 import std.range.primitives : isInputRange; 19 import std.traits : ReturnType; 20 static assert(is(typeof(R.init) == R)); 21 static assert(is(ReturnType!((R xr) => xr.empty) == bool)); 22 auto f = xr.front; 23 static assert(is(typeof((R xr) => xr.front))); 24 static assert(!is(ReturnType!((R xr) => xr.front) == void)); 25 static assert(is(typeof((R xr) => xr.popFront))); 26 27 static assert(isInputRange!(typeof(xr))); 28 29 assert(x.byElement.count == 3); 30 31 X y; 32 foreach (const ref e; x.byElement) 33 { 34 y.insert(e); 35 } 36 37 assert(y.byElement.count == 3); 38 assert(x == y); 39 40 const z = X(); 41 assert(z.byElement.count == 0); 42 43 immutable w = X(); 44 assert(w.byElement.count == 0); 45 46 { 47 auto xc = X.withElements([11, 12, 13].s); 48 assert(xc.length == 3); 49 assert(xc.contains(11)); 50 51 // TODO http://forum.dlang.org/post/kvwrktmameivubnaifdx@forum.dlang.org 52 xc.removeAllMatching!(_ => _ == 11); 53 54 assert(xc.length == 2); 55 assert(!xc.contains(11)); 56 57 xc.removeAllMatching!(_ => _ == 12); 58 assert(!xc.contains(12)); 59 assert(xc.length == 1); 60 61 xc.removeAllMatching!(_ => _ == 13); 62 assert(!xc.contains(13)); 63 assert(xc.length == 0); 64 65 // this is ok 66 foreach (e; xc.byElement) {} 67 68 } 69 70 { 71 auto k = X.withElements([11, 12].s).filtered!(_ => _ != 11).byElement; 72 static assert(isInputRange!(typeof(k))); 73 assert(k.front == 12); 74 k.popFront(); 75 assert(k.empty); 76 } 77 78 { 79 X q; 80 auto qv = [11U, 12U, 13U, 14U].s; 81 q.insertN(qv); 82 foreach (e; qv[]) 83 { 84 assert(q.contains(e)); 85 } 86 q.clear(); 87 assert(q.empty); 88 } 89 } 90 91 import nxt.container_traits : mustAddGCRange; 92 static if (X.hasValue && 93 is(V == string)) 94 { 95 static assert(mustAddGCRange!V); 96 static assert(mustAddGCRange!(V[1])); 97 static assert(mustAddGCRange!(X.T)); 98 static assert(mustAddGCRange!(X.SmallBin)); 99 static assert(!mustAddGCRange!(X.LargeBin)); 100 } 101 else 102 { 103 static assert(!mustAddGCRange!(X.T)); 104 static assert(!mustAddGCRange!(X.SmallBin), "Fails for X being " ~ X.SmallBin.stringof); 105 } 106 107 auto x1 = X(); // start empty 108 109 // all bins start small 110 assert(x1.binCounts.largeCount == 0); 111 112 // fill x1 113 114 foreach (immutable key; 0 .. n) 115 { 116 static if (X.hasValue) 117 { 118 const value = V.init; 119 const element = X.ElementType(key, value); 120 } 121 else 122 { 123 const element = key; 124 } 125 126 assert(key !in x1); 127 128 assert(x1.length == key); 129 assert(x1.insert(element) == X.InsertionStatus.added); 130 131 static if (X.hasValue) 132 { 133 const e2 = X.ElementType(key, "a"); 134 assert(x1.insert(e2) == X.InsertionStatus.modified); 135 assert(x1.contains(key)); 136 assert(x1.get(key, null) == "a"); 137 x1.remove(key); 138 x1[key] = value; 139 } 140 141 assert(x1.length == key + 1); 142 143 assert(key in x1); 144 static if (X.hasValue) 145 { 146 auto elementFound = key in x1; 147 assert(elementFound); 148 assert(*elementFound != "_"); 149 } 150 151 assert(x1.insert(element) == X.InsertionStatus.unmodified); 152 static if (X.hasValue) 153 { 154 assert(x1.insert(key, value) == X.InsertionStatus.unmodified); 155 } 156 assert(x1.length == key + 1); 157 158 assert(key in x1); 159 } 160 161 static if (X.hasValue) 162 { 163 import nxt.dynamic_array : Array = DynamicArray; 164 Array!(X.ElementType) a1; 165 166 foreach (const ref key; x1.byKey) 167 { 168 auto keyPtr = key in x1; 169 assert(keyPtr); 170 a1 ~= X.ElementType(key, (*keyPtr)); 171 } 172 173 assert(x1.length == a1.length); 174 175 foreach (aElement; a1[]) 176 { 177 auto keyPtr = aElement.key in x1; 178 assert(keyPtr); 179 assert((*keyPtr) is aElement.value); 180 } 181 } 182 183 assert(x1.length == n); 184 185 // duplicate x1 186 187 auto x2 = x1.dup; 188 189 // non-symmetric algorithm so both are needed 190 assert(x2 == x1); 191 assert(x1 == x2); 192 193 static if (X.hasValue) 194 { 195 assert(equal(x1.byKey, x2.byKey)); 196 assert(equal(x1.byValue, x2.byValue)); 197 assert(equal(x1.byKeyValue, x2.byKeyValue)); 198 assert(equal(x1[], x2[])); 199 } 200 201 assert(x1.binCounts.largeCount == 202 x2.binCounts.largeCount); 203 204 static assert(!__traits(compiles, { const _ = x1 < x2; })); // no ordering 205 206 assert(x2.length == n); 207 208 // empty x1 209 210 foreach (immutable key; 0 .. n) 211 { 212 static if (X.hasValue) 213 { 214 const element = X.ElementType(key, V.init); 215 } 216 else 217 { 218 const element = key; 219 } 220 221 assert(x1.length == n - key); 222 223 auto elementFound = key in x1; 224 assert(elementFound); 225 static if (X.hasValue) 226 { 227 assert(*elementFound is element.value); 228 } 229 230 assert(x1.remove(key)); 231 assert(x1.length == n - key - 1); 232 233 static if (!X.hasValue) 234 { 235 assert(!x1.contains(key)); 236 } 237 assert(key !in x1); 238 assert(!x1.remove(key)); 239 assert(x1.length == n - key - 1); 240 } 241 242 assert(x1.binCounts.largeCount == 0); 243 244 assert(x1.length == 0); 245 246 x1.clear(); 247 assert(x1.length == 0); 248 249 // empty x2 250 251 assert(x2.length == n); // should be not affected by emptying of x1 252 253 foreach (immutable key; 0 .. n) 254 { 255 static if (X.hasValue) 256 { 257 const element = X.ElementType(key, V.init); 258 } 259 else 260 { 261 const element = key; 262 } 263 264 assert(x2.length == n - key); 265 266 assert(key in x2); 267 268 assert(x2.remove(key)); 269 assert(x2.length == n - key - 1); 270 271 assert(key !in x2); 272 assert(!x2.remove(key)); 273 assert(x2.length == n - key - 1); 274 } 275 276 assert(x2.binCounts.largeCount == 0); 277 278 assert(x2.length == 0); 279 280 x2.clear(); 281 assert(x2.length == 0); 282 }
range checking
import std.exception : assertThrown, assertNotThrown; import core.exception : RangeError; import nxt.uncopyable_sample : V = SomeUncopyable; immutable n = 11; alias K = uint; alias X = SSOHashMapOrSet!(K, V, null, FNV!(64, true)); auto s = X.withCapacity(n); void dummy(ref V value) {} assertThrown!RangeError(dummy(s[K.init])); foreach (immutable uint i; 0 .. n) { s[i] = V(i); assertNotThrown!RangeError(dummy(s[i])); } foreach (immutable uint i; 0 .. n) { s.remove(i); assertThrown!RangeError(dummy(s[i])); } s[K.init] = V.init; auto vp = K.init in s; static assert(is(typeof(vp) == V*)); assert((*vp) == V.init); s.remove(K.init); assert(K.init !in s); X t; t.reserveExtra(4096); assert(t.binCount == 8192); t.clear();
class as value
import std.exception : assertThrown, assertNotThrown; import core.exception : RangeError; immutable n = 11; alias K = uint; class V { this(uint data) { this.data = data; } uint data; } alias X = SSOHashMapOrSet!(K, V, null, FNV!(64, true)); auto s = X.withCapacity(n); void dummy(ref V value) {} assertThrown!RangeError(dummy(s[K.init])); foreach (immutable uint i; 0 .. n) { s[i] = new V(i); assertNotThrown!RangeError(dummy(s[i])); } // test range auto sr = s.byKeyValue; assert(sr.length == n); foreach (immutable uint i; 0 .. n) { sr.popFront(); assert(sr.length == n - i - 1); } foreach (immutable uint i; 0 .. n) { s.remove(i); assertThrown!RangeError(dummy(s[i])); } s[K.init] = V.init; auto vp = K.init in s; static assert(is(typeof(vp) == V*)); s.remove(K.init); assert(K.init !in s); X t; t.reserveExtra(4096); assert(t.binCount == 8192);
constness inference of ranges
alias K = uint; class V { this(uint data) { this.data = data; } uint data; } alias X = SSOHashMapOrSet!(K, V, null, FNV!(64, true)); const x = X(); foreach (e; x.byKey) { static assert(is(typeof(e) == const(X.KeyType))); } foreach (e; x.byValue) { static assert(is(typeof(e) == X.ValueType)); // TODO should be const(X.ValueType) } foreach (e; x.byKeyValue) { static assert(is(typeof(e.key) == const(X.KeyType))); static assert(is(typeof(e.value) == const(X.ValueType))); static assert(is(typeof(e) == const(X.ElementType))); }
range key constness and value mutability with class value
struct K { @disable this(this); uint value; } class V { this(uint data) { this.data = data; } uint data; } alias X = SSOHashMapOrSet!(K, V, null, FNV!(64, true)); auto x = X(); x[K(42)] = new V(43); assert(x.length == 1); foreach (e; x.byValue) // `e` is auto ref { static assert(is(typeof(e) == X.ValueType)); // mutable access to value assert(e.data == 43); // value mutation side effects e.data += 1; assert(e.data == 44); e.data -= 1; assert(e.data == 43); } foreach (ref e; x.byKeyValue) // `e` is auto ref { static assert(is(typeof(e.key) == const(X.KeyType))); // const access to key static assert(is(typeof(e.value) == X.ValueType)); // mutable access to value assert(e.value.data == 43); // value mutation side effects e.value.data += 1; assert(e.value.data == 44); e.value.data -= 1; assert(e.value.data == 43); }