Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
ChunkedList.cs
1using System;
4using System.Diagnostics;
5using System.Text;
6using System.Threading.Tasks;
7
9{
18 public delegate bool ForEachChunkCallback<T>(T[] Chunk, int Offset, int Count);
19
28 public delegate Task<bool> ForEachChunkAsyncCallback<T>(T[] Chunk, int Offset, int Count);
29
36 public delegate Task<bool> PredicateAsync<in T>(T Arg);
37
45 public delegate bool UpdateCallback<T>(ref T Value, out bool Keep);
46
51 [DebuggerDisplay("Count = {Count}")]
52 [DebuggerTypeProxy(typeof(ChunkedListDebugView<>))]
53 public class ChunkedList<T> : ICollection<T>, IList<T>
54 {
55 private const int initialChunkSize = 16;
56
57 private readonly int maxChunkSize;
58 private readonly int minChunkSize;
59 private Chunk firstChunk;
60 private Chunk lastChunk;
61 private int chunkSize;
62 private int count;
63 private bool? nullable;
64
68 public int Count => this.count;
69
73 public bool IsReadOnly => false;
74
78 public ChunkedList()
79 : this(initialChunkSize, int.MaxValue)
80 {
81 }
82
87 public ChunkedList(int InitialChunkSize)
88 : this(InitialChunkSize, InitialChunkSize)
89 {
90 }
91
97 public ChunkedList(int InitialChunkSize, int MaxChunkSize)
98 {
99 if (InitialChunkSize <= 0)
100 throw new ArgumentException("Chunk size must be positive.", nameof(InitialChunkSize));
101
102 if (MaxChunkSize < InitialChunkSize)
103 throw new ArgumentException("Max chunk size must be greater than or equal to initial chunk size.", nameof(MaxChunkSize));
104
105 this.chunkSize = InitialChunkSize;
106 this.maxChunkSize = MaxChunkSize;
107 this.minChunkSize = InitialChunkSize;
108 this.count = 0;
109
110 this.firstChunk = this.lastChunk = new Chunk(InitialChunkSize);
111
112 this.chunkSize <<= 1;
113 if (this.chunkSize > this.maxChunkSize || this.chunkSize <= 0)
114 this.chunkSize = this.maxChunkSize;
115 }
116
122 public ChunkedList(params T[] InitialElements)
123 : this(InitialElements, int.MaxValue)
124 {
125 }
126
133 public ChunkedList(T[] InitialElements, int MaxChunkSize)
134 {
135 if (InitialElements is null)
136 throw new ArgumentNullException(nameof(InitialElements));
137
138 Chunk Chunk;
139
140 this.chunkSize = this.count = InitialElements.Length;
141
142 if (this.chunkSize == 0)
143 {
144 this.chunkSize = initialChunkSize;
145 Chunk = new Chunk(this.chunkSize);
146 }
147 else
148 Chunk = new Chunk(InitialElements);
149
150 if (MaxChunkSize < this.chunkSize)
151 throw new ArgumentException("Max chunk size must be greater than or equal to initial chunk size.", nameof(MaxChunkSize));
152
153 this.maxChunkSize = MaxChunkSize;
154 this.minChunkSize = this.chunkSize;
155
156 this.firstChunk = this.lastChunk = Chunk;
157
158 this.chunkSize <<= 1;
159 if (this.chunkSize > this.maxChunkSize || this.chunkSize <= 0)
160 this.chunkSize = this.maxChunkSize;
161 }
162
163 internal class Chunk
164 {
165 public ChunkNode<T> node;
166 public T[] Elements;
167 public Chunk Next;
168 public Chunk Prev;
169 public int Size;
170 public int Start;
171 public int Pos;
172
173 public Chunk(int Size)
174 {
175 this.Elements = new T[Size];
176 this.Size = Size;
177 this.Start = 0;
178 this.Pos = 0;
179 this.Next = null;
180 this.Prev = null;
181 }
182
183 public Chunk(int Size, Chunk Previous)
184 : this(Size)
185 {
186 this.Prev = Previous;
187 Previous.Next = this;
188 }
189
190 public Chunk(T[] Elements)
191 {
192 this.Elements = Elements;
193 this.Size = this.Pos = this.Elements.Length;
194 this.Start = 0;
195 this.Next = null;
196 this.Prev = null;
197 }
198
199 public override string ToString()
200 {
201 StringBuilder sb = new StringBuilder();
202
203 sb.Append("Size: ");
204 sb.Append(this.Size);
205 sb.Append(", Start: ");
206 sb.Append(this.Start);
207 sb.Append(", Pos: ");
208 sb.Append(this.Pos);
209 sb.Append(", Count: ");
210 sb.Append(this.Pos - this.Start);
211 sb.Append(", HasNext: ");
212 sb.Append(!(this.Next is null));
213 sb.Append(", HasPrev: ");
214 sb.Append(!(this.Prev is null));
215
216 return sb.ToString();
217 }
218
219 public ChunkNode<T> Node
220 {
221 get
222 {
223 if (this.node is null)
224 this.node = new ChunkNode<T>(this);
225
226 return this.node;
227 }
228 }
229
230 public T this[int Index]
231 {
232 get
233 {
234 if (Index < this.Start || Index >= this.Pos)
235 throw new IndexOutOfRangeException();
236
237 return this.Elements[Index];
238 }
239
240 set
241 {
242 if (Index < this.Start || Index >= this.Pos)
243 throw new IndexOutOfRangeException();
244
245 this.Elements[Index] = value;
246 }
247 }
248 }
249
251 public override string ToString()
252 {
253 return "Count = " + this.count.ToString();
254 }
255
259 public ChunkNode<T> FirstChunk => this.firstChunk.Node;
260
264 public ChunkNode<T> LastChunk => this.lastChunk.Node;
265
266 #region ICollection<T>
267
272 public void Add(T Item)
273 {
274 if (this.lastChunk.Pos < this.lastChunk.Size)
275 this.lastChunk.Elements[this.lastChunk.Pos++] = Item;
276 else if (this.lastChunk.Start > 0)
277 {
278 if (this.lastChunk.Start < this.lastChunk.Pos)
279 {
280 Array.Copy(this.lastChunk.Elements, this.lastChunk.Start,
281 this.lastChunk.Elements, 0, this.lastChunk.Pos - this.lastChunk.Start);
282 }
283
284 this.lastChunk.Pos -= this.lastChunk.Start;
285 this.lastChunk.Start = 0;
286
287 this.lastChunk.Elements[this.lastChunk.Pos++] = Item;
288 }
289 else
290 {
291 this.lastChunk = new Chunk(this.chunkSize, this.lastChunk);
292
293 this.chunkSize <<= 1;
294 if (this.chunkSize > this.maxChunkSize || this.chunkSize <= 0)
295 this.chunkSize = this.maxChunkSize;
296
297 this.lastChunk.Elements[this.lastChunk.Pos++] = Item;
298 }
299
300 this.count++;
301 }
302
306 public void Clear()
307 {
308 this.lastChunk = this.firstChunk;
309
310 this.firstChunk.Next = null;
311 this.firstChunk.Start = 0;
312 this.firstChunk.Pos = 0;
313 this.chunkSize = this.firstChunk.Size;
314
315 Array.Clear(this.firstChunk.Elements, 0, this.firstChunk.Size);
316 this.count = 0;
317 }
318
324 public bool Contains(T Item)
325 {
326 Chunk Loop = this.firstChunk;
327
328 while (!(Loop is null))
329 {
330 if (Loop.Start < Loop.Pos &&
331 Array.IndexOf(Loop.Elements, Item, Loop.Start, Loop.Pos - Loop.Start) >= 0)
332 {
333 return true;
334 }
335
336 Loop = Loop.Next;
337 }
338
339 return false;
340 }
341
347 public void CopyTo(T[] Destination, int DestinationIndex)
348 {
349 this.CopyTo(0, Destination, DestinationIndex, this.count);
350 }
351
357 public bool Remove(T Item)
358 {
359 Chunk Loop = this.firstChunk;
360 int i, c;
361
362 while (!(Loop is null))
363 {
364 c = Loop.Pos - Loop.Start;
365 if (c > 0)
366 {
367 i = Array.IndexOf(Loop.Elements, Item, Loop.Start, c);
368 if (i >= 0)
369 {
370 if (i == Loop.Start)
371 Loop.Start++;
372 else
373 {
374 c = --Loop.Pos;
375 if (i < c)
376 Array.Copy(Loop.Elements, i + 1, Loop.Elements, i, c - i);
377 }
378
379 this.count--;
380
381 if (Loop.Start == Loop.Pos)
382 {
383 Loop.Start = 0;
384 Loop.Pos = 0;
385
386 if (Loop.Prev is null)
387 this.firstChunk = Loop.Next ?? Loop;
388 else
389 Loop.Prev.Next = Loop.Next;
390
391 if (Loop.Next is null)
392 this.lastChunk = Loop.Prev ?? Loop;
393 else
394 Loop.Next.Prev = Loop.Prev;
395
396 Loop.Next = null;
397 Loop.Prev = null;
398
399 this.chunkSize >>= 1;
400 if (this.chunkSize < this.minChunkSize)
401 this.chunkSize = this.minChunkSize;
402 }
403
404 return true;
405 }
406 }
407
408 Loop = Loop.Next;
409 }
410
411 return false;
412 }
413
418 public IEnumerator<T> GetEnumerator()
419 {
420 // More efficient than yield.
421 // (But ForEach is more efficient that using an enumerator.)
422 return new ChunkedListEnumerator(this.firstChunk);
423 }
424
429 IEnumerator IEnumerable.GetEnumerator()
430 {
431 return this.GetEnumerator();
432 }
433
434 private class ChunkedListEnumerator : IEnumerator<T>
435 {
436 private Chunk first;
437 private Chunk current;
438 private int pos;
439 private bool ended;
440
441 public ChunkedListEnumerator(Chunk FirstChunk)
442 {
443 this.first = FirstChunk;
444 this.current = null;
445 this.pos = -1;
446 this.ended = false;
447 }
448
449 public T Current
450 {
451 get
452 {
453 if (this.current is null)
454 throw new InvalidOperationException("Enumeration must be started or reset.");
455 else
456 return this.current.Elements[this.pos];
457 }
458 }
459
460 object IEnumerator.Current => this.Current;
461
462 public void Dispose()
463 {
464 this.first = null;
465 this.current = null;
466 this.ended = true;
467 }
468
469 public bool MoveNext()
470 {
471 if (this.current is null)
472 {
473 if (this.ended)
474 return false;
475
476 this.current = this.first;
477 this.pos = this.current.Start - 1;
478 }
479
480 while (++this.pos >= this.current.Pos)
481 {
482 this.current = this.current.Next;
483 if (this.current is null)
484 {
485 this.ended = true;
486 return false;
487 }
488
489 this.pos = this.current.Start - 1;
490 }
491
492 return true;
493 }
494
495 public void Reset()
496 {
497 this.current = null;
498 this.pos = -1;
499 this.ended = false;
500 }
501 }
502
503 #endregion
504
505 #region ForEach Optimization
506
514 public bool ForEach(Predicate<T> Callback)
515 {
516 if (Callback is null)
517 throw new ArgumentNullException(nameof(Callback));
518
519 Chunk Loop = this.firstChunk;
520 int i, c;
521
522 while (!(Loop is null))
523 {
524 for (i = Loop.Start, c = Loop.Pos; i < c; i++)
525 {
526 if (!Callback(Loop.Elements[i]))
527 return false;
528 }
529
530 Loop = Loop.Next;
531 }
532
533 return true;
534 }
535
543 public async Task<bool> ForEachAsync(PredicateAsync<T> Callback)
544 {
545 if (Callback is null)
546 throw new ArgumentNullException(nameof(Callback));
547
548 Chunk Loop = this.firstChunk;
549 int i, c;
550
551 while (!(Loop is null))
552 {
553 for (i = Loop.Start, c = Loop.Pos; i < c; i++)
554 {
555 if (!await Callback(Loop.Elements[i]))
556 return false;
557 }
558
559 Loop = Loop.Next;
560 }
561
562 return true;
563 }
564
573 {
574 if (Callback is null)
575 throw new ArgumentNullException(nameof(Callback));
576
577 Chunk Loop = this.firstChunk;
578 int i, c;
579
580 while (!(Loop is null))
581 {
582 i = Loop.Start;
583 c = Loop.Pos - i;
584 if (c > 0)
585 {
586 if (!Callback(Loop.Elements, i, c))
587 return false;
588 }
589
590 Loop = Loop.Next;
591 }
592
593 return true;
594 }
595
603 public async Task<bool> ForEachChunkAsync(ForEachChunkAsyncCallback<T> Callback)
604 {
605 if (Callback is null)
606 throw new ArgumentNullException(nameof(Callback));
607
608 Chunk Loop = this.firstChunk;
609 int i, c;
610
611 while (!(Loop is null))
612 {
613 i = Loop.Start;
614 c = Loop.Pos - i;
615 if (c > 0)
616 {
617 if (!await Callback(Loop.Elements, i, c))
618 return false;
619 }
620
621 Loop = Loop.Next;
622 }
623
624 return true;
625 }
626
627 #endregion
628
629 #region Update Optimization
630
640 public bool Update(UpdateCallback<T> Callback)
641 {
642 if (Callback is null)
643 throw new ArgumentNullException(nameof(Callback));
644
645 Chunk Loop = this.firstChunk;
646 Chunk Next;
647 int i, c, d;
648 bool Continue = true;
649 bool Deleted = false;
650
651 while (!(Loop is null))
652 {
653 for (i = Loop.Start, c = Loop.Pos; i < c; i++)
654 {
655 Continue = Callback(ref Loop.Elements[i], out bool Keep);
656
657 if (!Keep)
658 {
659 if (i == Loop.Start)
660 Loop.Start++;
661 else
662 {
663 d = c - i - 1;
664 if (d > 0)
665 Array.Copy(Loop.Elements, i + 1, Loop.Elements, i, d);
666
667 Loop.Pos--;
668 i--;
669 c--;
670 }
671
672 this.count--;
673 Deleted = true;
674 }
675
676 if (!Continue)
677 break;
678 }
679
680 Next = Loop.Next;
681
682 if (Deleted)
683 {
684 Deleted = false;
685
686 if (Loop.Start == Loop.Pos)
687 {
688 Loop.Start = 0;
689 Loop.Pos = 0;
690
691 if (Loop.Prev is null)
692 this.firstChunk = Next ?? Loop;
693 else
694 Loop.Prev.Next = Next;
695
696 if (Next is null)
697 this.lastChunk = Loop.Prev ?? Loop;
698 else
699 Next.Prev = Loop.Prev;
700
701 Loop.Next = null;
702 Loop.Prev = null;
703
704 this.chunkSize >>= 1;
705 if (this.chunkSize < this.minChunkSize)
706 this.chunkSize = this.minChunkSize;
707 }
708 }
709
710 Loop = Next;
711
712 if (!Continue)
713 return false;
714 }
715
716 return true;
717 }
718
719 #endregion
720
721 #region Members corresponding to LinkedList<T> interface.
722
726 public bool HasLastItem => !(this.lastChunk is null) && this.lastChunk.Pos > this.lastChunk.Start;
727
731 public T LastItem
732 {
733 get
734 {
735 if (this.lastChunk is null || this.lastChunk.Pos == this.lastChunk.Start)
736 {
737 if (!this.nullable.HasValue)
738 this.nullable = typeof(T).IsClass;
739
740 if (this.nullable.Value)
741 return default;
742 else
743 throw new InvalidOperationException("No last item in the collection.");
744 }
745 else
746 return this.lastChunk.Elements[this.lastChunk.Pos - 1];
747 }
748
749 set
750 {
751 if (this.lastChunk is null)
752 {
753 this.firstChunk = this.lastChunk = new Chunk(this.chunkSize);
754
755 this.chunkSize <<= 1;
756 if (this.chunkSize > this.maxChunkSize || this.chunkSize <= 0)
757 this.chunkSize = this.maxChunkSize;
758
759 this.lastChunk.Pos = 1;
760 this.lastChunk.Elements[0] = value;
761 this.count++;
762 }
763 else if (this.lastChunk.Pos == this.lastChunk.Start)
764 {
765 this.lastChunk.Start = 0;
766 this.lastChunk.Pos = 1;
767 this.lastChunk.Elements[0] = value;
768 this.count++;
769 }
770 else
771 this.lastChunk.Elements[this.lastChunk.Pos - 1] = value;
772 }
773 }
774
778 public bool HasFirstItem => !(this.firstChunk is null) && this.firstChunk.Pos > this.firstChunk.Start;
779
783 public T FirstItem
784 {
785 get
786 {
787 if (this.firstChunk is null || this.firstChunk.Pos == this.firstChunk.Start)
788 {
789 if (!this.nullable.HasValue)
790 this.nullable = typeof(T).IsClass;
791
792 if (this.nullable.Value)
793 return default;
794 else
795 throw new InvalidOperationException("No first item in the collection.");
796 }
797 else
798 return this.firstChunk.Elements[this.firstChunk.Start];
799 }
800
801 set
802 {
803 if (this.firstChunk is null)
804 {
805 this.firstChunk = this.lastChunk = new Chunk(this.chunkSize);
806
807 this.chunkSize <<= 1;
808 if (this.chunkSize > this.maxChunkSize || this.chunkSize <= 0)
809 this.chunkSize = this.maxChunkSize;
810
811 this.firstChunk.Pos = 1;
812 this.firstChunk.Elements[0] = value;
813 this.count++;
814 }
815 else if (this.firstChunk.Pos == this.firstChunk.Start)
816 {
817 this.firstChunk.Start = 0;
818 this.firstChunk.Pos = 1;
819 this.firstChunk.Elements[0] = value;
820 this.count++;
821 }
822 else
823 this.firstChunk.Elements[this.firstChunk.Start] = value;
824 }
825 }
826
832 public void AddFirstItem(T Value)
833 {
834 if (this.firstChunk.Start > 0)
835 this.firstChunk.Elements[--this.firstChunk.Start] = Value;
836 else if (this.firstChunk.Pos == 0)
837 {
838 this.firstChunk.Start = this.firstChunk.Pos = this.firstChunk.Size;
839 this.firstChunk.Elements[--this.firstChunk.Start] = Value;
840 }
841 else
842 {
843 Chunk NewChunk = new Chunk(this.chunkSize)
844 {
845 Next = this.firstChunk,
846 };
847
848 this.chunkSize <<= 1;
849 if (this.chunkSize > this.maxChunkSize || this.chunkSize <= 0)
850 this.chunkSize = this.maxChunkSize;
851
852 this.firstChunk.Prev = NewChunk;
853 this.firstChunk = NewChunk;
854
855 this.firstChunk.Start = this.firstChunk.Pos = this.firstChunk.Size;
856 this.firstChunk.Elements[--this.firstChunk.Start] = Value;
857 }
858
859 this.count++;
860 }
861
867 public void AddLastItem(T Value)
868 {
869 this.Add(Value);
870 }
871
876 public T RemoveFirst()
877 {
878 if (this.firstChunk.Start < this.firstChunk.Pos)
879 {
880 T Result = this.firstChunk.Elements[this.firstChunk.Start++];
881 this.count--;
882
883 if (this.firstChunk.Start == this.firstChunk.Pos)
884 {
885 this.firstChunk.Start = 0;
886 this.firstChunk.Pos = 0;
887
888 if (!(this.firstChunk.Next is null))
889 {
890 Chunk Temp = this.firstChunk;
891 this.firstChunk = this.firstChunk.Next;
892 this.firstChunk.Prev = null;
893 Temp.Next = null;
894
895 this.chunkSize >>= 1;
896 if (this.chunkSize < this.minChunkSize)
897 this.chunkSize = this.minChunkSize;
898 }
899 }
900
901 return Result;
902 }
903 else
904 throw new InvalidOperationException("No first item to remove.");
905 }
906
911 public T RemoveLast()
912 {
913 if (this.lastChunk.Pos > this.lastChunk.Start)
914 {
915 T Result = this.lastChunk.Elements[--this.lastChunk.Pos];
916 this.count--;
917
918 if (this.lastChunk.Pos == this.lastChunk.Start)
919 {
920 this.lastChunk.Start = 0;
921 this.lastChunk.Pos = 0;
922
923 if (!(this.lastChunk.Prev is null))
924 {
925 Chunk Temp = this.lastChunk;
926 this.lastChunk = this.lastChunk.Prev;
927 this.lastChunk.Next = null;
928 Temp.Prev = null;
929
930 this.chunkSize >>= 1;
931 if (this.chunkSize < this.minChunkSize)
932 this.chunkSize = this.minChunkSize;
933 }
934 }
935
936 return Result;
937 }
938 else
939 throw new InvalidOperationException("No last item to remove.");
940 }
941
942 #endregion
943
944 #region IList<T>
945
952 public T this[int Index]
953 {
954 get
955 {
956 if (Index < 0 || Index >= this.count)
957 throw new ArgumentOutOfRangeException("Index out of bounds.", nameof(Index));
958
959 Chunk Loop = this.firstChunk;
960 int c;
961
962 while (!(Loop is null))
963 {
964 c = Loop.Pos - Loop.Start;
965
966 if (Index < c)
967 return Loop.Elements[Loop.Start + Index];
968 else
969 Index -= c;
970
971 Loop = Loop.Next;
972 }
973
974 throw new ArgumentOutOfRangeException("Index out of bounds.", nameof(Index));
975 }
976
977 set
978 {
979 if (Index < 0 || Index >= this.count)
980 throw new ArgumentOutOfRangeException("Index out of bounds.", nameof(Index));
981
982 Chunk Loop = this.firstChunk;
983 int c;
984
985 while (!(Loop is null))
986 {
987 c = Loop.Pos - Loop.Start;
988
989 if (Index < c)
990 {
991 Loop.Elements[Loop.Start + Index] = value;
992 return;
993 }
994 else
995 Index -= c;
996
997 Loop = Loop.Next;
998 }
999
1000 throw new ArgumentOutOfRangeException("Index out of bounds.", nameof(Index));
1001 }
1002 }
1003
1009 public int IndexOf(T Item)
1010 {
1011 return this.IndexOf(Item, 0, this.count);
1012 }
1013
1020 public int IndexOf(T Item, int Index)
1021 {
1022 return this.IndexOf(Item, Index, this.count - Index);
1023 }
1024
1032 public int IndexOf(T Item, int Index, int Count)
1033 {
1034 if (Index < 0 || Index > this.count)
1035 throw new ArgumentOutOfRangeException("Index out of bounds.", nameof(Index));
1036
1037 if (Count < 0 || Count > this.count - Index)
1038 throw new ArgumentOutOfRangeException("Count out of bounds.", nameof(Count));
1039
1040 Chunk Loop = this.firstChunk;
1041 int i, c, c0, Result = 0;
1042
1043 while (!(Loop is null) && Count > 0)
1044 {
1045 c = c0 = Loop.Pos - Loop.Start;
1046
1047 if (c > 0)
1048 {
1049 if (Index >= c)
1050 Index -= c;
1051 else
1052 {
1053 c -= Index;
1054
1055 if (Count < c)
1056 c = Count;
1057
1058 if ((i = Array.IndexOf(Loop.Elements, Item, Loop.Start + Index, c)) >= 0)
1059 return Result + i - Loop.Start;
1060
1061 Count -= c;
1062 Index = 0;
1063 }
1064
1065 Result += c0;
1066 }
1067
1068 Loop = Loop.Next;
1069 }
1070
1071 return -1;
1072 }
1073
1079 public int LastIndexOf(T Item)
1080 {
1081 if (this.count == 0)
1082 return -1;
1083
1084 return this.LastIndexOf(Item, this.count - 1, this.count);
1085 }
1086
1093 public int LastIndexOf(T Item, int Index)
1094 {
1095 if (this.count == 0)
1096 {
1097 if (Index == -1)
1098 return -1;
1099
1100 throw new ArgumentOutOfRangeException("Index out of bounds.", nameof(Index));
1101 }
1102
1103 return this.LastIndexOf(Item, Index, Math.Min(this.count, Index + 1));
1104 }
1105
1113 public int LastIndexOf(T Item, int Index, int Count)
1114 {
1115 if (this.count == 0)
1116 {
1117 if (Index == -1 && Count == 0)
1118 return -1;
1119
1120 if (Index < 0 || Index >= this.count)
1121 throw new ArgumentOutOfRangeException("Index out of bounds.", nameof(Index));
1122
1123 throw new ArgumentOutOfRangeException("Count out of bounds.", nameof(Count));
1124 }
1125
1126 if (Index < 0 || Index >= this.count)
1127 throw new ArgumentOutOfRangeException("Index out of bounds.", nameof(Index));
1128
1129 if (Count < 0 || Count > Index + 1)
1130 throw new ArgumentOutOfRangeException("Count out of bounds.", nameof(Count));
1131
1132 if (Count == 0)
1133 return -1;
1134
1135 Chunk Loop = this.firstChunk;
1136 int i = 0, j = 0, c;
1137
1138 while (!(Loop is null) && Index > 0)
1139 {
1140 c = Loop.Pos - Loop.Start;
1141
1142 if (c <= Index)
1143 {
1144 Index -= c;
1145 j += c;
1146 }
1147 else
1148 {
1149 i = Index;
1150 break;
1151 }
1152
1153 Loop = Loop.Next;
1154 }
1155
1156 if (Loop is null)
1157 {
1158 Loop = this.lastChunk;
1159 i = Loop.Pos - Loop.Start;
1160 }
1161
1162 j += Loop.Pos - Loop.Start;
1163
1164 while (!(Loop is null) && Count > 0)
1165 {
1166 c = Loop.Pos - Loop.Start;
1167 j -= c;
1168
1169 if (c > 0)
1170 {
1171 if (Count < c)
1172 c = Count;
1173
1174 if (i < 0)
1175 i = Array.LastIndexOf(Loop.Elements, Item, Loop.Pos - 1, c);
1176 else
1177 {
1178 if (c > i + 1)
1179 c = i + 1;
1180
1181 i = Array.LastIndexOf(Loop.Elements, Item, Loop.Start + i, c);
1182 }
1183
1184 if (i >= 0)
1185 return j + i - Loop.Start;
1186
1187 Count -= c;
1188 }
1189
1190 Loop = Loop.Prev;
1191 }
1192
1193 return -1;
1194 }
1195
1196 #endregion
1197
1198 #region Members corresponding to List<T> interface.
1199
1205 public void Insert(int Index, T Item)
1206 {
1207 if (Index < 0 || Index > this.count)
1208 throw new ArgumentOutOfRangeException("Index out of bounds.", nameof(Index));
1209
1210 if (Index == this.count)
1211 {
1212 this.Add(Item);
1213 return;
1214 }
1215
1216 Chunk Loop = this.firstChunk;
1217 int c;
1218
1219 while (!(Loop is null))
1220 {
1221 c = Loop.Pos - Loop.Start;
1222
1223 if (Index < c)
1224 {
1225 if (Loop.Start > 0)
1226 {
1227 if (Index > 0)
1228 Array.Copy(Loop.Elements, Loop.Start, Loop.Elements, Loop.Start - 1, Index);
1229
1230 Loop.Elements[--Loop.Start + Index] = Item;
1231 }
1232 else if (Loop.Pos < Loop.Size)
1233 {
1234 if (Index < Loop.Pos)
1235 Array.Copy(Loop.Elements, Index, Loop.Elements, Index + 1, Loop.Pos - Index);
1236
1237 Loop.Elements[Index] = Item;
1238 Loop.Pos++;
1239 }
1240 else
1241 {
1242 Chunk Temp = Loop.Next;
1243 Chunk NewChunk = new Chunk(Loop.Size, Loop)
1244 {
1245 Next = Temp
1246 };
1247
1248 if (!(Temp is null))
1249 Temp.Prev = NewChunk;
1250 else
1251 this.lastChunk = NewChunk;
1252
1253 c = Loop.Size >> 1;
1254
1255 if (c == 0) // Chunk of size 1; cannot be split.
1256 {
1257 NewChunk.Elements[0] = Loop.Elements[0];
1258 NewChunk.Pos = 1;
1259
1260 Loop.Elements[0] = Item;
1261 Loop.Pos = 1;
1262 }
1263 else if (Index < c)
1264 {
1265 NewChunk.Pos = Loop.Size - c;
1266 Array.Copy(Loop.Elements, c, NewChunk.Elements, 0, NewChunk.Pos);
1267 Array.Copy(Loop.Elements, Index, Loop.Elements, Index + 1, c - Index);
1268 Loop.Elements[Index] = Item;
1269 Loop.Pos = c + 1;
1270 }
1271 else
1272 {
1273 Loop.Pos = c;
1274 Index -= c;
1275 NewChunk.Pos = Loop.Size - c + 1;
1276
1277 if (Index > 0)
1278 {
1279 Array.Copy(Loop.Elements, c, NewChunk.Elements, 0, Index);
1280 c += Index;
1281 }
1282
1283 NewChunk.Elements[Index++] = Item;
1284
1285 if (Loop.Size > c)
1286 Array.Copy(Loop.Elements, c, NewChunk.Elements, Index, Loop.Size - c);
1287 }
1288 }
1289
1290 this.count++;
1291 return;
1292 }
1293 else
1294 Index -= c;
1295
1296 Loop = Loop.Next;
1297 }
1298
1299 throw new ArgumentOutOfRangeException("Index out of bounds.", nameof(Index));
1300 }
1301
1306 public void RemoveAt(int Index)
1307 {
1308 if (Index < 0 || Index >= this.count)
1309 throw new ArgumentOutOfRangeException("Index out of bounds.", nameof(Index));
1310
1311 Chunk Loop = this.firstChunk;
1312 int c;
1313
1314 while (!(Loop is null))
1315 {
1316 c = Loop.Pos - Loop.Start;
1317
1318 if (Index < c)
1319 {
1320 if (Index == 0)
1321 Loop.Start++;
1322 else
1323 {
1324 Index += Loop.Start;
1325 c = --Loop.Pos;
1326
1327 if (Index < c)
1328 Array.Copy(Loop.Elements, Index + 1, Loop.Elements, Index, c - Index);
1329 }
1330
1331 this.count--;
1332
1333 if (Loop.Start == Loop.Pos)
1334 {
1335 Loop.Start = 0;
1336 Loop.Pos = 0;
1337
1338 if (Loop.Prev is null)
1339 this.firstChunk = Loop.Next ?? Loop;
1340 else
1341 Loop.Prev.Next = Loop.Next;
1342
1343 if (Loop.Next is null)
1344 this.lastChunk = Loop.Prev ?? Loop;
1345 else
1346 Loop.Next.Prev = Loop.Prev;
1347
1348 Loop.Next = null;
1349 Loop.Prev = null;
1350
1351 this.chunkSize >>= 1;
1352 if (this.chunkSize < this.minChunkSize)
1353 this.chunkSize = this.minChunkSize;
1354 }
1355
1356 return;
1357 }
1358 else
1359 Index -= c;
1360
1361 Loop = Loop.Next;
1362 }
1363
1364 throw new ArgumentOutOfRangeException("Index out of bounds.", nameof(Index));
1365 }
1366
1371 public void AddRange(IEnumerable<T> Collection)
1372 {
1373 if (Collection is null)
1374 throw new ArgumentNullException(nameof(Collection));
1375 else if (ReferenceEquals(Collection, this))
1376 this.AddRange(this.ToArray());
1377 else if (Collection is T[] A)
1378 this.AddRange(A);
1379 else if (Collection is ChunkedList<T> B)
1380 this.AddRange(B);
1381 else if (Collection is ICollection<T> C)
1382 {
1383 A = new T[C.Count];
1384 C.CopyTo(A, 0);
1385 this.AddRange(A);
1386 }
1387 else
1388 {
1389 ChunkedList<T> Temp = new ChunkedList<T>();
1390 foreach (T Item in Collection)
1391 Temp.Add(Item);
1392
1393 this.AddRange(Temp.ToArray());
1394 }
1395 }
1396
1401 public void AddRange(ChunkedList<T> Collection)
1402 {
1403 if (Collection is null)
1404 throw new ArgumentNullException(nameof(Collection));
1405
1406 if (ReferenceEquals(Collection, this))
1407 this.AddRange(this.ToArray());
1408 else
1409 {
1410 Chunk Loop = Collection.firstChunk;
1411
1412 while (!(Loop is null))
1413 {
1414 this.AddRange(Loop.Elements, Loop.Start, Loop.Pos - Loop.Start);
1415 Loop = Loop.Next;
1416 }
1417 }
1418 }
1419
1424 public void AddRange(T[] Collection)
1425 {
1426 if (Collection is null)
1427 throw new ArgumentNullException(nameof(Collection));
1428
1429 this.AddRange(Collection, 0, Collection.Length);
1430 }
1431
1438 public void AddRange(T[] Collection, int Index, int Count)
1439 {
1440 if (Collection is null)
1441 throw new ArgumentNullException(nameof(Collection));
1442
1443 if (Index < 0 || Index > Collection.Length)
1444 throw new ArgumentOutOfRangeException(nameof(Index));
1445
1446 if (Count < 0 || Count > Collection.Length - Index)
1447 throw new ArgumentOutOfRangeException(nameof(Count));
1448
1449 int End = Index + Count;
1450
1451 if (this.lastChunk.Start > 0)
1452 {
1453 if (this.lastChunk.Start < this.lastChunk.Pos)
1454 {
1455 Array.Copy(this.lastChunk.Elements, this.lastChunk.Start,
1456 this.lastChunk.Elements, 0, this.lastChunk.Pos - this.lastChunk.Start);
1457 }
1458
1459 this.lastChunk.Pos -= this.lastChunk.Start;
1460 this.lastChunk.Start = 0;
1461 }
1462
1463 while (Index < End)
1464 {
1465 if (this.lastChunk.Pos >= this.lastChunk.Size)
1466 {
1467 this.lastChunk = new Chunk(this.chunkSize, this.lastChunk);
1468
1469 this.chunkSize <<= 1;
1470 if (this.chunkSize > this.maxChunkSize || this.chunkSize <= 0)
1471 this.chunkSize = this.maxChunkSize;
1472 }
1473
1474 int d = Math.Min(this.lastChunk.Size - this.lastChunk.Pos, End - Index);
1475
1476 Array.Copy(Collection, Index, this.lastChunk.Elements, this.lastChunk.Pos, d);
1477
1478 Index += d;
1479 this.count += d;
1480 this.lastChunk.Pos += d;
1481 }
1482 }
1483
1488 public void CopyTo(T[] Destination)
1489 {
1490 this.CopyTo(0, Destination, 0, this.count);
1491 }
1492
1500 public void CopyTo(int Index, T[] Destination, int DestinationIndex, int Count)
1501 {
1502 if (Index < 0 || Index > this.count)
1503 throw new ArgumentOutOfRangeException(nameof(Index));
1504
1505 if (Count < 0 || Count > this.count - Index)
1506 throw new ArgumentOutOfRangeException(nameof(Count));
1507
1508 if (Destination is null)
1509 throw new ArgumentNullException(nameof(Destination));
1510
1511 if (DestinationIndex < 0 || DestinationIndex > Destination.Length)
1512 throw new ArgumentOutOfRangeException(nameof(DestinationIndex));
1513
1514 if (Count > Destination.Length - DestinationIndex)
1515 throw new ArgumentException("Destination array is too small.", nameof(Destination));
1516
1517 Chunk Loop = this.firstChunk;
1518 int i, c;
1519 bool Offset = Index > 0;
1520
1521 while (!(Loop is null) && Count > 0)
1522 {
1523 i = Loop.Start;
1524 if ((c = Loop.Pos - i) > 0)
1525 {
1526 if (Offset)
1527 {
1528 if (Index >= c)
1529 {
1530 Index -= c;
1531 if (Index == 0)
1532 Offset = false;
1533 }
1534 else
1535 {
1536 c -= Index;
1537 i += Index;
1538 Offset = false;
1539
1540 if (Count < c)
1541 c = Count;
1542
1543 Array.Copy(Loop.Elements, i, Destination, DestinationIndex, c);
1544 DestinationIndex += c;
1545 Count -= c;
1546 }
1547 }
1548 else
1549 {
1550 if (Count < c)
1551 c = Count;
1552
1553 Array.Copy(Loop.Elements, i, Destination, DestinationIndex, c);
1554 DestinationIndex += c;
1555 Count -= c;
1556 }
1557 }
1558
1559 Loop = Loop.Next;
1560 }
1561 }
1562
1567 public T[] ToArray()
1568 {
1569 T[] Result = new T[this.count];
1570 Chunk Loop = this.firstChunk;
1571 int i = 0, c;
1572
1573 while (!(Loop is null))
1574 {
1575 if ((c = Loop.Pos - Loop.Start) > 0)
1576 {
1577 Array.Copy(Loop.Elements, Loop.Start, Result, i, c);
1578 i += c;
1579 }
1580
1581 Loop = Loop.Next;
1582 }
1583
1584 return Result;
1585 }
1586
1587 private void MakeOneChunk(bool Trimmed)
1588 {
1589 if (!(this.firstChunk.Next is null) ||
1590 Trimmed && (this.firstChunk.Start > 0 ||
1591 this.firstChunk.Pos < this.firstChunk.Size))
1592 {
1593 Chunk Chunk;
1594
1595 if (this.count == 0)
1596 Chunk = new Chunk(this.chunkSize);
1597 else
1598 Chunk = new Chunk(this.ToArray());
1599
1600 this.firstChunk = this.lastChunk = Chunk;
1601 }
1602 }
1603
1607 public void Sort()
1608 {
1609 if (this.count <= 1)
1610 return;
1611
1612 // TODO: Can be optimized
1613 this.MakeOneChunk(false);
1614 Array.Sort(this.firstChunk.Elements, this.firstChunk.Start,
1615 this.firstChunk.Pos - this.firstChunk.Start);
1616 }
1617
1622 public void Sort(IComparer<T> Comparer)
1623 {
1624 if (this.count <= 1)
1625 return;
1626
1627 // TODO: Can be optimized
1628 this.MakeOneChunk(false);
1629 Array.Sort(this.firstChunk.Elements, this.firstChunk.Start,
1630 this.firstChunk.Pos - this.firstChunk.Start, Comparer);
1631 }
1632
1637 public void Sort(Comparison<T> Comparison)
1638 {
1639 if (Comparison is null)
1640 throw new ArgumentNullException(nameof(Comparison));
1641
1642 if (this.count <= 1)
1643 return;
1644
1645 // TODO: Can be optimized
1646 this.MakeOneChunk(true);
1647 Array.Sort(this.firstChunk.Elements, Comparison);
1648 }
1649
1656 public void Sort(int Index, int Count, IComparer<T> Comparer)
1657 {
1658 if (Index < 0 || Index > this.count)
1659 throw new ArgumentOutOfRangeException(nameof(Index));
1660
1661 if (Count < 0 || Count > this.count - Index)
1662 throw new ArgumentOutOfRangeException(nameof(Count));
1663
1664 if (Count <= 1)
1665 return;
1666
1667 // TODO: Can be optimized
1668 this.MakeOneChunk(false);
1669 Array.Sort(this.firstChunk.Elements, this.firstChunk.Start + Index,
1670 Count, Comparer);
1671 }
1672
1676 public void Reverse()
1677 {
1678 // TODO: Can be optimized
1679 this.MakeOneChunk(false);
1680 Array.Reverse(this.firstChunk.Elements, this.firstChunk.Start,
1681 this.firstChunk.Pos - this.firstChunk.Start);
1682 }
1683
1689 public void Reverse(int Index, int Count)
1690 {
1691 if (Index < 0 || Index > this.count)
1692 throw new ArgumentOutOfRangeException(nameof(Index));
1693
1694 if (Count < 0 || Count > this.count - Index)
1695 throw new ArgumentOutOfRangeException(nameof(Count));
1696
1697 if (Count <= 1)
1698 return;
1699
1700 // TODO: Can be optimized
1701 this.MakeOneChunk(false);
1702 Array.Reverse(this.firstChunk.Elements, this.firstChunk.Start + Index, Count);
1703 }
1704
1705 #endregion
1706
1707 #region AddRangeFirst/AddRangeLast
1708
1713 public void AddRangeLast(IEnumerable<T> Collection)
1714 {
1715 this.AddRange(Collection);
1716 }
1717
1722 public void AddRangeLast(T[] Collection)
1723 {
1724 this.AddRange(Collection);
1725 }
1726
1731 public void AddRangeFirst(IEnumerable<T> Collection)
1732 {
1733 if (Collection is null)
1734 throw new ArgumentNullException(nameof(Collection));
1735
1736 if (Collection is T[] A)
1737 this.AddRangeFirst(A);
1738 else if (Collection is ICollection<T> C)
1739 {
1740 A = new T[C.Count];
1741 C.CopyTo(A, 0);
1742 this.AddRangeFirst(A);
1743 }
1744 else
1745 {
1746 ChunkedList<T> Temp = new ChunkedList<T>();
1747
1748 foreach (T Item in Collection)
1749 Temp.AddLastItem(Item);
1750
1751 this.AddRangeFirst(Temp.ToArray());
1752 }
1753 }
1754
1759 public void AddRangeFirst(T[] Collection)
1760 {
1761 if (Collection is null)
1762 throw new ArgumentNullException(nameof(Collection));
1763
1764 int c = Collection.Length;
1765 int d;
1766
1767 if ((d = this.firstChunk.Size - this.firstChunk.Pos) > 0)
1768 {
1769 Array.Copy(this.firstChunk.Elements, 0, this.firstChunk.Elements, d, this.firstChunk.Pos);
1770 this.firstChunk.Start += d;
1771 this.firstChunk.Pos = this.firstChunk.Size;
1772 }
1773
1774 if (this.firstChunk.Start > 0)
1775 {
1776 d = Math.Min(this.firstChunk.Start, c);
1777
1778 Array.Copy(Collection, c - d, this.firstChunk.Elements, this.firstChunk.Start - d, d);
1779 c -= d;
1780 this.firstChunk.Start -= d;
1781 this.count += d;
1782 }
1783
1784 while (c > 0)
1785 {
1786 Chunk Temp = this.firstChunk;
1787 this.firstChunk = new Chunk(this.chunkSize);
1788 Temp.Prev = this.firstChunk;
1789 this.firstChunk.Next = Temp;
1790
1791 this.chunkSize <<= 1;
1792 if (this.chunkSize > this.maxChunkSize || this.chunkSize <= 0)
1793 this.chunkSize = this.maxChunkSize;
1794
1795 this.firstChunk.Start = this.firstChunk.Pos = this.firstChunk.Size;
1796
1797 d = Math.Min(this.firstChunk.Start, c);
1798
1799 Array.Copy(Collection, c - d, this.firstChunk.Elements, this.firstChunk.Start - d, d);
1800 c -= d;
1801 this.firstChunk.Start -= d;
1802 this.count += d;
1803 }
1804 }
1805
1806 #endregion
1807
1808 #region InsertRange
1809
1815 public void InsertRange(int Index, IEnumerable<T> Collection)
1816 {
1817 if (Collection is null)
1818 throw new ArgumentNullException(nameof(Collection));
1819
1820 if (Index < 0 || Index > this.count)
1821 throw new ArgumentOutOfRangeException("Index out of bounds.", nameof(Index));
1822
1823 // TODO: Can be optimized
1824
1825 if (!(Collection is T[] A))
1826 {
1827 if (ReferenceEquals(Collection, this))
1828 A = this.ToArray();
1829 else if (Collection is ChunkedList<T> B)
1830 A = B.ToArray();
1831 else if (Collection is ICollection<T> C)
1832 {
1833 A = new T[C.Count];
1834 C.CopyTo(A, 0);
1835 }
1836 else
1837 {
1838 ChunkedList<T> Temp = new ChunkedList<T>();
1839 foreach (T Item in Collection)
1840 Temp.Add(Item);
1841
1842 A = Temp.ToArray();
1843 }
1844 }
1845
1846 foreach (T Item in A)
1847 this.Insert(Index++, Item);
1848 }
1849
1850 #endregion
1851 }
1852}
Node referencing a chunk in a ChunkedList<T>
Definition: ChunkNode.cs:11
A chunked list is a linked list of chunks of objects of type T .
Definition: ChunkedList.cs:54
void Clear()
Clears the collection.
Definition: ChunkedList.cs:306
ChunkedList(int InitialChunkSize, int MaxChunkSize)
A chunked list is a linked list of chunks of objects of type T .
Definition: ChunkedList.cs:97
IEnumerator< T > GetEnumerator()
Returns an enumerator for the collection.
Definition: ChunkedList.cs:418
int IndexOf(T Item, int Index)
Determines the index of a specific item
void AddFirstItem(T Value)
Adds a new item first in the collection.
Definition: ChunkedList.cs:832
void AddRangeFirst(T[] Collection)
Adds a range of elements first to the list.
async Task< bool > ForEachChunkAsync(ForEachChunkAsyncCallback< T > Callback)
Iterates through all chunks of elements in the collection, and calls the callback method in Callback ...
Definition: ChunkedList.cs:603
bool Remove(T Item)
Removes an element from the collection.
Definition: ChunkedList.cs:357
void AddRange(T[] Collection)
Adds a range of elements (last) to the list.
T LastItem
Last item in the collection.
Definition: ChunkedList.cs:732
void AddRange(IEnumerable< T > Collection)
Adds a range of elements (last) to the list.
void Reverse()
Reverses the order of the elements in the collection.
ChunkNode< T > FirstChunk
First chunk
Definition: ChunkedList.cs:259
void Sort()
Sorts the collection.
void RemoveAt(int Index)
Removes the item at the specified index.
void Sort(Comparison< T > Comparison)
Sorts the collection.
ChunkedList(T[] InitialElements, int MaxChunkSize)
A chunked list is a linked list of chunks of objects of type T , initially filled with elements in In...
Definition: ChunkedList.cs:133
bool Contains(T Item)
Checks if an item is a member of the collection.
Definition: ChunkedList.cs:324
bool ForEach(Predicate< T > Callback)
Iterates through all elements in the collection, and calls the callback method in Callback for each ...
Definition: ChunkedList.cs:514
void CopyTo(T[] Destination, int DestinationIndex)
Copies the contents of the collection to an array.
Definition: ChunkedList.cs:347
void AddRange(T[] Collection, int Index, int Count)
Adds a range of elements (last) to the list.
int LastIndexOf(T Item)
Determines the last index of a specific item
bool HasLastItem
If there is a last item in the collection
Definition: ChunkedList.cs:726
bool IsReadOnly
If collection is read-only (false).
Definition: ChunkedList.cs:73
int IndexOf(T Item)
Determines the index of a specific item
ChunkedList()
A chunked list is a linked list of chunks of objects of type T .
Definition: ChunkedList.cs:78
bool Update(UpdateCallback< T > Callback)
Iterates through all elements in the collection, and calls the callback method in Callback for each ...
Definition: ChunkedList.cs:640
void CopyTo(T[] Destination)
Copies the contents of the collection to an array.
int IndexOf(T Item, int Index, int Count)
Determines the index of a specific item
void Insert(int Index, T Item)
Inserts an item to the list at the specified index.
int Count
Number of elements in collection.
Definition: ChunkedList.cs:68
ChunkNode< T > LastChunk
Last chunk
Definition: ChunkedList.cs:264
int LastIndexOf(T Item, int Index, int Count)
Determines the last index of a specific item
ChunkedList(int InitialChunkSize)
A chunked list is a linked list of chunks of objects of type T .
Definition: ChunkedList.cs:87
bool ForEachChunk(ForEachChunkCallback< T > Callback)
Iterates through all chunks of elements in the collection, and calls the callback method in Callback ...
Definition: ChunkedList.cs:572
int LastIndexOf(T Item, int Index)
Determines the last index of a specific item
void AddRangeLast(IEnumerable< T > Collection)
Adds a range of elements last to the list.
void InsertRange(int Index, IEnumerable< T > Collection)
Inserts a range of elements at a given index.
void AddRangeFirst(IEnumerable< T > Collection)
Adds a range of elements first to the list.
void CopyTo(int Index, T[] Destination, int DestinationIndex, int Count)
Copies the contents of the collection to an array.
void Reverse(int Index, int Count)
Reverses the order of a subset of elements in the collection.
T RemoveFirst()
Removes the first item in the collection.
Definition: ChunkedList.cs:876
T RemoveLast()
Removes the last item in the collection.
Definition: ChunkedList.cs:911
void Add(T Item)
Adds an item to the collection.
Definition: ChunkedList.cs:272
void AddLastItem(T Value)
Adds a new item last in the collection.
Definition: ChunkedList.cs:867
void AddRange(ChunkedList< T > Collection)
Adds a range of elements (last) to the list.
T[] ToArray()
Returns an array containing all elements of the collection.
void Sort(int Index, int Count, IComparer< T > Comparer)
Sorts a part of the collection.
void Sort(IComparer< T > Comparer)
Sorts the collection.
async Task< bool > ForEachAsync(PredicateAsync< T > Callback)
Iterates through all elements in the collection, and calls the callback method in Callback for each ...
Definition: ChunkedList.cs:543
ChunkedList(params T[] InitialElements)
A chunked list is a linked list of chunks of objects of type T , initially filled with elements in In...
Definition: ChunkedList.cs:122
void AddRangeLast(T[] Collection)
Adds a range of elements last to the list.
delegate Task< bool > ForEachChunkAsyncCallback< T >(T[] Chunk, int Offset, int Count)
Asynchronous callback method for the ForEachChunkAsync method.
delegate Task< bool > PredicateAsync< in T >(T Arg)
Asynchronous predicate function with one argument.
delegate bool UpdateCallback< T >(ref T Value, out bool Keep)
Delegate for callback methods that update the elements of a ChunkedList<T>.
delegate bool ForEachChunkCallback< T >(T[] Chunk, int Offset, int Count)
Callback method for the ForEachChunk method.