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