1 // Written by Christopher E. Miller 2 // See the included license.txt for copyright and license details. 3 4 5 // Not actually part of forms, but is handy. 6 7 8 module dfl.collections; 9 10 private import dfl.internal.dlib; 11 12 private import dfl.base; 13 14 15 void _blankListCallback(TValue)(size_t idx, TValue val) { // package 16 } 17 18 19 // Mixin. 20 // Item*Callback called before modifications. 21 // For clear(), index is size_t.max and value is null. If CLEAR_EACH, also called back for each value. 22 template ListWrapArray(TValue, alias Array, 23 /+ // DMD 1.005: basic type expected, not function 24 alias ItemAddingCallback = function(size_t idx, TValue val) {}, 25 alias ItemAddedCallback = function(size_t idx, TValue val) {}, 26 alias ItemRemovingCallback = function(size_t idx, TValue val) {}, 27 alias ItemRemovedCallback = function(size_t idx, TValue val) {}, 28 +/ 29 alias ItemAddingCallback, 30 alias ItemAddedCallback, 31 alias ItemRemovingCallback, 32 alias ItemRemovedCallback, 33 bool OVERLOAD_STRING = false, 34 bool OVERLOAD_OBJECT = false, 35 bool COW = true, 36 bool CLEAR_EACH = false) { // package 37 mixin OpApplyWrapArray!(TValue, Array); // Note: this overrides COW. 38 39 40 static if(OVERLOAD_OBJECT) { 41 static assert(!is(TValue == Object)); 42 } 43 44 static if(OVERLOAD_STRING) { 45 static assert(!is(TValue == Dstring)); 46 47 static if(is(TValue == Object)) { 48 alias StringObject TValueString; 49 } else { 50 alias TValue TValueString; 51 } 52 } 53 54 55 56 void opIndexAssign(TValue value, int index) { 57 TValue oldval = Array[index]; 58 ItemRemovingCallback(index, oldval); // Removing. 59 static if(COW) { 60 Array = Array.dup; 61 } else { 62 //Array[index] = TValue.init; 63 } 64 ItemRemovedCallback(index, oldval); // Removed. 65 66 ItemAddingCallback(index, value); // Adding. 67 Array[index] = value; 68 ItemAddedCallback(index, value); // Added. 69 } 70 71 static if(OVERLOAD_OBJECT) { 72 /// ditto 73 void opIndexAssign(Object value, int index) { 74 TValue tval; 75 tval = cast(TValue)value; 76 if(tval) { 77 return opIndexAssign(tval, index); 78 } else { 79 return opIndexAssign(new TValue(value), index); // ? 80 } 81 } 82 } 83 84 static if(OVERLOAD_STRING) { 85 /// ditto 86 void opIndexAssign(Dstring value, int index) { 87 return opIndexAssign(new TValueString(value), index); 88 } 89 } 90 91 92 93 @property TValue opIndex(int index) { // getter 94 return Array[index]; 95 } 96 97 98 99 void add(TValue value) { 100 _insert(cast(int)Array.length, value); 101 } 102 103 static if(OVERLOAD_OBJECT) { 104 /// ditto 105 void add(Object value) { 106 _insert(cast(int)Array.length, value); 107 } 108 } 109 110 static if(OVERLOAD_STRING) { 111 /// ditto 112 void add(Dstring value) { 113 _insert(cast(int)Array.length, new TValueString(value)); 114 } 115 } 116 117 118 119 void clear() { 120 ItemRemovingCallback(size_t.max, null); // Removing ALL. 121 122 size_t iw; 123 iw = Array.length; 124 if(iw) { 125 static if(CLEAR_EACH) { 126 try { 127 // Remove in reverse order so the indices don't keep shifting. 128 TValue oldval; 129 for(--iw;; iw--) { 130 oldval = Array[iw]; 131 static if(CLEAR_EACH) { 132 ItemRemovingCallback(iw, oldval); // Removing. 133 } 134 /+static if(COW) { 135 } else { 136 //Array[iw] = TValue.init; 137 }+/ 138 debug { 139 Array = Array[0 .. iw]; // 'Temporarily' removes it for ItemRemovedCallback. 140 } 141 static if(CLEAR_EACH) { 142 ItemRemovedCallback(iw, oldval); // Removed. 143 } 144 if(!iw) { 145 break; 146 } 147 } 148 } 149 finally { 150 Array = Array[0 .. iw]; 151 static if(COW) { 152 if(!iw) { 153 Array = null; 154 } 155 } 156 } 157 } else { 158 Array = Array[0 .. 0]; 159 static if(COW) { 160 Array = null; 161 } 162 } 163 } 164 165 ItemRemovedCallback(size_t.max, null); // Removed ALL. 166 } 167 168 169 170 bool contains(TValue value) { 171 return -1 != findIsIndex!(TValue)(Array, value); 172 } 173 174 static if(OVERLOAD_OBJECT) { 175 /// ditto 176 bool contains(Object value) { 177 return -1 != indexOf(value); 178 } 179 } 180 181 static if(OVERLOAD_STRING) { 182 /// ditto 183 bool contains(Dstring value) { 184 return -1 != indexOf(value); 185 } 186 } 187 188 189 190 int indexOf(TValue value) { 191 return findIsIndex!(TValue)(Array, value); 192 } 193 194 static if(OVERLOAD_OBJECT) { 195 /// ditto 196 int indexOf(Object value) { 197 TValue tval; 198 tval = cast(TValue)value; 199 if(tval) { 200 return indexOf(tval); 201 } else { 202 foreach(size_t idx, TValue onval; Array) { 203 if(onval == value) { // TValue must have opEquals. 204 return idx; 205 } 206 } 207 return -1; 208 } 209 } 210 } 211 212 static if(OVERLOAD_STRING) { 213 /// ditto 214 int indexOf(Dstring value) { 215 foreach(size_t idx, TValue onval; Array) { 216 static if(is(TValue == TValueString)) { 217 if(onval == value) { // TValue must have opEquals. 218 return idx; 219 } 220 } else { 221 if(getObjectString(onval) == value) { 222 return idx; 223 } 224 } 225 } 226 return -1; 227 } 228 } 229 230 231 private final void _insert(int index, TValue value) { 232 if(index > Array.length) { 233 index = Array.length; 234 } 235 ItemAddingCallback(index, value); // Adding. 236 static if(COW) { 237 if(index >= Array.length) { 238 if(Array.length) { // Workaround old bug ? 239 Array = Array[0 .. index] ~ (&value)[0 .. 1]; 240 } else { 241 Array = (&value)[0 .. 1].dup; 242 } 243 goto insert_done; 244 } 245 } else { 246 if(index >= Array.length) { 247 Array ~= value; 248 goto insert_done; 249 } 250 } 251 Array = Array[0 .. index] ~ (&value)[0 .. 1] ~ Array[index .. Array.length]; 252 insert_done: 253 ItemAddedCallback(index, value); // Added. 254 } 255 256 static if(OVERLOAD_OBJECT) { 257 private final void _insert(int index, Object value) { 258 TValue tval; 259 tval = cast(TValue)value; 260 if(tval) { 261 return _insert(index, tval); 262 } else { 263 return _insert(index, new TValue(value)); // ? 264 } 265 } 266 } 267 268 static if(OVERLOAD_STRING) { 269 /// ditto 270 private final void _insert(int index, Dstring value) { 271 return _insert(index, new TValueString(value)); 272 } 273 } 274 275 276 277 void insert(int index, TValue value) { 278 _insert(index, value); 279 } 280 281 static if(OVERLOAD_OBJECT) { 282 /// ditto 283 void insert(int index, Object value) { 284 _insert(index, value); 285 } 286 } 287 288 static if(OVERLOAD_STRING) { 289 /// ditto 290 void insert(int index, Dstring value) { 291 return _insert(index, value); 292 } 293 } 294 295 296 297 void remove(TValue value) { 298 int index; 299 index = findIsIndex!(TValue)(Array, value); 300 if(-1 != index) { 301 removeAt(index); 302 } 303 } 304 305 static if(OVERLOAD_OBJECT) { 306 /// ditto 307 void remove(Object value) { 308 TValue tval; 309 tval = cast(TValue)value; 310 if(tval) { 311 return remove(tval); 312 } else { 313 int i; 314 i = indexOf(value); 315 if(-1 != i) { 316 removeAt(i); 317 } 318 } 319 } 320 } 321 322 static if(OVERLOAD_STRING) { 323 /// ditto 324 void remove(Dstring value) { 325 int i; 326 i = indexOf(value); 327 if(-1 != i) { 328 removeAt(i); 329 } 330 } 331 } 332 333 334 335 void removeAt(int index) { 336 TValue oldval = Array[index]; 337 ItemRemovingCallback(index, oldval); // Removing. 338 if(!index) { 339 Array = Array[1 .. Array.length]; 340 } else if(index == Array.length - 1) { 341 Array = Array[0 .. index]; 342 } else if(index > 0 && index < cast(int)Array.length) { 343 Array = Array[0 .. index] ~ Array[index + 1 .. Array.length]; 344 } 345 ItemRemovedCallback(index, oldval); // Removed. 346 } 347 348 349 deprecated alias length count; 350 351 352 @property size_t length() { // getter 353 return Array.length; 354 } 355 356 357 deprecated alias dup clone; 358 359 360 TValue[] dup() { 361 return Array.dup; 362 } 363 364 365 366 void copyTo(TValue[] dest, int destIndex) { 367 dest[destIndex .. destIndex + Array.length] = Array[]; 368 } 369 370 371 372 void addRange(TValue[] values) { 373 foreach(TValue value; values) { 374 add(value); 375 } 376 } 377 378 static if(OVERLOAD_OBJECT) { 379 /// ditto 380 void addRange(Object[] values) { 381 foreach(Object value; values) { 382 add(value); 383 } 384 } 385 } 386 387 static if(OVERLOAD_STRING) { 388 /// ditto 389 void addRange(Dstring[] values) { 390 foreach(Dstring value; values) { 391 add(value); 392 } 393 } 394 } 395 } 396 397 398 // Mixin. 399 template OpApplyAddIndex(alias ApplyFunc, TValue, bool ADD_APPLY_FUNC = false) { // package 400 401 int opApply(int delegate(ref size_t, ref TValue val) dg) { 402 size_t idx = 0; 403 return ApplyFunc( 404 (ref TValue val) { 405 int result; 406 result = dg(idx, val); 407 idx++; 408 return result; 409 }); 410 } 411 412 413 static if(ADD_APPLY_FUNC) { 414 /// ditto 415 int opApply(int delegate(ref TValue val) dg) { 416 return ApplyFunc(dg); 417 } 418 } 419 } 420 421 422 // Mixin. 423 template OpApplyWrapArray(TValue, alias Array) { // package 424 425 int opApply(int delegate(ref TValue val) dg) { 426 int result = 0; 427 foreach(ref TValue val; Array) { 428 result = dg(val); 429 if(result) { 430 break; 431 } 432 } 433 return result; 434 } 435 436 /// ditto 437 int opApply(int delegate(ref size_t, ref TValue val) dg) { 438 int result = 0; 439 foreach(size_t idx, ref TValue val; Array) { 440 result = dg(idx, val); 441 if(result) { 442 break; 443 } 444 } 445 return result; 446 } 447 } 448 449 450 template removeIndex(T) { // package 451 T[] removeIndex(T[] array, size_t index) { 452 if(!index) { 453 array = array[1 .. array.length]; 454 } else if(index == array.length - 1) { 455 array = array[0 .. index]; 456 } else { 457 array = array[0 .. index] ~ array[index + 1 .. array.length]; 458 } 459 return array; 460 } 461 } 462 463 464 // Returns -1 if not found. 465 template findIsIndex(T) { // package 466 int findIsIndex(T[] array, T obj) { 467 int idx; 468 for(idx = 0; idx != array.length; idx++) { 469 if(obj is array[idx]) { 470 return idx; 471 } 472 } 473 return -1; 474 } 475 } 476