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