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