1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 module thrift.util.hashset; 20 21 import std.algorithm : joiner, map; 22 import std.conv : to; 23 import std.traits : isImplicitlyConvertible, ParameterTypeTuple; 24 import std.range : ElementType, isInputRange; 25 26 /** 27 * A quickly hacked together hash set implementation backed by built-in 28 * associative arrays to have something to compile Thrift's set<> to until 29 * std.container gains something suitable. 30 */ 31 // Note: The funky pointer casts (i.e. *(cast(immutable(E)*)&e) instead of 32 // just cast(immutable(E))e) are a workaround for LDC 2 compatibilty. 33 final class HashSet(E) { 34 /// 35 this() {} 36 37 /// 38 this(E[] elems...) { 39 insert(elems); 40 } 41 42 /// 43 void insert(Stuff)(Stuff stuff) if (isImplicitlyConvertible!(Stuff, E)) { 44 aa_[*(cast(immutable(E)*)&stuff)] = []; 45 } 46 47 /// 48 void insert(Stuff)(Stuff stuff) if ( 49 isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, E) 50 ) { 51 foreach (e; stuff) { 52 aa_[*(cast(immutable(E)*)&e)] = []; 53 } 54 } 55 56 /// 57 void opOpAssign(string op : "~", Stuff)(Stuff stuff) { 58 insert(stuff); 59 } 60 61 /// 62 void remove(E e) { 63 aa_.remove(*(cast(immutable(E)*)&e)); 64 } 65 alias remove removeKey; 66 67 /// 68 void removeAll() { 69 aa_ = null; 70 } 71 72 /// 73 size_t length() @property const { 74 return aa_.length; 75 } 76 77 /// 78 size_t empty() @property const { 79 return !aa_.length; 80 } 81 82 /// 83 bool opBinaryRight(string op : "in")(E e) const { 84 return (e in aa_) !is null; 85 } 86 87 /// 88 auto opSlice() const { 89 // TODO: Implement using AA key range once availabe in release DMD/druntime 90 // to avoid allocation. 91 return cast(E[])(aa_.keys); 92 } 93 94 /// 95 override string toString() const { 96 // Only provide toString() if to!string() is available for E (exceptions are 97 // e.g. delegates). 98 static if (is(typeof(to!string(E.init)) : string)) { 99 return "{" ~ to!string(joiner(map!`to!string(a)`(aa_.keys), ", ")) ~ "}"; 100 } else { 101 // Cast to work around Object not being const-correct. 102 return (cast()super).toString(); 103 } 104 } 105 106 /// 107 override bool opEquals(Object other) const { 108 auto rhs = cast(const(HashSet))other; 109 if (rhs) { 110 return aa_ == rhs.aa_; 111 } 112 113 // Cast to work around Object not being const-correct. 114 return (cast()super).opEquals(other); 115 } 116 117 private: 118 alias void[0] Void; 119 Void[immutable(E)] aa_; 120 } 121 122 /// Ditto 123 auto hashSet(E)(E[] elems...) { 124 return new HashSet!E(elems); 125 } 126 127 unittest { 128 import std.exception; 129 130 auto a = hashSet(1, 2, 2, 3); 131 enforce(a.length == 3); 132 enforce(2 in a); 133 enforce(5 !in a); 134 enforce(a.toString().length == 9); 135 a.remove(2); 136 enforce(a.length == 2); 137 enforce(2 !in a); 138 a.removeAll(); 139 enforce(a.empty); 140 enforce(a.toString() == "{}"); 141 142 void delegate() dg; 143 auto b = hashSet(dg); 144 static assert(__traits(compiles, b.toString())); 145 }