test various things
1 import std.meta : AliasSeq; 2 import std.typecons : Nullable; 3 import std.algorithm.comparison : equal; 4 import nxt.container_traits : mustAddGCRange; 5 import nxt.digestx.fnv : FNV; 6 import nxt.array_help : s; 7 8 version(showEntries) dbg(); 9 const n = 100; 10 11 void testEmptyAll(K, V, X)(ref X x, size_t n, 12 scope K[] keys) 13 { 14 assert(x.length == n); 15 foreach (key; keys) 16 { 17 static if (X.hasValue) 18 { 19 const element = X.ElementType(key, V.init); 20 } 21 else 22 { 23 alias element = key; 24 } 25 26 assert(x.length == n - key.get); 27 28 const hitPtr = key in x; 29 static if (X.hasValue) 30 { 31 assert(hitPtr && *hitPtr is element.value); 32 } 33 else 34 { 35 assert(hitPtr && *hitPtr is element); 36 } 37 38 assert(x.remove(key)); 39 assert(x.length == n - key.get - 1); 40 41 static if (!X.hasValue) 42 { 43 assert(!x.contains(key)); 44 assert(!x.containsUsingLinearSearch(key)); 45 } 46 assert(key !in x); 47 assert(!x.remove(key)); 48 assert(x.length == n - key.get - 1); 49 } 50 51 assert(x.length == 0); 52 53 x.clear(); 54 assert(x.length == 0); 55 } 56 57 X testDup(X)(scope ref X x, size_t n) 58 { 59 typeof(return) y = x.dup; 60 61 assert(x._store.ptr !is y._store.ptr); 62 assert(x.length == y.length); 63 assert(y.length == n); 64 // non-symmetric algorithm so both are needed 65 assert(y == x); 66 assert(x == y); 67 68 static if (X.hasValue) 69 { 70 assert(equal(x.byKey, 71 y.byKey)); 72 assert(equal(x.byValue, 73 y.byValue)); 74 assert(equal(x.byKeyValue, 75 y.byKeyValue)); 76 } 77 else 78 { 79 assert(equal(x.byElement, 80 y.byElement)); 81 } 82 83 debug static assert(!__traits(compiles, { const _ = x < y; })); // no ordering 84 85 return y; 86 } 87 88 alias NullableUlong = Nullable!(ulong, ulong.max); 89 90 static class SomeSimpleClass 91 { 92 @safe pure nothrow @nogc 93 this(ulong value) 94 { 95 this._value = value; 96 } 97 98 @safe pure nothrow @nogc 99 ulong get() const 100 { 101 return _value; 102 } 103 104 @property void toString(scope void delegate(scope const(char)[]) sink) const 105 { 106 import std.format : formattedWrite; 107 sink.formattedWrite(typeof(this).stringof, "(%s)", _value); 108 } 109 110 @property bool opEquals(const scope typeof(this) rhs) const 111 { 112 return _value == rhs._value; 113 } 114 115 private ulong _value; 116 } 117 118 debug static assert(mustAddGCRange!string); 119 120 foreach (K; AliasSeq!(SomeSimpleClass, 121 NullableUlong)) 122 { 123 foreach (V; AliasSeq!(string, 124 int, 125 void)) 126 { 127 alias X = OpenHashMap!(K, V, FNV!(64, true)); 128 129 auto k11 = make!K(11); 130 auto k12 = make!K(12); 131 auto k13 = make!K(13); 132 133 static if (!X.hasValue) 134 { 135 auto x = X.withElements([k11, k12, k13].s); 136 137 import std.algorithm : count; 138 139 // ByLvalueElement 140 auto xr = x.byElement; 141 142 alias R = typeof(xr); 143 import std.range.primitives : isInputRange; 144 import std.traits : ReturnType; 145 debug static assert(is(typeof(R.init) == R)); 146 debug static assert(is(ReturnType!((R xr) => xr.empty) == bool)); 147 148 debug static assert(!__traits(compiles, { xr.front == K.init; })); // always head-const 149 auto f = xr.front; 150 static if (is(K == class)) 151 { 152 debug static assert(is(typeof(f) == K)); // tail-mutable 153 } 154 else 155 { 156 debug static assert(is(typeof(f) == const(K))); // tail-const 157 } 158 159 debug static assert(is(typeof((R xr) => xr.front))); 160 debug static assert(!is(ReturnType!((R xr) => xr.front) == void)); 161 debug static assert(is(typeof((R xr) => xr.popFront))); 162 163 debug static assert(isInputRange!(typeof(xr))); 164 165 assert(x.byElement.count == 3); 166 167 X y; 168 size_t ix = 0; 169 foreach (ref e; x.byElement) 170 { 171 assert(x.contains(e)); 172 assert(x.containsUsingLinearSearch(e)); 173 assert(!y.contains(e)); 174 assert(!y.containsUsingLinearSearch(e)); 175 static if (is(K == class)) 176 { 177 y.insert(cast(K)e); // ugly but ok in tests 178 } 179 else 180 { 181 y.insert(e); 182 } 183 assert(y.contains(e)); 184 assert(y.containsUsingLinearSearch(e)); 185 ix++; 186 } 187 188 assert(y.byElement.count == 3); 189 assert(x == y); 190 191 const z = X(); 192 assert(z.byElement.count == 0); 193 194 immutable w = X(); 195 assert(w.byElement.count == 0); 196 197 { 198 auto xc = X.withElements([k11, k12, k13].s); 199 assert(xc.length == 3); 200 assert(xc.contains(k11)); 201 assert(xc.containsUsingLinearSearch(k11)); 202 203 // TODO http://forum.dlang.org/post/kvwrktmameivubnaifdx@forum.dlang.org 204 xc.removeAllMatching!(_ => _ == k11); 205 206 assert(xc.length == 2); 207 assert(!xc.contains(k11)); 208 assert(!xc.containsUsingLinearSearch(k11)); 209 210 xc.removeAllMatching!(_ => _ == k12); 211 assert(!xc.contains(k12)); 212 assert(!xc.containsUsingLinearSearch(k12)); 213 assert(xc.length == 1); 214 215 xc.removeAllMatching!(_ => _ == k13); 216 assert(!xc.contains(k13)); 217 assert(!xc.containsUsingLinearSearch(k13)); 218 assert(xc.length == 0); 219 220 // this is ok 221 foreach (e; xc.byElement) {} 222 } 223 224 { // ByRvalueElement 225 auto k = X.withElements([k11, k12].s).filtered!(_ => _ != k11).byElement; 226 debug static assert(isInputRange!(typeof(k))); 227 assert(k.front == k12); 228 229 debug static assert(!__traits(compiles, { k.front = K.init; })); // head-const 230 static if (is(K == class)) 231 { 232 debug static assert(is(typeof(k.front) == K)); // tail-mutable 233 } 234 else 235 { 236 debug static assert(is(typeof(k.front) == const(K))); // tail-const 237 } 238 239 k.popFront(); 240 assert(k.empty); 241 } 242 243 { 244 X q; 245 auto qv = [make!K(11U), make!K(12U), make!K(13U), make!K(14U)].s; 246 q.insertN(qv[]); 247 foreach (e; qv[]) 248 { 249 assert(q.contains(e)); 250 assert(q.containsUsingLinearSearch(e)); 251 } 252 q.clear(); 253 assert(q.empty); 254 } 255 } 256 257 static if (is(V == string)) 258 { 259 debug static assert(mustAddGCRange!V); 260 debug static assert(mustAddGCRange!(V[1])); 261 debug static assert(mustAddGCRange!(X.T)); 262 } 263 264 auto x1 = X(); // start empty 265 266 // fill x1 267 268 import std.array : Appender; 269 Appender!(K[]) keys; 270 271 foreach (immutable key_; 0 .. n) 272 { 273 auto key = make!K(key_); 274 keys.put(key); 275 276 // create elements 277 static if (X.hasValue) 278 { 279 auto value = V.init; 280 auto element = X.ElementType(key, value); 281 } 282 else 283 { 284 // no assignment because Nullable.opAssign may leave rhs in null state 285 auto element = key; 286 } 287 288 assert(key !in x1); 289 290 assert(x1.length == key.get); 291 assert(x1.insert(element) == X.InsertionStatus.added); 292 assert(x1.length == key.get + 1); 293 294 static if (X.hasValue) 295 { 296 import std.conv : to; 297 auto e2 = X.ElementType(key, (42 + key_).to!V); 298 assert(x1.insert(e2) == X.InsertionStatus.modified); 299 assert(x1.contains(key)); 300 assert(x1.get(key, V.init) == (42 + key_).to!V); 301 302 assert(x1.remove(key)); 303 assert(!x1.contains(key)); 304 305 x1[key] = value; // restore value 306 assert(x1.contains(key)); 307 } 308 309 assert(x1.length == key.get + 1); 310 311 const hitPtr = key in x1; 312 static if (X.hasValue) 313 { 314 assert(hitPtr && *hitPtr == value); 315 } 316 else 317 { 318 assert(hitPtr && *hitPtr is key); 319 } 320 321 auto status = x1.insert(element); 322 assert(status == X.InsertionStatus.unmodified); 323 static if (X.hasValue) 324 { 325 assert(x1.insert(key, value) == X.InsertionStatus.unmodified); 326 } 327 assert(x1.length == key.get + 1); 328 329 assert(key in x1); 330 } 331 332 static if (X.hasValue) 333 { 334 import nxt.dynamic_array : Array = DynamicArray; 335 Array!(X.ElementType) a1; // remember the keys 336 337 foreach (const ref key; x1.byKey) 338 { 339 auto keyPtr = key in x1; 340 assert(keyPtr); 341 a1 ~= X.ElementType(cast(K)key, (*keyPtr)); 342 } 343 344 assert(x1.length == a1.length); 345 346 foreach (ae; a1[]) 347 { 348 auto keyPtr = ae.key in x1; 349 assert(keyPtr); 350 assert((*keyPtr) is ae.value); 351 } 352 } 353 354 assert(x1.length == n); 355 356 auto x2 = testDup(x1, n); 357 358 testEmptyAll!(K, V)(x1, n, keys.data); 359 360 testEmptyAll!(K, V)(x2, n, keys.data); // should be not affected by emptying of x1 361 } 362 }
version(showEntries) dbg(); import std.typecons : Nullable; import nxt.digestx.fnv : FNV; alias X = OpenHashMap!(Nullable!(size_t, size_t.max), size_t, FNV!(64, true)); import nxt.dynamic_array : Array = DynamicArray; X x; // TODO these segfault: // TODO auto a = Array!(X.KeyType).withElementsOfRange_untested(x.byKey); // l-value byKey // TODO auto b = Array!(X.KeyType).withElementsOfRange_untested(X().byKey); // r-value byKey
manual Nullable type
import nxt.digestx.fnv : FNV; static class Zing { @safe pure nothrow @nogc: this(ulong value) { this._value = value; } private ulong _value; } debug static assert(isNullable!Zing); enum Alt { unknown, a, b, c, d } struct ZingRelation { Zing zing; Alt alts; alias nullifier = zing; static immutable nullValue = typeof(this).init; bool opEquals(const scope typeof(this) that) const @safe pure nothrow @nogc { return (this.zing is that.zing && this.alts == that.alts); } } debug static assert(isNullable!ZingRelation); alias X = OpenHashSet!(ZingRelation, FNV!(64, true)); debug static assert(X.sizeof == 32); // TODO fix hole handling and change to 24 X x; scope e = ZingRelation(new Zing(42), Alt.init); assert(!x.contains(e)); assert(!x.containsUsingLinearSearch(e)); assert(x.insert(e) == X.InsertionStatus.added); assert(x.contains(e)); assert(x.containsUsingLinearSearch(e));
abstract class value type
static abstract class Zing { @safe pure nothrow @nogc: } static class Node : Zing { @safe pure nothrow @nogc: } alias X = OpenHashSet!(Zing); X x; const Zing cz = new Node(); x.insert(cz); // ok to insert const Zing z = new Node(); x.insert(z); // ok to insert mutable because hashing is on address by default
class type with default hashing
1 static class Base 2 { 3 static size_t dtorCount = 0; // number of calls to this destructor 4 @safe nothrow @nogc: 5 ~this() @nogc { dtorCount += 1; } 6 pure: 7 this(ulong value) { this._value = value; } 8 @property bool opEquals(const scope typeof(this) rhs) const 9 { 10 return _value == rhs._value; 11 } 12 override hash_t toHash() const 13 { 14 return hashOf(_value); 15 } 16 private ulong _value; 17 } 18 19 /** Node containing same data members but different type. */ 20 static class Node : Base 21 { 22 @safe pure nothrow @nogc: 23 this(ulong value) { super(value); } 24 } 25 debug static assert(is(Node : Base)); 26 27 import nxt.hash_functions : hashOfPolymorphic; // neede to separate hash of `Base(N)` from `Node(N)` 28 alias X = OpenHashSet!(Base, hashOfPolymorphic, "a && b && (typeid(a) is typeid(b)) && a.opEquals(b)"); 29 debug static assert(X.sizeof == 24); 30 X x; 31 32 // top-class 33 scope b42 = new Base(42); 34 assert(!x.contains(b42)); 35 assert(!x.containsUsingLinearSearch(b42)); 36 assert(x.insert(b42) == X.InsertionStatus.added); 37 assert(x.contains(b42)); 38 assert(x.containsUsingLinearSearch(b42)); 39 assert(x.tryGetElementFromCtorParams!Base(42) !is null); 40 assert(Base.dtorCount == 1); 41 assert(x.tryGetElementFromCtorParams!Base(42)._value == 42); 42 assert(Base.dtorCount == 2); 43 assert(x.tryGetElementFromCtorParams!Base(41) is null); 44 assert(Base.dtorCount == 3); 45 46 // top-class 47 scope b43 = new Base(43); 48 assert(!x.contains(b43)); 49 assert(!x.containsUsingLinearSearch(b43)); 50 assert(x.insert(b43) == X.InsertionStatus.added); 51 assert(x.contains(b43)); 52 assert(x.containsUsingLinearSearch(b43)); 53 assert(x.tryGetElementFromCtorParams!Base(43) !is null); 54 assert(Base.dtorCount == 4); 55 assert(x.tryGetElementFromCtorParams!Base(43)._value == 43); 56 assert(Base.dtorCount == 5); 57 58 // sub-class 59 assert(x.tryGetElementFromCtorParams!Node(42) is null); 60 assert(Base.dtorCount == 6); 61 immutable n42 = new Node(42); 62 assert(!x.contains(n42)); // mustn't equal to `b42` 63 assert(!x.containsUsingLinearSearch(n42)); // mustn't equal to `b42` 64 assert(x.insert(n42) == X.InsertionStatus.added); // added as separate type 65 assert(x.contains(n42)); 66 assert(x.containsUsingLinearSearch(n42)); 67 assert(x.tryGetElementFromCtorParams!Node(42) !is null); 68 assert(Base.dtorCount == 7); 69 assert(x.tryGetElementFromCtorParams!Node(42)._value == 42); 70 assert(Base.dtorCount == 8); 71 72 assert(hashOf(b42) == hashOf(n42)); 73 74 // sub-class 75 assert(x.tryGetElementFromCtorParams!Node(43) is null); 76 assert(Base.dtorCount == 9); 77 auto n43 = new Node(43); 78 assert(!x.contains(n43)); // mustn't equal to `b43` 79 assert(!x.containsUsingLinearSearch(n43)); // mustn't equal to `b43` 80 assert(x.insert(n43) == X.InsertionStatus.added); // added as separate type 81 assert(x.contains(n43)); 82 assert(x.containsUsingLinearSearch(n43)); 83 assert(x.tryGetElementFromCtorParams!Node(43) !is null); 84 assert(Base.dtorCount == 10); 85 assert(x.tryGetElementFromCtorParams!Node(43)._value == 43); 86 assert(Base.dtorCount == 11); 87 88 assert(hashOf(b43) == hashOf(n43));
enumeration key
import nxt.digestx.fnv : FNV; enum Alt { nullValue, // trait a, b, c, d } alias X = OpenHashSet!(Alt, FNV!(64, true)); X x; assert(!x.contains(Alt.a)); assert(x.insert(Alt.a) == X.InsertionStatus.added); assert(x.contains(Alt.a)); assert(x.containsUsingLinearSearch(Alt.a)); assert(!x.contains(Alt.b)); assert(!x.contains(Alt.c)); assert(!x.contains(Alt.d)); assert(!x.containsUsingLinearSearch(Alt.b)); assert(!x.containsUsingLinearSearch(Alt.c)); assert(!x.containsUsingLinearSearch(Alt.d)); assert(x.remove(Alt.a)); assert(!x.contains(Alt.a)); assert(!x.containsUsingLinearSearch(Alt.a));
import nxt.digestx.fnv : FNV; static struct Rel { static immutable nullValue = typeof(this).init; string name; // relation name. WARNING compiler crashes when qualified with `package` } alias X = OpenHashSet!(Rel, FNV!(64, true)); X x; foreach (const i; 0 .. 100) { const char[1] ch = ['a' + i]; assert(!x.contains(Rel(ch.idup))); assert(!x.containsUsingLinearSearch(Rel(ch.idup))); x.insert(Rel(ch.idup)); assert(x.contains(Rel(ch.idup))); /* TODO assert(x.containsUsingLinearSearch(Rel(ch.idup))); */ }
SSOString as set key type
1 import nxt.sso_string : SSOString; 2 import nxt.digestx.fnv : FNV; 3 4 alias K = SSOString; 5 static assert(isHoleable!K); 6 alias X = OpenHashSet!(K, FNV!(64, true)); 7 const n = 100; 8 9 X a; 10 foreach (const i; 0 .. n) 11 { 12 const char[1] ch = ['a' + i]; 13 const k = K(ch); // @nogc 14 15 assert(!a.contains(k)); 16 assert(!a.containsUsingLinearSearch(k)); 17 18 assert(a.insert(K(ch)) == X.InsertionStatus.added); 19 // TODO assert(a.insertAndReturnElement(K(ch)) == k); 20 assert(a.contains(k)); 21 assert(a.containsUsingLinearSearch(k)); 22 23 assert(a.remove(k)); 24 assert(!a.contains(k)); 25 assert(a.insert(K(ch)) == X.InsertionStatus.added); 26 27 assert(a.remove(ch[])); 28 assert(!a.contains(k)); 29 assert(a.insert(K(ch)) == X.InsertionStatus.added); 30 } 31 32 X b; 33 foreach (const i; 0 .. n) 34 { 35 const char[1] ch = ['a' + (n - 1 - i)]; 36 const k = K(ch); // @nogc 37 38 assert(!b.contains(k)); 39 assert(!b.containsUsingLinearSearch(k)); 40 41 assert(b.insert(K(ch)) == X.InsertionStatus.added); 42 // TODO assert(b.insertAndReturnElement(K(ch)) == k); 43 44 assert(b.contains(k)); 45 assert(b.containsUsingLinearSearch(k)); 46 47 assert(b.remove(k)); 48 assert(!b.contains(k)); 49 50 assert(b.insert(K(ch)) == X.InsertionStatus.added); 51 } 52 53 assert(a == b); 54 55 const us = K("_"); 56 assert(!a.contains(us)); 57 a ~= us; 58 assert(a.contains(us));
test opIndexOpAssign
import nxt.sso_string : SSOString; import nxt.digestx.fnv : FNV; alias K = SSOString; alias V = long; alias X = OpenHashMap!(K, V, FNV!(64, true)); X x; const a = K("a"); const b = K("b"); x[a] = 17; assert(x[a] == 17); x[a] += 10; // opIndexOpAssign!("+=") with existing key assert(x[a] == 27); x[b] += 10; // opIndexOpAssign!("+=") with non-existing key assert(x[b] == 10); x[b] *= 10; // opIndexOpAssign!("*=") with non-existing key assert(x[b] == 100); assert(x.length == 2); assert(x.contains(a)); assert(x.contains(a[])); assert(a in x); assert(a[] in x); assert(x.contains(b)); assert(x.contains(b[])); assert(b in x); assert(b[] in x); const c = K("c"); assert(!x.contains(c)); assert(!x.contains(c[])); assert(c !in x); assert(c[] !in x);
use prime numbers as capacity
import nxt.address : Address; alias K = Address; alias V = size_t; enum bool usePrimeCapacity = false; // TODO enable alias M = OpenHashMap!(Address, V, hashOf, defaultKeyEqualPredOf!K, Mallocator.instance, false, true, usePrimeCapacity); M x;
SSOString as map key type
1 import nxt.sso_string : SSOString; 2 import nxt.digestx.fnv : FNV; 3 alias K = SSOString; 4 alias V = long; 5 alias X = OpenHashMap!(K, V, FNV!(64, true)); 6 const n = 100; 7 8 immutable default_k = K("miss"); 9 10 X a; 11 12 // insert all 13 foreach (const i; 0 .. n) 14 { 15 const char[1] ch = ['a' + i]; 16 const k = K(ch); // @nogc 17 assert(k[] == ch[]); 18 19 assert(!a.contains(k)); 20 assert(!a.contains(ch[])); // @nogc 21 assert(a.getKeyRef(k, default_k)[] is default_k[]); // on miss use `default_k` 22 // TODO assert(a.getKeyRef(ch, default_k)[] is default_k[]); // on miss use `default_k` 23 24 a[k] = V.init; 25 26 assert(a.contains(k)); 27 assert(a.contains(ch[])); // @nogc 28 assert(a.getKeyRef(k, default_k)[] !is k[]); // on hit doesn't use `default_k` 29 assert(a.getKeyRef(k, default_k)[] == ch); 30 // TODO assert(a.getKeyRef(ch, default_k)[] !is k[]); // on hit doesn't use `default_k` 31 // assert(a.getKeyRef(ch, default_k)[] == ch); 32 } 33 assert(a.length == n); 34 35 // remove all 36 foreach (const i; 0 .. n) 37 { 38 const char[1] ch = ['a' + i]; 39 const k = K(ch); // @nogc 40 assert(a.contains(k)); 41 assert(a.remove(k)); 42 assert(!a.contains(k)); 43 } 44 assert(a.length == 0); 45 46 // insert all again 47 foreach (const i; 0 .. n) 48 { 49 const char[1] ch = ['a' + i]; 50 const k = K(ch); // @nogc 51 assert(k[] == ch[]); 52 53 assert(!a.contains(k)); 54 assert(!a.contains(ch[])); // @nogc 55 assert(a.getKeyRef(k, default_k)[] is default_k[]); // on miss use `default_k` 56 // TODO assert(a.getKeyRef(ch, default_k)[] is default_k[]); // on miss use `default_k` 57 58 a[k] = V.init; 59 } 60 assert(a.length == n); 61 62 X b; 63 foreach (const i; 0 .. n) 64 { 65 const char[1] ch = ['a' + (n - 1 - i)]; 66 const k = K(ch); // @nogc 67 68 assert(!b.contains(k)); 69 70 b[k] = V.init; 71 72 assert(b.contains(k)); 73 } 74 75 assert(a == b);