1 /// Ordered Associative Array 2 module nwnlibd.orderedaa; 3 4 import std.typecons; 5 debug import std.stdio: writeln; 6 7 template isOpApplyDg(DG, KEY, VALUE) { 8 import std.traits; 9 static if (is(DG == delegate) && is(ReturnType!DG : int)) { 10 private alias PTT = ParameterTypeTuple!(DG); 11 private alias PSCT = ParameterStorageClassTuple!(DG); 12 private alias STC = ParameterStorageClass; 13 // Just a value 14 static if (PTT.length == 1) { 15 enum isOpApplyDg = (is(PTT[0] == VALUE)); 16 } else static if (PTT.length == 2) { 17 enum isOpApplyDg = (is(PTT[0] == KEY)) 18 && (is(PTT[1] == VALUE)); 19 } else 20 enum isOpApplyDg = false; 21 } else { 22 enum isOpApplyDg = false; 23 } 24 } 25 26 27 /// Ordered Associative Array 28 struct OrderedAA(KEY, VALUE){ 29 private alias THIS = OrderedAA!(KEY, VALUE); 30 private alias DataContainer = Tuple!(KEY, "key", VALUE, "value"); 31 32 /// 33 @property THIS dup() inout{ 34 THIS ret; 35 ret.data.length = data.length; 36 foreach(i, ref d ; ret.data){ 37 import std.meta: AliasSeq; 38 foreach(TUPLEI ; AliasSeq!(0,1)){ 39 static if(is(typeof(d[TUPLEI] = data[i][TUPLEI].dup))) 40 d[TUPLEI] = data[i][TUPLEI].dup; 41 else static if(is(typeof(d[TUPLEI] = data[i][TUPLEI]))) 42 d[TUPLEI] = data[i][TUPLEI]; 43 else 44 static assert(0, "Cannot duplicate type "~typeof(ret.data[0][TUPLEI]).stringof); 45 } 46 } 47 48 foreach(ref k, ref v ; map){ 49 static if(is(typeof(ret.map[k] = v.dup))) 50 ret.map[k] = v.dup; 51 else static if(is(typeof(ret.map[k] = v))) 52 ret.map[k] = v; 53 else 54 static assert(0, "Cannot duplicate type "~typeof(ret.data[0][TUPLEI]).stringof); 55 } 56 57 return ret; 58 } 59 60 /// 61 bool opEquals(const THIS rhs) const{ 62 if(length != rhs.length) 63 return false; 64 65 foreach(idx, ref kv ; data){ 66 if(kv.key!=rhs.data[idx].key || kv.value!=rhs.data[idx].value) 67 return false; 68 } 69 return true; 70 } 71 72 /// 73 @property bool empty() const{ 74 return data.length==0; 75 } 76 /// 77 @property size_t length() const{ 78 return data.length; 79 } 80 81 /// 82 void rehash(){ 83 map.rehash; 84 } 85 86 /// 87 void remove(KEY key) 88 { 89 immutable idx = map[key]; 90 if(idx+1<length) 91 data = data[0..idx]~data[idx+1..$]; 92 else 93 data = data[0..idx]; 94 map.remove(key); 95 foreach(ref k, ref v ; map){ 96 if(v>idx) v--; 97 } 98 } 99 100 /// 101 void clear(){ 102 data.length = 0; 103 map.clear; 104 } 105 106 /// 107 void opIndexAssign(VALUE value, KEY key){ 108 if(auto idx = key in map){ 109 data[*idx].value = value; 110 } 111 else{ 112 map[key] = data.length; 113 data ~= DataContainer(key, value); 114 } 115 } 116 /// 117 ref inout(VALUE) opIndex(KEY key) inout{ 118 immutable idx = map[key]; 119 return data[idx].value; 120 } 121 122 /// 123 inout(VALUE)* opBinaryRight(string op)(KEY key) inout if(op == "in"){ 124 auto idx = key in map; 125 if(idx is null) 126 return null; 127 128 return &(data[*idx].value); 129 } 130 131 /// 132 int opApply(DG)(scope DG dg) if(isOpApplyDg!(DG, KEY, VALUE)){ 133 import std.traits : arity; 134 135 foreach(ref kv ; data){ 136 static if(arity!dg == 1){ 137 if(auto res = dg(kv.value)) 138 return res; 139 } 140 else static if(arity!dg == 2){ 141 if(auto res = dg(kv.key, kv.value)) 142 return res; 143 } 144 else 145 static assert(0); 146 } 147 return 0; 148 } 149 150 /// 151 auto ref byKeyValue() inout{ 152 return data; 153 } 154 155 156 /// This function should not be used by sane people. 157 /// 158 /// Workaround for storing multiple values for a same key like in half-broken nwn2 gff files. 159 /// Only the newest value will be accessible using its key. 160 /// The older value will only be accessible using byKeyValue or iterating with foreach. 161 void dirtyAppendKeyValue(KEY key, VALUE value){ 162 map[key] = data.length; 163 data ~= DataContainer(key, value); 164 } 165 166 167 168 private: 169 DataContainer[] data; 170 size_t[KEY] map; 171 172 invariant{ 173 foreach(ref k, ref v ; map){ 174 assert(v<data.length && data[v].key == k, "Corrupted OrderedAA"); 175 } 176 } 177 } 178 179 unittest{ 180 OrderedAA!(string, string) orderedAA; 181 orderedAA["Hello"] = "World"; 182 orderedAA["Foo"] = "Bar"; 183 orderedAA["Yolo"] = "Yay"; 184 185 size_t i = 0; 186 foreach(string k, string v ; orderedAA){ 187 switch(i++){ 188 case 0: assert(k=="Hello"); assert(v=="World"); break; 189 case 1: assert(k=="Foo"); assert(v=="Bar"); break; 190 case 2: assert(k=="Yolo"); assert(v=="Yay"); break; 191 default: assert(0); 192 } 193 } 194 195 assert(*("Hello" in orderedAA)=="World"); 196 assert(("nothing" in orderedAA) is null); 197 198 auto orderedAA2 = orderedAA.dup; 199 assert(orderedAA2 == orderedAA); 200 201 orderedAA2["Foo"] = "42"; 202 assert(orderedAA["Foo"] == "Bar"); 203 assert(orderedAA2 != orderedAA); 204 205 orderedAA2.remove("Foo"); 206 orderedAA2["Heya"] = "It's me Imoen"; 207 foreach(idx, kv ; orderedAA2.byKeyValue){ 208 switch(idx){ 209 case 0: assert(kv.key=="Hello"); assert(kv.value=="World"); break; 210 case 1: assert(kv.key=="Yolo"); assert(kv.value=="Yay"); break; 211 case 2: assert(kv.key=="Heya"); assert(kv.value=="It's me Imoen");break; 212 default: assert(0); 213 } 214 } 215 orderedAA2.remove("Heya"); 216 foreach(idx, kv ; orderedAA2.byKeyValue){ 217 switch(idx){ 218 case 0: assert(kv.key=="Hello"); assert(kv.value=="World"); break; 219 case 1: assert(kv.key=="Yolo"); assert(kv.value=="Yay"); break; 220 default: assert(0); 221 } 222 } 223 224 225 OrderedAA!(string, int) aa; 226 foreach(ref int v ; aa){} 227 foreach(ref int v ; aa){} 228 foreach(string k, int v ; aa){} 229 foreach(ref string k, ref int v ; aa){} 230 }