1 /* 2 * This file is part of gir-to-d. 3 * 4 * gir-to-d is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License 6 * as published by the Free Software Foundation, either version 3 7 * of the License, or (at your option) any later version. 8 * 9 * gir-to-d is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public License 15 * along with gir-to-d. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 module gtd.LinkedHasMap; 19 20 import core.memory; 21 22 /** 23 * An hash map that allows iteration in the insertion-order. 24 */ 25 struct LinkedHashMap(Key, Value) 26 { 27 static struct Node 28 { 29 Value val; 30 Key key; 31 Node* next; 32 Node* previous; 33 } 34 35 private Node*[Key] data; 36 private Node* front; 37 private Node* back; 38 39 /** 40 * Looks up key, if it exsists returns the corresponding value 41 * else evaluates and returns defaultValue. 42 */ 43 inout(Value) get(Key key, lazy inout(Value) defaultValue = Value.init) inout pure @safe 44 { 45 if ( key !in data ) 46 return defaultValue; 47 48 return data[key].val; 49 } 50 51 /** 52 * remove(key) does nothing if the given key does not exist and returns false. 53 * If the given key does exist, it removes it from the HashMap and returns true. 54 */ 55 bool remove(Key key) pure nothrow @trusted 56 { 57 Node** nodeP = key in data; 58 59 if ( nodeP is null ) 60 return false; 61 62 Node* node = *nodeP; 63 64 if ( node is front ) 65 front = node.next; 66 if ( node is back ) 67 back = node.previous; 68 69 if ( node.previous ) 70 node.previous.next = node.next; 71 if ( node.next ) 72 node.next.previous = node.previous; 73 74 data.remove(key); 75 GC.free(node); 76 77 return true; 78 } 79 80 /** 81 * Removes all contents from the LinkedHashMap 82 */ 83 void clear() pure nothrow @trusted 84 { 85 Node* node = front; 86 Node* previous; 87 88 while ( node !is null ) 89 { 90 previous = node; 91 node = node.next; 92 93 GC.free(previous); 94 } 95 96 data.destroy(); 97 front = null; 98 back = null; 99 } 100 101 /** 102 * Returns: the number of values in the LinkedHasMap. 103 */ 104 @property size_t length() nothrow pure const @trusted 105 { 106 return data.length; 107 } 108 109 /** 110 * Returns: true if the LinkedHasmap has no elements. 111 */ 112 @property bool empty() nothrow pure const @safe 113 { 114 return front is null; 115 } 116 117 /** 118 * Indexing operators yield or modify the value at a specified index. 119 */ 120 inout(Value) opIndex(Key key) inout pure @safe 121 { 122 return data[key].val; 123 } 124 125 /// ditto 126 void opIndexAssign(Value value, Key key) @safe 127 { 128 Node* node; 129 130 if ( key !in data ) 131 { 132 node = new Node; 133 } 134 else 135 { 136 node = data[key]; 137 node.val = value; 138 return; 139 } 140 141 node.val = value; 142 node.key = key; 143 144 data[key] = node; 145 146 if ( front is null ) 147 { 148 front = node; 149 back = node; 150 return; 151 } 152 153 node.previous = back; 154 back.next = node; 155 back = node; 156 } 157 158 /// ditto 159 Value opIndexUnary(string op)(Key key) @safe 160 { 161 Node* node = data[key]; 162 return mixin(op ~"node.val;"); 163 } 164 165 /// ditto 166 Value opIndexOpAssign(string op)(Value v, Key key) @safe 167 { 168 Node* node = data[key]; 169 return mixin("node.val" ~ op ~ "=v"); 170 } 171 172 /** 173 * in operator. Check to see if the given element exists in the container. 174 */ 175 inout(Value)* opBinaryRight(string op)(Key key) inout nothrow pure @safe 176 if (op == "in") 177 { 178 inout(Node*)* node = key in data; 179 180 if ( node is null ) 181 return null; 182 183 return &((*node).val); 184 } 185 186 /** 187 * foreach iteration uses opApply. 188 */ 189 int opApply(scope int delegate(ref Value) dg) 190 { 191 Node* node = front; 192 193 while ( node !is null ) 194 { 195 int result = dg(node.val); 196 if ( result ) 197 return result; 198 199 node = node.next; 200 } 201 202 return 0; 203 } 204 205 /// ditto 206 int opApply(scope int delegate(ref Key, ref Value) dg) 207 { 208 Node* node = front; 209 210 while ( node !is null ) 211 { 212 int result = dg(node.key, node.val); 213 if ( result ) 214 return result; 215 216 node = node.next; 217 } 218 219 return 0; 220 } 221 222 /** 223 * Returns: true if this and that are equal. 224 */ 225 bool opEquals(inout typeof(this) that) inout nothrow pure @safe 226 { 227 return data == that.data; 228 } 229 230 /** 231 * Returns: An dynamic array, the elements of which are the keys in the LinkedHashmap. 232 */ 233 @property inout(Key)[] keys() inout @safe 234 { 235 inout(Key)[] k; 236 237 inout(Node)* node = front; 238 239 while ( node !is null ) 240 { 241 k ~= node.key; 242 node = node.next; 243 } 244 245 return k; 246 } 247 248 /** 249 * Returns: An dynamic array, the elements of which are the values in the LinkedHashmap. 250 */ 251 @property inout(Value)[] values() inout @safe 252 { 253 inout(Value)[] v; 254 255 inout(Node)* node = front; 256 257 while ( node !is null ) 258 { 259 v ~= node.val; 260 node = node.next; 261 } 262 263 return v; 264 } 265 266 /** 267 * Reorganizes the LinkedHashMap in place so that lookups are more efficient, 268 * rehash is effective when, for example, the program is done loading up 269 * a symbol table and now needs fast lookups in it. 270 */ 271 void rehash() @trusted 272 { 273 data = data.rehash(); 274 } 275 276 /** 277 * Create a new LinkedHashMap of the same size and copy the contents of the LinkedHashMap into it. 278 */ 279 LinkedHashMap!(Key, Value) dub() @safe 280 { 281 LinkedHashMap!(Key, Value) copy; 282 Node* node = front; 283 284 while ( node !is null ) 285 { 286 copy[node.key] = node.val; 287 node = node.next; 288 } 289 290 return copy; 291 } 292 293 /** 294 * Returns a delegate suitable for use as a ForeachAggregate to 295 * a ForeachStatement which will iterate over the keys of the LinkedHashMap. 296 */ 297 @property auto byKey() pure nothrow @safe 298 { 299 static struct KeyRange 300 { 301 Node* node; 302 303 this(Node* node) pure nothrow @safe 304 { 305 this.node = node; 306 } 307 308 @property Key front() pure nothrow @safe 309 { 310 return node.key; 311 } 312 313 void popFront() pure nothrow @safe 314 { 315 node = node.next; 316 } 317 318 @property bool empty() pure const nothrow @safe 319 { 320 return node is null; 321 } 322 } 323 324 return KeyRange(front); 325 } 326 327 /** 328 * Returns a delegate suitable for use as a ForeachAggregate to 329 * a ForeachStatement which will iterate over the values of the LinkedHashMap. 330 */ 331 @property auto byValue() pure nothrow @safe 332 { 333 static struct ValueRange 334 { 335 Node* node; 336 337 this(Node* node) pure nothrow @safe 338 { 339 this.node = node; 340 } 341 342 @property Value front() pure nothrow @safe 343 { 344 return node.val; 345 } 346 347 void popFront() pure nothrow @safe 348 { 349 node = node.next; 350 } 351 352 @property bool empty() pure const nothrow @safe 353 { 354 return node is null; 355 } 356 } 357 358 return ValueRange(front); 359 } 360 }