range that iterates through the key-value-pairs of c in undefined order.
make range from l-value and r-value. element access is always const
1 version(showEntries) dbg(); 2 3 import core.exception : AssertError; 4 import std.typecons : Nullable; 5 import nxt.digestx.fnv : FNV; 6 import nxt.array_help : s; 7 debug import std.exception : assertThrown; 8 9 import std.algorithm.searching : count; 10 alias K = Nullable!(uint, uint.max); 11 alias X = OpenHashSet!(K, 12 FNV!(64, true), 13 defaultKeyEqualPredOf!K, 14 Mallocator.instance, 15 true); 16 17 auto k11 = K(11); 18 auto k22 = K(22); 19 auto k33 = K(33); 20 auto ks = [k11, k22, k33].s; 21 auto k44 = K(44); 22 23 // mutable 24 auto x = X.withElements(ks); 25 assert(!x.contains(k44)); 26 assert(!x.containsUsingLinearSearch(k44)); 27 assert(x.length == 3); 28 29 assert(x.byElement.count == x.length); 30 foreach (e; x.byElement) // from l-value 31 { 32 debug static assert(is(typeof(e) == const(K))); // always const access 33 34 // range invalidation forbidden: 35 debug 36 { 37 assertThrown!AssertError(x.reserveExtra(1)); // range invalidation 38 assertThrown!AssertError(x.clear()); // range invalidation 39 assertThrown!AssertError(x.insert(k11)); // range invalidation 40 assertThrown!AssertError(x.insertN([k11].s)); // range invalidation 41 assertThrown!AssertError(x.remove(k11)); // range invalidation 42 } 43 44 // allowed 45 assert(x.contains(e)); 46 assert(x.containsUsingLinearSearch(e)); 47 48 const eHit = e in x; 49 assert(eHit); // found 50 assert(*eHit is e); // and the value equals what we searched for 51 52 const eDup = x.dup; // duplication is `const` and allowed 53 assert(eDup == x); 54 } 55 56 // const 57 const y = X.withElements(ks); 58 assert(!x.contains(k44)); 59 assert(!x.containsUsingLinearSearch(k44)); 60 foreach (e; y.byElement) // from l-value 61 { 62 auto _ = y.byElement; // ok to read-borrow again 63 assert(y.contains(e)); 64 assert(y.containsUsingLinearSearch(e)); 65 debug static assert(is(typeof(e) == const(K))); 66 } 67 68 foreach (e; X.withElements([K(11)].s).byElement) // from r-value 69 { 70 assert(e == K(11)); 71 debug static assert(is(typeof(e) == const(K))); // always const access 72 }
range checking
version(showEntries) dbg(); import core.exception : RangeError; import std.typecons : Nullable; import nxt.digestx.fnv : FNV; debug import std.exception : assertThrown, assertNotThrown; immutable n = 11; alias K = Nullable!(uint, uint.max); alias V = uint; alias X = OpenHashMap!(K, V, FNV!(64, true)); auto s = X.withCapacity(n); void dummy(ref V value) {} debug assertThrown!RangeError(dummy(s[K(0)])); foreach (immutable i; 0 .. n) { const k = K(i); s[k] = V(i); debug assertNotThrown!RangeError(dummy(s[k])); } foreach (immutable i; 0 .. n) { const k = K(i); assert(s.remove(k)); debug assertThrown!RangeError(dummy(s[k])); } s[K(0)] = V.init; auto vp = K(0) in s; debug static assert(is(typeof(vp) == V*)); assert((*vp) == V.init); assert(s.remove(K(0))); assert(K(0) !in s); X t; t.reserveExtra(4096); t.clear();
class as value
1 version(showEntries) dbg(); 2 import core.exception : RangeError; 3 import std.typecons : Nullable; 4 debug import std.exception : assertThrown, assertNotThrown; 5 import nxt.digestx.fnv : FNV; 6 7 immutable n = 11; 8 9 alias K = Nullable!(uint, uint.max); 10 class V 11 { 12 this(uint data) { this.data = data; } 13 uint data; 14 } 15 16 alias X = OpenHashMap!(K, V, FNV!(64, true)); 17 18 auto s = X.withCapacity(n); 19 20 void dummy(ref V value) {} 21 22 debug assertThrown!RangeError(dummy(s[K(0)])); 23 24 foreach (immutable i; 0 .. n) 25 { 26 const k = K(i); 27 s[k] = new V(i); 28 debug assertNotThrown!RangeError(dummy(s[k])); 29 } 30 31 // test range 32 { 33 auto sr = s.byKeyValue; // scoped range 34 assert(sr.length == n); 35 foreach (immutable i; 0 .. n) 36 { 37 sr.popFront(); 38 assert(sr.length == n - i - 1); 39 } 40 } 41 42 foreach (immutable i; 0 .. n) 43 { 44 const k = K(i); 45 assert(s.remove(k)); 46 debug assertThrown!RangeError(dummy(s[k])); 47 } 48 49 s[K(0)] = V.init; 50 auto vp = K(0) in s; 51 debug static assert(is(typeof(vp) == V*)); 52 53 assert(s.remove(K(0))); 54 assert(K(0) !in s); 55 56 X t; 57 t.reserveExtra(4096);
constness inference of ranges
version(showEntries) dbg(); import std.typecons : Nullable; import nxt.digestx.fnv : FNV; alias K = Nullable!(uint, uint.max); class V { this(uint data) { this.data = data; } uint data; } alias X = OpenHashMap!(K, V, FNV!(64, true)); const x = X(); foreach (const e; x.byKey) { debug static assert(is(typeof(e) == const(X.KeyType))); } foreach (const e; x.byValue) { debug static assert(is(typeof(e) == const(X.ValueType))); } foreach (const e; X.init.byValue) { debug static assert(is(typeof(e) == const(X.ValueType))); } foreach (const e; x.byKeyValue) { debug static assert(is(typeof(e.key) == const(X.KeyType))); debug static assert(is(typeof(e.value) == const(X.ValueType))); debug static assert(is(typeof(e) == const(X.ElementType))); }
range key constness and value mutability with class value
version(showEntries) dbg(); import std.typecons : Nullable; import nxt.digestx.fnv : FNV; struct S { uint value; } alias K = Nullable!(S, S(uint.min)); // use uint.min to trigger use of faster `Allocator.allocateZeroed` class V { this(uint data) { this.data = data; } uint data; } alias X = OpenHashMap!(K, V, FNV!(64, true)); auto x = X(); x[K(S(42))] = new V(43); assert(x.length == 1); foreach (e; x.byValue) // `e` is auto ref { debug 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 { debug static assert(is(typeof(e.key) == const(X.KeyType))); // const access to key debug static assert(is(typeof(e.value) == X.ValueType)); // mutable access to value assert(e.key.value == 42); assert(e.value.data == 43); // key cannot be mutated debug static assert(!__traits(compiles, { e.key.value += 1; })); // value mutation side effects e.value.data += 1; assert(e.value.data == 44); e.value.data -= 1; assert(e.value.data == 43); }
range key constness and value mutability with class key and class value
1 import nxt.digestx.fnv : FNV; 2 3 version(showEntries) dbg(); 4 class K 5 { 6 this(uint value) 7 { 8 this.value = value; 9 } 10 11 @property bool opEquals(const scope typeof(this) rhs) const 12 { 13 return value == rhs.value; 14 } 15 16 uint value; 17 } 18 19 class V 20 { 21 this(uint data) { this.data = data; } 22 uint data; 23 } 24 25 alias X = OpenHashMap!(K, V, FNV!(64, true)); 26 auto x = X(); 27 28 x[new K(42)] = new V(43); 29 30 assert(x.length == 1); 31 32 foreach (e; x.byValue) // `e` is auto ref 33 { 34 debug static assert(is(typeof(e) == X.ValueType)); // mutable access to value 35 assert(e.data == 43); 36 37 // value mutation side effects 38 e.data += 1; 39 assert(e.data == 44); 40 e.data -= 1; 41 assert(e.data == 43); 42 } 43 44 foreach (ref e; x.byKeyValue) // `e` is auto ref 45 { 46 debug static assert(is(typeof(e.key) == X.KeyType)); // mutable access to class key 47 debug static assert(is(typeof(e.value) == X.ValueType)); // mutable access to value 48 49 assert(e.key.value == 42); 50 assert(e.value.data == 43); 51 52 // class key itself should not be mutable 53 debug static assert(!__traits(compiles, { e.key = null; })); 54 55 // members of key can be mutated 56 debug static assert(__traits(compiles, { e.key.value += 1; })); 57 58 // value mutation side effects 59 e.value.data += 1; 60 assert(e.value.data == 44); 61 e.value.data -= 1; 62 assert(e.value.data == 43); 63 }
range key constness and value mutability with class key and class value
1 import nxt.digestx.fnv : FNV; 2 version(showEntries) dbg(); 3 class K 4 { 5 this(uint value) 6 { 7 this.value = value; 8 } 9 uint value; 10 } 11 12 struct V 13 { 14 this(uint data) { this.data = data; } 15 @disable this(this); 16 uint data; 17 } 18 19 alias X = OpenHashMap!(K, V, FNV!(64, true)); 20 auto x = X(); 21 22 scope key42 = new K(42); 23 x[key42] = V(43); 24 25 assert(x.length == 1); 26 27 foreach (ref e; x.byValue) // `e` is auto ref 28 { 29 debug static assert(is(typeof(e) == X.ValueType)); // mutable access to value 30 assert(e.data == 43); 31 32 // value mutation side effects 33 e.data += 1; 34 assert(e.data == 44); 35 e.data -= 1; 36 assert(e.data == 43); 37 } 38 39 foreach (ref e; x.byKeyValue) // `e` is auto ref 40 { 41 debug static assert(is(typeof(e.key) == X.KeyType)); // mutable access to class key 42 debug static assert(is(typeof(e.value) == X.ValueType)); // mutable access to value 43 44 assert(e.key.value == 42); 45 assert(e.value.data == 43); 46 47 // value mutation side effects 48 e.value.data += 1; 49 assert(e.value.data == 44); 50 e.value.data -= 1; 51 assert(e.value.data == 43); 52 } 53 54 assert(x.length == 1); 55 56 assert(x.remove(key42)); 57 assert(x.length == 0); 58 59 x[key42] = V(43); 60 assert(x.length == 1);