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, int, void)) 124 { 125 alias X = OpenHashMap!(K, V, FNV!(64, true)); 126 127 auto k11 = make!K(11); 128 auto k12 = make!K(12); 129 auto k13 = make!K(13); 130 131 static if (!X.hasValue) 132 { 133 auto x = X.withElements([k11, k12, k13].s); 134 135 import std.algorithm : count; 136 137 // ByLvalueElement 138 auto xr = x.byElement; 139 140 alias R = typeof(xr); 141 import std.range.primitives : isInputRange; 142 import std.traits : ReturnType; 143 debug static assert(is(typeof(R.init) == R)); 144 debug static assert(is(ReturnType!((R xr) => xr.empty) == bool)); 145 146 debug static assert(!__traits(compiles, { xr.front == K.init; })); // always head-const 147 auto f = xr.front; 148 static if (is(K == class)) 149 { 150 debug static assert(is(typeof(f) == K)); // tail-mutable 151 } 152 else 153 { 154 debug static assert(is(typeof(f) == const(K))); // tail-const 155 } 156 157 debug static assert(is(typeof((R xr) => xr.front))); 158 debug static assert(!is(ReturnType!((R xr) => xr.front) == void)); 159 debug static assert(is(typeof((R xr) => xr.popFront))); 160 161 debug static assert(isInputRange!(typeof(xr))); 162 163 assert(x.byElement.count == 3); 164 165 X y; 166 size_t ix = 0; 167 foreach (ref e; x.byElement) 168 { 169 assert(x.contains(e)); 170 assert(x.containsUsingLinearSearch(e)); 171 assert(!y.contains(e)); 172 assert(!y.containsUsingLinearSearch(e)); 173 static if (is(K == class)) 174 { 175 y.insert(cast(K)e); // ugly but ok in tests 176 } 177 else 178 { 179 y.insert(e); 180 } 181 assert(y.contains(e)); 182 assert(y.containsUsingLinearSearch(e)); 183 ix++; 184 } 185 186 assert(y.byElement.count == 3); 187 assert(x == y); 188 189 const z = X(); 190 assert(z.byElement.count == 0); 191 192 immutable w = X(); 193 assert(w.byElement.count == 0); 194 195 { 196 auto xc = X.withElements([k11, k12, k13].s); 197 assert(xc.length == 3); 198 assert(xc.contains(k11)); 199 assert(xc.containsUsingLinearSearch(k11)); 200 201 // TODO http://forum.dlang.org/post/kvwrktmameivubnaifdx@forum.dlang.org 202 xc.removeAllMatching!(_ => _ == k11); 203 204 assert(xc.length == 2); 205 assert(!xc.contains(k11)); 206 assert(!xc.containsUsingLinearSearch(k11)); 207 208 xc.removeAllMatching!(_ => _ == k12); 209 assert(!xc.contains(k12)); 210 assert(!xc.containsUsingLinearSearch(k12)); 211 assert(xc.length == 1); 212 213 xc.removeAllMatching!(_ => _ == k13); 214 assert(!xc.contains(k13)); 215 assert(!xc.containsUsingLinearSearch(k13)); 216 assert(xc.length == 0); 217 218 // this is ok 219 foreach (e; xc.byElement) {} 220 } 221 222 { // ByRvalueElement 223 auto k = X.withElements([k11, k12].s).filtered!(_ => _ != k11).byElement; 224 debug static assert(isInputRange!(typeof(k))); 225 assert(k.front == k12); 226 227 debug static assert(!__traits(compiles, { k.front = K.init; })); // head-const 228 static if (is(K == class)) 229 { 230 debug static assert(is(typeof(k.front) == K)); // tail-mutable 231 } 232 else 233 { 234 debug static assert(is(typeof(k.front) == const(K))); // tail-const 235 } 236 237 k.popFront(); 238 assert(k.empty); 239 } 240 241 { 242 X q; 243 auto qv = [make!K(11U), make!K(12U), make!K(13U), make!K(14U)].s; 244 q.insertN(qv[]); 245 foreach (e; qv[]) 246 { 247 assert(q.contains(e)); 248 assert(q.containsUsingLinearSearch(e)); 249 } 250 q.clear(); 251 assert(q.empty); 252 } 253 } 254 255 static if (is(V == string)) 256 { 257 debug static assert(mustAddGCRange!V); 258 debug static assert(mustAddGCRange!(V[1])); 259 debug static assert(mustAddGCRange!(X.T)); 260 } 261 262 auto x1 = X(); // start empty 263 264 // fill x1 265 266 import std.array : Appender; 267 Appender!(K[]) keys; 268 269 foreach (immutable key_; 0 .. n) 270 { 271 auto key = make!K(key_); 272 keys.put(key); 273 274 // create elements 275 static if (X.hasValue) 276 { 277 auto value = V.init; 278 auto element = X.ElementType(key, value); 279 } 280 else 281 { 282 // no assignment because Nullable.opAssign may leave rhs in null state 283 auto element = key; 284 } 285 286 assert(key !in x1); 287 288 assert(x1.length == key.get); 289 assert(x1.insert(element) == X.InsertionStatus.added); 290 assert(x1.length == key.get + 1); 291 292 static if (X.hasValue) 293 { 294 import std.conv : to; 295 auto e2 = X.ElementType(key, (42 + key_).to!V); 296 assert(x1.insert(e2) == X.InsertionStatus.modified); 297 assert(x1.contains(key)); 298 assert(x1.get(key, V.init) == (42 + key_).to!V); 299 300 assert(x1.remove(key)); 301 assert(!x1.contains(key)); 302 303 x1[key] = value; // restore value 304 assert(x1.contains(key)); 305 } 306 307 assert(x1.length == key.get + 1); 308 309 const hitPtr = key in x1; 310 static if (X.hasValue) 311 { 312 assert(hitPtr && *hitPtr == value); 313 } 314 else 315 { 316 assert(hitPtr && *hitPtr is key); 317 } 318 319 auto status = x1.insert(element); 320 assert(status == X.InsertionStatus.unmodified); 321 static if (X.hasValue) 322 { 323 assert(x1.insert(key, value) == X.InsertionStatus.unmodified); 324 } 325 assert(x1.length == key.get + 1); 326 327 assert(key in x1); 328 } 329 330 static if (X.hasValue) 331 { 332 import nxt.dynamic_array : Array = DynamicArray; 333 Array!(X.ElementType) a1; // remember the keys 334 335 foreach (const ref key; x1.byKey) 336 { 337 auto keyPtr = key in x1; 338 assert(keyPtr); 339 a1 ~= X.ElementType(cast(K)key, (*keyPtr)); 340 } 341 342 assert(x1.length == a1.length); 343 344 foreach (ae; a1[]) 345 { 346 auto keyPtr = ae.key in x1; 347 assert(keyPtr); 348 assert((*keyPtr) is ae.value); 349 } 350 } 351 352 assert(x1.length == n); 353 354 auto x2 = testDup(x1, n); 355 356 testEmptyAll!(K, V)(x1, n, keys.data); 357 358 testEmptyAll!(K, V)(x2, n, keys.data); // should be not affected by emptying of x1 359 } 360 }
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);