1 {
2  *****************************************************************************
3   This file is part of LazUtils.
4 
5   See the file COPYING.modifiedLGPL.txt, included in this distribution,
6   for details about the license.
7  *****************************************************************************
8 
9  Initial Revision  : May 2015
10 
11  This unit provides generics for list classes. The lists are provided as
12  "object" and "class" version.
13  Each list can be specialized to either hold items of a given type (specialized
14  to type at compile time) or to hold untyped data the size of with can be
15  specified at runtime as argument to the constructor.
16 
17  *** The lists are currently not suitable for managed types, such as string. ***
18 
19  ****************************************
20  * TLazShiftBufferList
21 
22    This list is designed for shift/unshift/pop/push operations.
23 
24    The first list element is not forced to the start of the allocated memory.
25    Instead it allows a gap (some of the over-allocated memory / List.Capacity)
26    in front of the first element.
27 
28    Therefore elements can be added/removed at either the begin or end of the
29    list, withouth any need to move the other elemnts in the list.
30 
31  ****************************************
32  * TLazRoundBufferList
33 
34    The first element of the list can be anywhere within the allocated memory
35    (capacity). If the elements of the list reach the end of the memory, the list
36    will wrap arount and continues in the available memory at the start of the
37    allocation.
38 
39    This list can be used for a first-in, first-out queue. If the list does never
40    exceed the size set by its capacity, then elements can be pushed/shifted
41    from/to the list without any need to reallocate or move entries to new
42    locations.
43 
44  ****************************************
45  * TLazPagedListMem
46 
47    This list organize its data into pages of fixed size. If the list grows or
48    shrinks it can allocate extra pages or free existing pages. It does not need
49    to reallocate its entire memory.
50    This means that growing needs less extra memory than conventional lists.
51    The page size is specified in the call to the constructor. The size has to be
52    a power of 2. The constructor takes the exponent as argument. (e.g. an
53    argument of "10" will give a size of 1024)
54 
55    The list also acts like a TLazShiftBufferList.
56 
57 
58  *****************************************************************************
59  * Variants of the above lists.
60 
61    Note about "object" variants:
62      "object"s are stored on the stack, as such the memory of the object itself
63      is freed when the variable goes out of scope. The objects do however
64      allocate additional memory on the heap.
65    => So it is necessary to call Destroy.
66    => It is also necessary to call Create. (Unless you can gurantee that the
67       memory of the object has been zero filled)
68 
69    The samples below show the available variants of the above list.
70    The constructor for each sample is included.
71 
72 
73  ****************************************
74  * Helpers for specializing the variants
75 
76    Either of the following can be specified to generics that take a "TSizeT"
77 
78    type
79    generic TLazListClassesItemSize<T> = object
80 
81      Used for specializing lists with a fixed item size.
82      The size will be:
83        sizeof(T)
84 
85    type
86    TLazListClassesVarItemSize = object
87 
88      Used for specializing list with a configurable item size. The size must
89      be set in the constructor and can not be changed after this.
90      When using this, you need to add a constructor setting:
91        fItemSize.ItemSize := ASize; // "ASize" is your size
92 
93  ****************************************
94  * Variants for TLazShiftBufferList
95 
96    generic TLazShiftBufferListObjBase<TPItemT, TSizeT> = object
97      procedure Create;
98 
99      This is the base for all other variants. Usually you do not use this
100      directly, but use one of the other variants below.
101      TSizeT: See above
102      TPItemT: The type of the item.
103               Can be "Pointer" or any type. Must match TSizeT
104 
105    TLazShiftBufferListObj = object
106      procedure Create(AnItemSize: Integer);
107 
108      The list as an "object"
109      Use the "ItemPointer" method to get a pointer to the item-data.
110      The pointer is only valid while there are no insert/delete/capacity operations.
111 
112    generic TLazShiftBufferListObjGen<T> = object
113      procedure Create;
114 
115      The list as a generic for a typed "object"
116      This list an "Items" method to access the list entries.
117 
118    TLazShiftBufferList = class
119      procedure Create(AnItemSize: Integer);
120 
121      The pointer-list as an "class"
122 
123    generic TLazShiftBufferListGen<T> = class
124      procedure Create;
125 
126      The typed-list as an "class"
127 
128  ****************************************
129  * Variants for TLazRoundBufferList
130 
131    generic TLazRoundBufferListObjBase<TPItemT, TSizeT> = object
132      procedure Create;
133 
134    TLazRoundBufferListObj = object
135      procedure Create(AnItemSize: Integer);
136 
137    generic TLazRoundBufferListObjGen<T> = object
138      procedure Create;
139 
140 
141  ****************************************
142  * Variants for TLazPagedListObj
143 
144    generic TLazPagedListObjBase<TPItemT, TSizeT> = object
145      procedure Create(APageSizeExp: Integer);
146      // pagesize := 2 ^^ APageSizeExp
147 
148    TLazPagedListObj = object
149     procedure Create(APageSizeExp: Integer; AnItemSize: Integer);
150 
151  ********************************************************************************
152  * Notes
153 
154  * MoveRows(From, To, Cnt)
155 
156    - Can handle overlaps
157    - The Data in the "from" block will be undefined afterwards
158      (except for overlaps with "To")
159 
160 }
161 
162 unit LazListClasses;
163 
164 {$mode objfpc}{$H+}
165 
166 interface
167 {$IFDEF LazListClassTestCase}
168   {$INLINE off}
169   {$STACKFRAMES on}
170   {$ASSERTIONS on}
171   {$IMPLICITEXCEPTIONS off} // use with debugln
172 {$ELSE}
173   {$INLINE on}
174   {$STACKFRAMES off}
175   {$ASSERTIONS off}
176 {$ENDIF}
177 
178 uses
179   Classes, SysUtils, math, LazLoggerBase;
180 
181 type
182 
Requirednull183   TLazStorageMemShrinkProc = function(ARequired: Integer): Integer of object;
Requirednull184   TLazStorageMemGrowProc = function(ARequired: Integer): Integer of object;
185 
186   (* TLazListClassesItemSize
187      Helper to specialize lists for a give type
188   *)
189   generic TLazListClassesItemSize<T> = object
190   protected
191     const
192     ItemSize = SizeOf(T);
193   end;
194 
195   (* TLazListClassesVarItemSize
196      Helper to specialize lists for runtime specified size "TList.Create(ASize)"
197   *)
198   TLazListClassesVarItemSize = object
199   protected
200     ItemSize: Integer;
201   end;
202 
203   { TLazListClassesInternalMem
204     Internally used helper object
205   }
206 
207   TLazListClassesInternalMem = object
208   protected type
209     TMemRecord = record
210       FirstItem: record
211         case integer of
212           1: (Ptr: PByte;);
213           2: (Idx: Integer;);
214         end;
215       Count: Integer;
216       Capacity: Cardinal;
217       Data: record end; // The address for the first byte of data. This is a dummy field
218     end;
219     PMemRecord = ^TMemRecord;
220   private
221     FMem: PMemRecord;
222 
GetCapacityFastnull223     function GetCapacityFast: Cardinal; inline;
GetDataPointernull224     function GetDataPointer: PByte; inline;
GetFirstItemIndexnull225     function GetFirstItemIndex: Integer; inline;
GetFirstItemPointernull226     function GetFirstItemPointer: PByte;  inline;
227     procedure SetCapacity(AValue: Cardinal); inline;
GetCapacitynull228     function GetCapacity: Cardinal; inline;
GetCountnull229     function GetCount: Integer; inline;
230     procedure SetCount(AValue: Integer); inline;
231     procedure SetFirstItemIndex(AValue: Integer);
232     procedure SetFirstItemPointer(AValue: PByte); inline;
233   protected
234     property CapacityFast: Cardinal read GetCapacityFast;
235   public
236     procedure Init; inline;
237     procedure Alloc(AByteSize: Integer); inline;
238     procedure Free; inline;
IsAllocatednull239     function IsAllocated: Boolean; inline;
240 
241     property Capacity: Cardinal read GetCapacity write SetCapacity;
242     property Count: Integer read GetCount write SetCount;
243     property DataPointer: PByte read GetDataPointer;
244     // Lists can use either FirstItemPointer or FirstItemIndex
245     // Only RoundBuffer uses FirstItemIndex
246     property FirstItemPointer: PByte read GetFirstItemPointer write SetFirstItemPointer;
247     property FirstItemIndex: Integer read GetFirstItemIndex write SetFirstItemIndex;
248   end;
249 
250   { TLazShiftBufferListObjBase }
251 
252   generic TLazShiftBufferListObjBase<TPItemT, TSizeT> = object
253   private
254     FMem: TLazListClassesInternalMem;
255     FItemSize: TSizeT; // May be zero size
256 
GetItemPointernull257     function GetItemPointer(Index: Integer): TPItemT; inline;
GetItemPointerFastnull258     function GetItemPointerFast(Index: Integer): TPItemT; inline;
259     procedure SetCapacity(AValue: Integer); inline;
GetCapacitynull260     function GetCapacity: Integer; inline;
GetCountnull261     function GetCount: Integer; inline;
262   protected
GrowCapacitynull263     function GrowCapacity(ARequired: Integer): Integer;
ShrinkCapacitynull264     function ShrinkCapacity(ARequired: Integer): Integer;
265 
SetCapacityExnull266     function SetCapacityEx(AValue, AnInsertPos, AnInsertSize: Integer): TPItemT;
InsertRowsExnull267     function InsertRowsEx(AIndex, ACount: Integer; AGrowProc: TLazStorageMemGrowProc): TPItemT;
268     procedure DeleteRowsEx(AIndex, ACount: Integer; AShrinkProc: TLazStorageMemShrinkProc);
269     property ItemPointerFast[Index: Integer]: TPItemT read GetItemPointerFast;
270   public
271     procedure Create;
272     procedure Destroy;
InsertRowsnull273     function InsertRows(AIndex, ACount: Integer): TPItemT; inline; // can be re-introduced, to change GrowProc
274     procedure DeleteRows(AIndex, ACount: Integer); inline; // can be re-introduced, to change ShrinkProc
275     procedure MoveRows(AFromIndex, AToIndex, ACount: Integer);
276     procedure SwapEntries(AIndex1, AIndex2: Integer); inline;
277     procedure DebugDump;
278 
279     property Capacity: Integer read GetCapacity write SetCapacity;
280     property Count: Integer read GetCount;
281     property ItemPointer[Index: Integer]: TPItemT read GetItemPointer;
282   end;
283 
284   { TLazShiftBufferListObj }
285 
286   TLazShiftBufferListObj = object(specialize TLazShiftBufferListObjBase<Pointer, TLazListClassesVarItemSize>)
287   public
288     procedure Create(AnItemSize: Integer);
289   end;
290 
291   { TLazShiftBufferListObjGen }
292 
293   generic TLazShiftBufferListObjGen<T> = object
294   private type
295     TItemSize = specialize TLazListClassesItemSize<T>;
296     PT = ^T;
297     TListType = specialize TLazShiftBufferListObjBase<PT, TItemSize>;
298   private
299     FList: TListType;
300   // forwarded methods
301   private
GetCapacitynull302     function GetCapacity: Integer; inline;
GetCountnull303     function GetCount: Integer; inline;
GetItemPointernull304     function GetItemPointer(Index: Integer): PT; inline;
GetItemPointerFastnull305     function GetItemPointerFast(Index: Integer): PT; inline;
306     procedure SetCapacity(AValue: Integer); inline;
307   protected
InsertRowsExnull308     function InsertRowsEx(AIndex, ACount: Integer; AGrowProc: TLazStorageMemGrowProc): PT; inline;
309     procedure DeleteRowsEx(AIndex, ACount: Integer; AShrinkProc: TLazStorageMemShrinkProc); inline;
310     property ItemPointerFast[Index: Integer]: PT read GetItemPointerFast;
311   public
312     procedure Create;
313     procedure Destroy;
InsertRowsnull314     function InsertRows(AIndex, ACount: Integer): PT; inline; // can be re-introduced, to change GrowProc
315     procedure DeleteRows(AIndex, ACount: Integer); inline; // can be re-introduced, to change ShrinkProc
316     procedure MoveRows(AFromIndex, AToIndex, ACount: Integer); inline;
317     procedure SwapEntries(AIndex1, AIndex2: Integer); inline;
318     procedure DebugDump;
319 
320     property Capacity: Integer read GetCapacity write SetCapacity;
321     property Count: Integer read GetCount;
322     property ItemPointer[Index: Integer]: PT read GetItemPointer;
323   // new extra methods
324   private
Getnull325     function Get(Index: Integer): T;
326     procedure Put(Index: Integer; AValue: T);
327   public
IndexOfnull328     function IndexOf(AnItem: T): integer;
329     property Items[Index: Integer]: T read Get write Put; default;
330   end;
331 
332   { TLazRoundBufferListObjBase }
333 
334   generic TLazRoundBufferListObjBase<TPItemT, TSizeT> = object
335   private
336     // Keep the size small, if no entries exist
337     // FMem:  FLowElemPointer: PByte; FCount, FCapacity_in_bytes: Integer; Array of <FItemSize
338     FMem: TLazListClassesInternalMem;
339     FItemSize: TSizeT; // May be zero size
340 
GetItemPointernull341     function GetItemPointer(Index: Integer): TPItemT; inline;
GetItemPointerFastnull342     function GetItemPointerFast(Index: Integer): TPItemT; inline;
343     procedure SetCapacity(AValue: Integer); inline;
GetCapacitynull344     function GetCapacity: Integer; inline;
GetCountnull345     function GetCount: Integer; inline;
346   protected
347     procedure InternalMoveUp(AFromEnd, AToEnd: PByte; AByteCnt, AByteCap: Integer); inline;
348     procedure InternalMoveDown(AFrom, ATo: PByte; AByteCnt: Integer; AUpperBound: PByte); inline;
GrowCapacitynull349     function GrowCapacity(ARequired: Integer): Integer;
ShrinkCapacitynull350     function ShrinkCapacity({%H-}ARequired: Integer): Integer;
351 
SetCapacityExnull352     function  SetCapacityEx(AValue, AnInsertPos, AnInsertSize: Integer): TPItemT;
InsertRowsExnull353     function  InsertRowsEx(AIndex, ACount: Integer; AGrowProc: TLazStorageMemGrowProc): TPItemT;
354     procedure DeleteRowsEx(AIndex, ACount: Integer; AShrinkProc: TLazStorageMemShrinkProc);
355     property ItemPointerFast[Index: Integer]: TPItemT read GetItemPointerFast;
356   public
357     procedure Create;
358     procedure Destroy;
InsertRowsnull359     function  InsertRows(AIndex, ACount: Integer): TPItemT; inline; // can be re-introduced, to change GrowProc
360     procedure DeleteRows(AIndex, ACount: Integer); inline; // can be re-introduced, to change ShrinkProc
361     procedure MoveRows(AFromIndex, AToIndex, ACount: Integer);
362     procedure SwapEntries(AIndex1, AIndex2: Integer); inline;
363     procedure DebugDump;
364 
365     property Capacity: Integer read GetCapacity write SetCapacity;
366     property Count: Integer read GetCount;
367     property ItemPointer[Index: Integer]: TPItemT read GetItemPointer;
368   end;
369 
370   { TLazRoundBufferListObj }
371 
372   TLazRoundBufferListObj = object(specialize TLazRoundBufferListObjBase<Pointer, TLazListClassesVarItemSize>)
373   public
374     procedure Create(AnItemSize: Integer);
375   end;
376 
377   { TLazRoundBufferListObjGen }
378 
379   generic TLazRoundBufferListObjGen<T> = object
380   private type
381     TItemSize = specialize TLazListClassesItemSize<T>;
382     PT = ^T;
383     TListType = specialize TLazRoundBufferListObjBase<PT, TItemSize>;
384   private
385     FList: TListType;
386   // forwarded methods
387   private
GetCapacitynull388     function GetCapacity: Integer; inline;
GetCountnull389     function GetCount: Integer; inline;
GetItemPointernull390     function GetItemPointer(Index: Integer): PT; inline;
GetItemPointerFastnull391     function GetItemPointerFast(Index: Integer): PT; inline;
392     procedure SetCapacity(AValue: Integer); inline;
393   protected
InsertRowsExnull394     function InsertRowsEx(AIndex, ACount: Integer; AGrowProc: TLazStorageMemGrowProc): PT; inline;
395     procedure DeleteRowsEx(AIndex, ACount: Integer; AShrinkProc: TLazStorageMemShrinkProc); inline;
396     property ItemPointerFast[Index: Integer]: PT read GetItemPointerFast;
397   public
398     procedure Create;
399     procedure Destroy;
InsertRowsnull400     function InsertRows(AIndex, ACount: Integer): PT; inline; // can be re-introduced, to change GrowProc
401     procedure DeleteRows(AIndex, ACount: Integer); inline; // can be re-introduced, to change ShrinkProc
402     procedure MoveRows(AFromIndex, AToIndex, ACount: Integer); inline;
403     procedure SwapEntries(AIndex1, AIndex2: Integer); inline;
404     procedure DebugDump;
405 
406     property Capacity: Integer read GetCapacity write SetCapacity;
407     property Count: Integer read GetCount;
408     property ItemPointer[Index: Integer]: PT read GetItemPointer;
409   // new extra methods
410   private
Getnull411     function Get(Index: Integer): T;
412     procedure Put(Index: Integer; AValue: T);
413   public
IndexOfnull414     function IndexOf(AnItem: T): integer;
415     property Items[Index: Integer]: T read Get write Put; default;
416   end;
417 
418   { TLazFixedRoundBufferListMemBase }
419 
420   generic TLazFixedRoundBufferListMemBase<TPItemT, TSizeT> = object(specialize TLazRoundBufferListObjBase<TPItemT, TSizeT>)
421   private
GetItemPointerMaskednull422     function GetItemPointerMasked(AnIndex, AMask: Integer): TPItemT; inline; // AMask: Bitmask for Capacity
GetItemByteOffsetMaskednull423     function GetItemByteOffsetMasked(AnIndex, AMask: Integer): Integer; inline; // AMask: Bitmask for Capacity
GetFirstItemByteOffsetnull424     function GetFirstItemByteOffset: Integer; inline; // AMask: Bitmask for Capacity
425   protected
GrowCapacitynull426     function GrowCapacity(ARequired: Integer): Integer;
ShrinkCapacitynull427     function ShrinkCapacity({%H-}ARequired: Integer): Integer;
428     property Mem: TLazListClassesInternalMem read FMem;
429 
430     // Special Methods for use with PagedList
431     procedure AdjustFirstItemOffset(ACount, AMask: Integer); inline; // For bubbling / shift the buffer
432     procedure InsertRowsAtStart(ACount, AMask: Integer); inline;
433     procedure InsertRowsAtEnd(ACount: Integer); inline;
434     procedure InsertRowsAtBoundary(AnAtStart: Boolean; ACount, AMask: Integer);
435     procedure DeleteRowsAtStart(ACount, AMask: Integer); inline;
436     procedure DeleteRowsAtEnd(ACount: Integer); inline;
437     procedure DeleteRowsAtBoundary(AnAtStart: Boolean; ACount, AMask: Integer); inline;
438     procedure MoveRowsToOther(AFromOffset, AToOffset, ACount, ACap: Integer; AnOther: TLazFixedRoundBufferListMemBase); inline;
439     procedure MoveBytesToOther(AFromByteOffset, AToByteOffset, AByteCount, AByteCap: Integer; AnOther: TLazFixedRoundBufferListMemBase);
440     property ItemPointerMasked[AnIndex, AMask: Integer]: TPItemT read GetItemPointerMasked;
441   public
442     procedure Create(AItemSize: TSizeT; ACapacity: Integer);
InsertRowsnull443     function InsertRows(AIndex, ACount: Integer): TPItemT; inline;
444     procedure DeleteRows(AIndex, ACount: Integer); inline;
445   end;
446 
447   { TLazPagedListObjBase }
448 
449   generic TLazPagedListObjBase<TPItemT, TSizeT> = object
450   private type
451     TPageType = specialize TLazFixedRoundBufferListMemBase<TPItemT, TSizeT>;
452     PPageType = ^TPageType;
453     TPageSize = specialize TLazListClassesItemSize<TPageType>;
454     TPageListType = specialize TLazShiftBufferListObjBase<PPageType, TPageSize>;
455   private
456     FGrowProc: TLazStorageMemGrowProc;
457     FShrinkProc: TLazStorageMemShrinkProc;
458     FExtraCapacityNeeded: Integer;
459     FPages: TPageListType;
460     FItemSize: TSizeT;
461     FPageSizeMask, FPageSizeExp: Integer;
462     FFirstPageEmpty: Integer;
463     FCount: Integer;
464 
GetPagePointernull465     function GetPagePointer(PageIndex: Integer): PPageType; inline;
GetPageSubIdxnull466     function GetPageSubIdx(Index: Integer): Integer; inline; // except for page=0
GetItemPageIdxnull467     function GetItemPageIdx(Index: Integer): Integer; inline;
GetItemPointernull468     function GetItemPointer(Index: Integer): TPItemT; inline;
469     procedure SetCapacity(AValue: Integer); inline;
GetCapacitynull470     function GetCapacity: Integer; inline;
GetPageCountnull471     function GetPageCount: Integer; inline;
472     procedure JoinPageWithNext(APageIdx, AJoinEntryIdx, AnExtraDelPages: Integer); inline;
473     procedure SplitPageToFront(ASourcePageIdx, ASplitAtIdx, AnExtraPages: Integer; AExtraCapacityNeeded: Integer = 0); inline;
474     procedure SplitPageToBack(ASourcePageIdx, ASplitAtIdx, AnExtraPages: Integer; AExtraCapacityNeeded: Integer = 0); inline;
475     procedure SplitPage(ASourcePageIdx, ASplitAtIdx, AnExtraPages: Integer; AExtraCapacityNeeded: Integer = 0);
476     procedure BubbleEntriesDown(ASourceStartIdx, ATargetEndIdx, AnEntryCount: Integer); inline;
477     procedure BubbleEntriesUp(ASourceStartIdx, ATargetEndIdx, AnEntryCount: Integer); inline;
478     procedure InternalBubbleEntriesDown(ASourceStartIdx, ATargetEndIdx, AnEntryCount: Integer);
479     procedure InternalBubbleEntriesUp(ASourceStartIdx, ATargetEndIdx, AnEntryCount: Integer);
480     procedure SwapPagesUp(ASourceStartIndex, ATargetStartIndex, ATargetEndIndex: Integer); inline;
481     procedure SwapPagesDown(ASourceStartIndex, ATargetStartIndex, ATargetEndIndex: Integer); inline;
482     procedure InternalMoveRowsDown(AFromIndex, AToIndex, ACount: Integer); inline;
483     procedure InternalMoveRowsUp(AFromIndex, AToIndex, ACount: Integer); inline;
484     procedure InsertFilledPages(AIndex, ACount: Integer; AExtraCapacityNeeded: Integer = 0); inline;
485     procedure DeletePages(AIndex, ACount: Integer); inline;
486   protected
GrowCapacitynull487     function GrowCapacity(ARequiredPages: Integer): Integer;
ShrinkCapacitynull488     function ShrinkCapacity(ARequiredPages: Integer): Integer;
489     property PagePointer[PageIndex: Integer]: PPageType read GetPagePointer;
490   public
491     procedure Create(APageSizeExp: Integer);
492     procedure Destroy;
493     procedure InsertRows(AIndex, ACount: Integer);
494     procedure DeleteRows(AIndex, ACount: Integer);
495     procedure MoveRows(AFromIndex, AToIndex, ACount: Integer);
496     procedure DebugDump;
497 
498     property Capacity: Integer read GetCapacity write SetCapacity;
499     property Count: Integer read FCount;
500     property PageCount: Integer read GetPageCount;
501     property ItemPointer[Index: Integer]: TPItemT read GetItemPointer;
502     property GrowProc: TLazStorageMemGrowProc read FGrowProc write FGrowProc;
503     property ShrinkProc: TLazStorageMemShrinkProc read FShrinkProc write FShrinkProc;
504   end;
505 
506   TLazPagedListObjParent = specialize TLazPagedListObjBase<Pointer, TLazListClassesVarItemSize>;
507 
508   TLazPagedListObj = object(TLazPagedListObjParent)
509   public
510     procedure Create(APageSizeExp: Integer; AnItemSize: Integer);
511   end;
512 
513 
514   { TLazShiftBufferList }
515 
516   TLazShiftBufferList = class
517   private
518     FListMem: TLazShiftBufferListObj;
GetCapacitynull519     function GetCapacity: Integer;
GetCountnull520     function GetCount: Integer;
GetItemPointernull521     function GetItemPointer(Index: Integer): Pointer;
522     procedure SetCapacity(AValue: Integer);
523     procedure SetCount(AValue: Integer);
524   public
525     constructor Create(AnItemSize: Integer);
526     destructor Destroy; override;
527 
Addnull528     function Add(ItemPointer: Pointer): Integer;
529     procedure Clear; virtual;
530     procedure Delete(Index: Integer);
IndexOfnull531     //function IndexOf(ItemPointer: Pointer): Integer;
532     procedure Insert(Index: Integer; ItemPointer: Pointer);
533     property Capacity: Integer read GetCapacity write SetCapacity;
534     property Count: Integer read GetCount write SetCount;
535     property ItemPointer[Index: Integer]: Pointer read GetItemPointer;
536   end;
537 
538   { TLazShiftBufferListGen }
539 
540   generic TLazShiftBufferListGen<T> = class
541   private type
542     TListMem = specialize TLazShiftBufferListObjGen<T>;
543     PT = ^T;
544   private
545     FListMem: TListMem;
Getnull546     function Get(Index: Integer): T;
GetCapacitynull547     function GetCapacity: Integer;
GetCountnull548     function GetCount: Integer;
GetItemPointernull549     function GetItemPointer(Index: Integer): PT;
550     procedure Put(Index: Integer; AValue: T);
551     procedure SetCapacity(AValue: Integer);
552     procedure SetCount(AValue: Integer);
553   public
554     constructor Create;
555     destructor Destroy; override;
556 
Addnull557     function Add(Item: T): Integer;
558     procedure Clear; virtual;
559     procedure Delete(Index: Integer);
IndexOfnull560     function IndexOf(Item: T): Integer;
561     procedure Insert(Index: Integer; Item: T);
Removenull562     function Remove(Item: T): Integer;
563     property Capacity: Integer read GetCapacity write SetCapacity;
564     property Count: Integer read GetCount write SetCount;
565     property ItemPointer[Index: Integer]: PT read GetItemPointer;
566     property Items[Index: Integer]: T read Get write Put; default;
567   end;
568 
569 implementation
570 
571 { TLazListClassesInternalMem }
572 
TLazListClassesInternalMem.GetDataPointernull573 function TLazListClassesInternalMem.GetDataPointer: PByte;
574 begin
575   Result := @(FMem^.Data);
576 end;
577 
GetFirstItemIndexnull578 function TLazListClassesInternalMem.GetFirstItemIndex: Integer;
579 begin
580   Result := FMem^.FirstItem.Idx;
581 end;
582 
TLazListClassesInternalMem.GetFirstItemPointernull583 function TLazListClassesInternalMem.GetFirstItemPointer: PByte;
584 begin
585   Result := FMem^.FirstItem.Ptr;
586 end;
587 
GetCapacityFastnull588 function TLazListClassesInternalMem.GetCapacityFast: Cardinal;
589 begin
590   Result := FMem^.Capacity;
591 end;
592 
593 procedure TLazListClassesInternalMem.SetCapacity(AValue: Cardinal);
594 begin
595   assert(FMem <> nil, 'TLazListClassesInternalMem.SetCapacity: FMem <> nil');
596   FMem^.Capacity := AValue;
597 end;
598 
GetCapacitynull599 function TLazListClassesInternalMem.GetCapacity: Cardinal;
600 begin
601   if FMem = nil
602   then Result := 0
603   else Result := FMem^.Capacity;
604 end;
605 
GetCountnull606 function TLazListClassesInternalMem.GetCount: Integer;
607 begin
608   if FMem = nil
609   then Result := 0
610   else Result := FMem^.Count;
611 end;
612 
613 procedure TLazListClassesInternalMem.SetCount(AValue: Integer);
614 begin
615   assert(FMem <> nil, 'TLazListClassesInternalMem.SetCount: FMem <> nil');
616   FMem^.Count := AValue;
617 end;
618 
619 procedure TLazListClassesInternalMem.SetFirstItemIndex(AValue: Integer);
620 begin
621   FMem^.FirstItem.Idx := AValue;
622 end;
623 
624 procedure TLazListClassesInternalMem.SetFirstItemPointer(AValue: PByte);
625 begin
626   assert(FMem <> nil, 'TLazListClassesInternalMem.SetFirstItemPointer: FMem <> nil');
627   FMem^.FirstItem.Ptr := AValue;
628 end;
629 
630 procedure TLazListClassesInternalMem.Init;
631 begin
632   FMem := nil;
633 end;
634 
635 procedure TLazListClassesInternalMem.Alloc(AByteSize: Integer);
636 begin
637   Free;
638   FMem := Getmem(SizeOf(TMemRecord) + AByteSize);
639 end;
640 
641 procedure TLazListClassesInternalMem.Free;
642 begin
643   if FMem <> nil then
644     Freemem(FMem);
645   FMem := nil;
646 end;
647 
IsAllocatednull648 function TLazListClassesInternalMem.IsAllocated: Boolean;
649 begin
650   Result := FMem <> nil;
651 end;
652 
653 { TLazShiftBufferListObjBase }
654 
TLazShiftBufferListObjBase.GetItemPointernull655 function TLazShiftBufferListObjBase.GetItemPointer(Index: Integer): TPItemT;
656 begin
657   assert((not FMem.IsAllocated) or (Cardinal(Index) <= FMem.Capacity), 'TLazShiftBufferListObjBase.GetItemPointer: (not FMem.IsAllocated) or (Index <= FMem.Capacity)');
658   if not FMem.IsAllocated
659   then Result := nil
660   else Result := TPItemT(FMem.FirstItemPointer + (Index * FItemSize.ItemSize));
661 end;
662 
TLazShiftBufferListObjBase.GetItemPointerFastnull663 function TLazShiftBufferListObjBase.GetItemPointerFast(Index: Integer): TPItemT;
664 begin
665   assert(Cardinal(Index) <= FMem.Capacity, 'TLazShiftBufferListObjBase.GetItemPointerFast: Index <= FMem.Capacity');
666   Result := TPItemT(FMem.FirstItemPointer + (Index * FItemSize.ItemSize));
667 end;
668 
669 procedure TLazShiftBufferListObjBase.SetCapacity(AValue: Integer);
670 begin
671   SetCapacityEx(AValue, 0, 0);
672 end;
673 
GetCapacitynull674 function TLazShiftBufferListObjBase.GetCapacity: Integer;
675 begin
676   Result := FMem.Capacity;
677 end;
678 
TLazShiftBufferListObjBase.GetCountnull679 function TLazShiftBufferListObjBase.GetCount: Integer;
680 begin
681   Result := FMem.Count;
682 end;
683 
TLazShiftBufferListObjBase.GrowCapacitynull684 function TLazShiftBufferListObjBase.GrowCapacity(ARequired: Integer): Integer;
685 begin
686   Result := Min(ARequired * 2, ARequired + $8000);
687 end;
688 
ShrinkCapacitynull689 function TLazShiftBufferListObjBase.ShrinkCapacity(ARequired: Integer): Integer;
690 begin
691   assert(ARequired <= Capacity, 'TLazShiftBufferListObjBase.ShrinkCapacity: ARequired <= Capacity');
692   if ARequired * 4 < Capacity then
693     Result := ARequired * 2
694   else
695     Result := -1;
696 end;
697 
TLazShiftBufferListObjBase.SetCapacityExnull698 function TLazShiftBufferListObjBase.SetCapacityEx(AValue, AnInsertPos,
699   AnInsertSize: Integer): TPItemT;
700 var
701   NewMem: TLazListClassesInternalMem;
702   Pos1, Cnt, NewCnt, c: Integer;
703   PTarget, PSource: PByte;
704 begin
705   Result := nil;
706   Cnt := Count;
707   NewCnt := Cnt + AnInsertSize;
708   if AValue < NewCnt then
709     AValue := NewCnt;
710 
711   if AValue = 0 then begin
712     FMem.Free;
713     exit;
714   end;
715 
716   if AnInsertSize = 0 then begin;
717     if (AValue = Capacity) then
718       exit;
719     AnInsertPos := 0;
720   end;
721 
722   {%H-}NewMem.Init;
723   NewMem.Alloc(AValue * FItemSize.ItemSize);
724 
725   Pos1 := Cardinal(AValue-NewCnt) div 2;
726   PTarget := NewMem.DataPointer + (Pos1 * FItemSize.ItemSize);
727 
728   NewMem.FirstItemPointer := PTarget;
729   NewMem.Count := NewCnt;
730   NewMem.Capacity := AValue;
731   assert((NewMem.FirstItemPointer >= NewMem.DataPointer) and (NewMem.FirstItemPointer < NewMem.DataPointer + NewMem.Capacity {%H-}* FItemSize.ItemSize), 'TLazShiftBufferListObjBase.InsertRowsEx: (NewMem.FirstItemPointer >= NewMem.NewMem+NewMem.DATA_OFFS) and (NewMem.FirstItemPointer < NewMem.NewMem+NewMem.DATA_OFFS + NewMem.Capacity * FItemSize.ItemSize)');
732 
733   if Cnt > 0 then begin
734     PSource := FMem.FirstItemPointer;
735     if AnInsertPos > 0 then begin
736       c := AnInsertPos * FItemSize.ItemSize;
737       Move(PSource^, PTarget^, c);
738       PSource := PSource + c;
739       Result := TPItemT(PTarget+c);
740     end
741     else
742       Result := TPItemT(PTarget);
743     PTarget := PTarget + ((AnInsertPos + AnInsertSize) * FItemSize.ItemSize);
744 
745     if AnInsertPos < Cnt then
746       Move(PSource^, PTarget^, ((Cnt - AnInsertPos) * FItemSize.ItemSize));
747   end
748   else begin
749     assert(AnInsertPos=0, 'TLazShiftBufferListObjBase.SetCapacityEx: AnInsertPos=0');
750     Result := TPItemT(PTarget);
751   end;
752 
753   FMem.Free;
754   FMem := NewMem;
755 end;
756 
InsertRowsExnull757 function TLazShiftBufferListObjBase.InsertRowsEx(AIndex, ACount: Integer;
758   AGrowProc: TLazStorageMemGrowProc): TPItemT;
759 var
760   Cnt, Cap, CntFreeFront, CntFreeEnd, Middle, i, c: Integer;
761   CanFront, CanEnd: Boolean;
762   PTarget, PSource: PByte;
763 begin
764   Result := nil;
765   if ACount = 0 then exit;
766 
767   Cnt := Count;
768   Cap := Capacity;
769   assert((ACount>0) and (AIndex>=0) and (AIndex<=Cnt), 'TLazShiftBufferListObj.InsertRows: (ACount>0) and (AIndex>=0) and (AIndex<=Cnt)');
770 
771   if Cnt + ACount > Cap then begin
772     if not assigned(AGrowProc) then
773       AGrowProc := @GrowCapacity;
774     Result := SetCapacityEx(AGrowProc(Cnt + ACount), AIndex, ACount);
775     exit;
776   end;
777 
778   CntFreeFront := (FMem.FirstItemPointer - FMem.DataPointer) div FItemSize.ItemSize;
779   CntFreeEnd   := Cap - CntFreeFront - Cnt;
780   CanFront := CntFreeFront >= ACount;
781   CanEnd   := CntFreeEnd >= ACount;
782 
783   if not(CanFront or CanEnd)
784   then begin
785     if not assigned(AGrowProc) then
786       AGrowProc := @GrowCapacity;
787     i := AGrowProc(Cnt + ACount);
788     if i > Cap then begin
789       Result := SetCapacityEx(AGrowProc(Cnt + ACount), AIndex, ACount);
790       exit;
791     end;
792 
793     Middle := 0;
794   end
795   else
796     Middle := Cardinal(Cnt) div 2;
797 
798   if CanFront and ((AIndex < Middle) or (not CanEnd)) then begin
799     // use space at front of list
800     i := ACount;
801     if (AIndex = Cnt) and (CntFreeFront-ACount > CntFreeEnd) then             // move all entries;
802       i := i + Max(Cardinal(CntFreeFront-ACount-CntFreeEnd) div 2 - 1, 0);    // Make some room at the end of the list
803 
804     PSource := FMem.FirstItemPointer;
805     PTarget := PSource - (i * FItemSize.ItemSize);
806     c := AIndex * FItemSize.ItemSize;
807     if AIndex > 0 then
808       Move(PSource^, PTarget^, c);
809     Result := TPItemT(PTarget + c);
810 
811     assert(PTarget >= FMem.DataPointer, 'TLazShiftBufferListObj.InsertRows: PTarget >= FMem+DATA_OFFS');
812     FMem.FirstItemPointer := PTarget;
813     FMem.Count := Cnt + ACount;
814   end
815   else
816   if CanEnd then begin
817     // use space at end of list
818     if (AIndex = 0) and (CntFreeEnd-ACount > CntFreeFront) then             // move all entries;
819       i := max(Cardinal(CntFreeEnd-ACount-CntFreeFront) div 2 - 1, 0)    // Make some room at the end of the list
820     else
821       i := 0;
822 
823     PSource := FMem.FirstItemPointer + (AIndex * FItemSize.ItemSize);
824     PTarget := PSource + ((ACount + i) * FItemSize.ItemSize);
825     if Cnt-AIndex > 0 then
826       Move(PSource^, PTarget^, (Cnt-AIndex) * FItemSize.ItemSize);
827 
828     if i > 0 then begin
829       assert(PSource + (i * FItemSize.ItemSize) >= FMem.DataPointer, 'TLazShiftBufferListObj.InsertRows: PSource + (i * FItemSize.ItemSize) >= FMem+DATA_OFFS');
830       PSource := PSource + (i * FItemSize.ItemSize);
831       FMem.FirstItemPointer := PSource;
832     end;
833     Result := TPItemT(PSource);
834     FMem.Count := Cnt + ACount;
835   end
836   else
837   begin
838  	// split to both ends
839     assert((cap >= ACount) and (CntFreeFront> 0) and (CntFreeEnd > 0), 'TLazShiftBufferListObj.InsertRows: (cap >= ACount) and (CntFreeFront> 0) and (CntFreeEnd > 0)');
840     i := Max(Cardinal(Cap-Cnt-ACount) div 2 - 1, 0);
841 
842     PSource := FMem.FirstItemPointer;
843     PTarget := PSource - ((CntFreeFront - i) * FItemSize.ItemSize);
844     c := AIndex * FItemSize.ItemSize;
845     if AIndex > 0 then
846       Move(PSource^, PTarget^, c);
847     Result := TPItemT(PTarget + c);
848 
849     FMem.FirstItemPointer := PTarget;
850     FMem.Count := Cnt + ACount;
851 
852     assert((ACount>CntFreeFront-i) and (ACount-(CntFreeFront - i)<=CntFreeEnd), 'TLazShiftBufferListObj.InsertRows: (ACount>CntFreeFront-i) and (ACount-(CntFreeFront - i)<=CntFreeEnd)');
853     PSource := PSource + c;
854     PTarget := PSource + ((ACount - (CntFreeFront - i)) * FItemSize.ItemSize);
855     if Cnt-AIndex > 0 then
856       Move(PSource^, PTarget^, (Cnt-AIndex) * FItemSize.ItemSize);
857   end;
858 
859   assert((FMem.FirstItemPointer >= FMem.DataPointer) and (FMem.FirstItemPointer < FMem.DataPointer + FMem.Capacity {%H-}* FItemSize.ItemSize), 'TLazShiftBufferListObjBase.InsertRowsEx: (FMem.FirstItemPointer >= FMem.FMem+FMem.DATA_OFFS) and (FMem.FirstItemPointer < FMem.FMem+FMem.DATA_OFFS + FMem.Capacity * FItemSize.ItemSize)');
860 end;
861 
862 procedure TLazShiftBufferListObjBase.DeleteRowsEx(AIndex, ACount: Integer;
863   AShrinkProc: TLazStorageMemShrinkProc);
864 var
865   Cnt, Middle, i: Integer;
866   PTarget, PSource: PByte;
867 begin
868   if ACount = 0 then exit;
869 
870   Cnt := Count;
871   assert((ACount>0) and (AIndex>=0) and (AIndex+ACount<=Cnt), 'TLazShiftBufferListObj.InsertRows: (ACount>0) and (AIndex>=0) and (AIndex+ACount<=Cnt)');
872   Middle := Cardinal(Cnt) div 2;
873 
874   if AIndex < Middle then begin
875     // use space at front of list
876     PSource := FMem.FirstItemPointer;
877     PTarget := PSource + (ACount * FItemSize.ItemSize);
878     if AIndex > 0 then
879       Move(PSource^, PTarget^, AIndex * FItemSize.ItemSize);
880     FMem.FirstItemPointer := PTarget;
881     FMem.Count := Cnt - ACount;
882   end
883   else begin
884     // use space at end of list
885     i := AIndex + ACount;
886     PSource := FMem.FirstItemPointer + (i * FItemSize.ItemSize);
887     PTarget := PSource - (ACount * FItemSize.ItemSize);
888     if Cnt-i > 0 then
889       Move(PSource^, PTarget^, (Cnt-i) * FItemSize.ItemSize);
890     FMem.Count := Cnt - ACount;
891   end;
892   if not assigned(AShrinkProc) then
893     i := ShrinkCapacity(Count)
894   else
895     i := AShrinkProc(Count);
896   if i >= 0 then
897     SetCapacityEx(i, 0, 0)
898   else
899   if (Count = 0) then
900     FMem.FirstItemPointer := FMem.DataPointer + (FMem.Capacity div 2) * Cardinal(FItemSize.ItemSize);
901   assert((not FMem.IsAllocated) or ((FMem.FirstItemPointer >= FMem.DataPointer) and (FMem.FirstItemPointer < FMem.DataPointer + FMem.Capacity {%H-}* FItemSize.ItemSize)), 'TLazShiftBufferListObjBase.InsertRowsEx: (FMem.FirstItemPointer >= FMem.FMem+FMem.DATA_OFFS) and (FMem.FirstItemPointer < FMem.FMem+FMem.DATA_OFFS + FMem.Capacity * FItemSize.ItemSize)');
902 end;
903 
904 procedure TLazShiftBufferListObjBase.Create;
905 begin
906   FMem.Init;
907 end;
908 
909 procedure TLazShiftBufferListObjBase.Destroy;
910 begin
911   FMem.Free;
912 end;
913 
InsertRowsnull914 function TLazShiftBufferListObjBase.InsertRows(AIndex, ACount: Integer): TPItemT;
915 begin
916   Result := InsertRowsEx(AIndex, ACount, @GrowCapacity);
917 end;
918 
919 procedure TLazShiftBufferListObjBase.DeleteRows(AIndex, ACount: Integer);
920 begin
921   DeleteRowsEx(AIndex, ACount, @ShrinkCapacity);
922 end;
923 
924 procedure TLazShiftBufferListObjBase.MoveRows(AFromIndex, AToIndex, ACount: Integer);
925 var
926   BytesToMove, DistanceToMove: Integer;
927   p, pFrom, pTo: PByte;
928 begin
929   assert((AFromIndex>=0) and (AToIndex>=0) and (AFromIndex+ACount<=Count) and (AToIndex+ACount<=Count), 'TLazShiftBufferListObjBase.MoveRows: (AFromIndex>=0) and (AToIndex>=0) and (AFromIndex+ACount<=Count) and (AToIndex+ACount<=Count)');
930 
931   BytesToMove := FItemSize.ItemSize * ACount;
932   pFrom := PByte(GetItemPointer(AFromIndex));
933   pTo   := PByte(GetItemPointer(AToIndex));
934 
935   if (ACount << 1) > Count then begin
936     p := FMem.FirstItemPointer;
937     if AToIndex < AFromIndex then begin
938       // free at end? (instead of moving entries down, move surroundings up
939       DistanceToMove := pFrom - pTo;
940       if (FMem.DataPointer + (FMem.Capacity - Count) * FItemSize.ItemSize - p) > DistanceToMove then begin
941         Move(p^, (p + DistanceToMove)^, pTo - p);
942         pFrom := pFrom + BytesToMove;
943         Move(pFrom^, (pFrom + DistanceToMove)^, p + Count * FItemSize.ItemSize - pFrom);
944         FMem.FirstItemPointer := FMem.FirstItemPointer + DistanceToMove;
945         assert((FMem.FirstItemPointer >= FMem.DataPointer) and (FMem.FirstItemPointer < FMem.DataPointer + FMem.Capacity {%H-}* FItemSize.ItemSize), 'TLazShiftBufferListObjBase.MoveRows: (FMem.FirstItemPointer >= FMem.DataPointer) and (FMem.FirstItemPointer < FMem.DataPointer + FMem.Capacity {%H-}* FItemSize.ItemSize)');
946         exit;
947       end;
948     end
949     else begin
950       // free at front (instead of moving entries up, move surroundings down
951       DistanceToMove := pTo - pFrom;
952       if (FMem.FirstItemPointer - FMem.DataPointer) > DistanceToMove then begin
953         Move(p^, (p - DistanceToMove)^, pFrom - p);
954         pFrom := pFrom + BytesToMove;
955         Move((pFrom + DistanceToMove)^, pFrom^, p + Count * FItemSize.ItemSize - pFrom - DistanceToMove);
956         FMem.FirstItemPointer := FMem.FirstItemPointer - DistanceToMove;
957         assert((FMem.FirstItemPointer >= FMem.DataPointer) and (FMem.FirstItemPointer < FMem.DataPointer + FMem.Capacity {%H-}* FItemSize.ItemSize), 'TLazShiftBufferListObjBase.MoveRows: (FMem.FirstItemPointer >= FMem.DataPointer) and (FMem.FirstItemPointer < FMem.DataPointer + FMem.Capacity {%H-}* FItemSize.ItemSize)');
958         exit;
959       end;
960     end;
961   end;
962 
963   Move(pFrom^, pTo^, BytesToMove);
964   assert((FMem.FirstItemPointer >= FMem.DataPointer) and (FMem.FirstItemPointer < FMem.DataPointer + FMem.Capacity {%H-}* FItemSize.ItemSize), 'TLazShiftBufferListObjBase.MoveRows: (FMem.FirstItemPointer >= FMem.DataPointer) and (FMem.FirstItemPointer < FMem.DataPointer + FMem.Capacity {%H-}* FItemSize.ItemSize)');
965 end;
966 
967 procedure TLazShiftBufferListObjBase.SwapEntries(AIndex1, AIndex2: Integer);
968 var
969   t: PByte;
970 begin
971   t := Getmem(FItemSize.ItemSize);
972   Move(PByte(GetItemPointer(AIndex1))^, t^, FItemSize.ItemSize);
973   Move(PByte(GetItemPointer(AIndex2))^, PByte(GetItemPointer(AIndex1))^, FItemSize.ItemSize);
974   Move(t^, PByte(GetItemPointer(AIndex2))^, FItemSize.ItemSize);
975   FreeMem(t);
976 end;
977 
978 procedure TLazShiftBufferListObjBase.DebugDump;
979 var i : integer; s:string;
980 begin
981   if fmem.IsAllocated then begin
982     dbgout(['TLazFixedRoundBufferListMemBase.Dump ', FMem.Capacity, ' , ',FMem.Count,
983     ' --- ', fmem.datapointer, ' , ',FMem.FirstItemPointer,' --- ', ': ']);
984      s :='';
985     for i := 0 to FMem.Count - 1 do s := s +dbgMemRange(itempointer[i], FItemSize.ItemSize )+ ', ';
986     debugln(s);
987   end
988   else debugln(['TLazFixedRoundBufferListMemBase.Dump NONE']);
989 end;
990 
991 { TLazShiftBufferListObj }
992 
993 procedure TLazShiftBufferListObj.Create(AnItemSize: Integer);
994 begin
995   fItemSize.ItemSize := AnItemSize;
996   inherited Create;
997 end;
998 
999 { TLazShiftBufferListObjGen }
1000 
TLazShiftBufferListObjGen.Getnull1001 function TLazShiftBufferListObjGen.Get(Index: Integer): T;
1002 begin
1003   Result := FList.ItemPointer[Index]^;
1004 end;
1005 
GetCapacitynull1006 function TLazShiftBufferListObjGen.GetCapacity: Integer;
1007 begin
1008   Result := FList.GetCapacity;
1009 end;
1010 
TLazShiftBufferListObjGen.GetCountnull1011 function TLazShiftBufferListObjGen.GetCount: Integer;
1012 begin
1013   Result := FList.GetCount;
1014 end;
1015 
TLazShiftBufferListObjGen.GetItemPointernull1016 function TLazShiftBufferListObjGen.GetItemPointer(Index: Integer): PT;
1017 begin
1018   Result := FList.GetItemPointer(Index);
1019 end;
1020 
TLazShiftBufferListObjGen.GetItemPointerFastnull1021 function TLazShiftBufferListObjGen.GetItemPointerFast(Index: Integer): PT;
1022 begin
1023   Result := FList.ItemPointerFast[Index];
1024 end;
1025 
1026 procedure TLazShiftBufferListObjGen.Put(Index: Integer; AValue: T);
1027 begin
1028   FList.ItemPointer[Index]^ := AValue;
1029 end;
1030 
1031 procedure TLazShiftBufferListObjGen.SetCapacity(AValue: Integer);
1032 begin
1033   FList.SetCapacity(AValue);
1034 end;
1035 
InsertRowsExnull1036 function TLazShiftBufferListObjGen.InsertRowsEx(AIndex, ACount: Integer;
1037   AGrowProc: TLazStorageMemGrowProc): PT;
1038 begin
1039   Result := FList.InsertRowsEx(AIndex, ACount, AGrowProc);
1040 end;
1041 
1042 procedure TLazShiftBufferListObjGen.DeleteRowsEx(AIndex, ACount: Integer;
1043   AShrinkProc: TLazStorageMemShrinkProc);
1044 begin
1045   FList.DeleteRowsEx(AIndex, ACount, AShrinkProc);
1046 end;
1047 
1048 procedure TLazShiftBufferListObjGen.Create;
1049 begin
1050   FList.Create;
1051 end;
1052 
1053 procedure TLazShiftBufferListObjGen.Destroy;
1054 begin
1055   FList.Destroy;
1056 end;
1057 
InsertRowsnull1058 function TLazShiftBufferListObjGen.InsertRows(AIndex, ACount: Integer): PT;
1059 begin
1060   Result := FList.InsertRows(AIndex, ACount);
1061 end;
1062 
1063 procedure TLazShiftBufferListObjGen.DeleteRows(AIndex, ACount: Integer);
1064 begin
1065   FList.DeleteRows(AIndex, ACount);
1066 end;
1067 
1068 procedure TLazShiftBufferListObjGen.MoveRows(AFromIndex, AToIndex, ACount: Integer);
1069 begin
1070   FList.MoveRows(AFromIndex, AToIndex, ACount);
1071 end;
1072 
1073 procedure TLazShiftBufferListObjGen.SwapEntries(AIndex1, AIndex2: Integer);
1074 begin
1075   FList.SwapEntries(AIndex1, AIndex2);
1076 end;
1077 
1078 procedure TLazShiftBufferListObjGen.DebugDump;
1079 begin
1080   FList.DebugDump;
1081 end;
1082 
TLazShiftBufferListObjGen.IndexOfnull1083 function TLazShiftBufferListObjGen.IndexOf(AnItem: T): integer;
1084 var
1085   p: PT;
1086 begin
1087   Result := Count - 1;
1088   p := ItemPointer[Result];
1089   while Result >= 0 do begin
1090     if p^ = AnItem then exit;
1091     dec(p);
1092     dec(Result);
1093   end;
1094 end;
1095 
1096 { TLazRoundBufferListObjBase }
1097 
TLazRoundBufferListObjBase.GetItemPointernull1098 function TLazRoundBufferListObjBase.GetItemPointer(Index: Integer): TPItemT;
1099 var
1100   c: Integer;
1101 begin
1102   if not FMem.IsAllocated
1103   then Result := nil
1104   else begin
1105     c := FMem.Capacity;
1106     assert(Index <= c, 'TLazRoundBufferListObjBase.GetItemPointer: Index <= c');
1107     Index := FMem.FirstItemIndex + Index;
1108     if Index >= c then
1109       Index := Index - c;
1110     Result := TPItemT(FMem.DataPointer + Index * FItemSize.ItemSize);
1111   end;
1112 end;
1113 
TLazRoundBufferListObjBase.GetItemPointerFastnull1114 function TLazRoundBufferListObjBase.GetItemPointerFast(Index: Integer): TPItemT;
1115 var
1116   c: Cardinal;
1117 begin
1118   c := FMem.Capacity;
1119   assert(Cardinal(Index) <= c, 'TLazRoundBufferListObjBase.GetItemPointerFast: Index <= c');
1120   Index := FMem.FirstItemIndex + Index;
1121   if Cardinal(Index) >= c then
1122     Index := Index - c;
1123   Result := TPItemT(FMem.DataPointer + Index * FItemSize.ItemSize);
1124 end;
1125 
1126 procedure TLazRoundBufferListObjBase.SetCapacity(AValue: Integer);
1127 begin
1128   SetCapacityEx(AValue, 0, 0);
1129 end;
1130 
TLazRoundBufferListObjBase.GetCapacitynull1131 function TLazRoundBufferListObjBase.GetCapacity: Integer;
1132 begin
1133   Result := FMem.Capacity;
1134 end;
1135 
GetCountnull1136 function TLazRoundBufferListObjBase.GetCount: Integer;
1137 begin
1138   Result := FMem.Count;
1139 end;
1140 
1141 procedure TLazRoundBufferListObjBase.InternalMoveUp(AFromEnd, AToEnd: PByte; AByteCnt,
1142   AByteCap: Integer);
1143 var
1144   c: Integer;
1145   l: PByte;
1146 begin
1147   assert(AFromEnd <> AToEnd, 'TLazRoundBufferListObjBase.InternalMoveUp: AFrom <> ATo');
1148   l := FMem.DataPointer;
1149   if AToEnd = l then AToEnd := l + AByteCap;
1150   if AFromEnd = l then AFromEnd := l + AByteCap;
1151 
1152   if AToEnd < AFromEnd then begin
1153     c := Min(AToEnd - l, AByteCnt);
1154     AFromEnd := AFromEnd - c;
1155     AToEnd := AToEnd - c;
1156     Move(AFromEnd^, AToEnd^, c);
1157     AByteCnt := AByteCnt - c;
1158     if AByteCnt = 0 then
1159       exit;
1160     AToEnd := l + AByteCap;
1161   end;
1162 
1163   c := Min(AFromEnd - l, AByteCnt);
1164   AFromEnd := AFromEnd - c;
1165   AToEnd := AToEnd - c;
1166   Move(AFromEnd^, AToEnd^, c);
1167   AByteCnt := AByteCnt - c;
1168   if AByteCnt = 0 then
1169     exit;
1170   AFromEnd := l + AByteCap;
1171 
1172   c := Min(AToEnd - l, AByteCnt);
1173   AFromEnd := AFromEnd - c;
1174   AToEnd := AToEnd - c;
1175   Move(AFromEnd^, AToEnd^, c);
1176   AByteCnt := AByteCnt - c;
1177   if AByteCnt = 0 then
1178     exit;
1179   AToEnd := l + AByteCap;
1180 
1181   Move((AFromEnd-AByteCnt)^, (AToEnd-AByteCnt)^, AByteCnt);
1182 end;
1183 
1184 procedure TLazRoundBufferListObjBase.InternalMoveDown(AFrom, ATo: PByte; AByteCnt: Integer;
1185   AUpperBound: PByte);
1186 var
1187   c: Integer;
1188 begin
1189   assert(AFrom <> ATo, 'TLazRoundBufferListObjBase.InternalMoveDown: AFrom <> ATo');
1190   if ATo > AFrom then begin
1191     c := Min(AUpperBound - ATo, AByteCnt);
1192     Move(AFrom^, ATo^, c);
1193     AByteCnt := AByteCnt - c;
1194     if AByteCnt = 0 then
1195       exit;
1196     ATo := FMem.DataPointer; // ATo + c - AByteCap;
1197     AFrom := AFrom + c;
1198   end;
1199 
1200   c := Min(AUpperBound - AFrom, AByteCnt);
1201   Move(AFrom^, ATo^, c);
1202   AByteCnt := AByteCnt - c;
1203   if AByteCnt = 0 then
1204     exit;
1205   AFrom := FMem.DataPointer; // AFrom + c - AByteCap;
1206   ATo := ATo + c;
1207 
1208   c := Min(AUpperBound - ATo, AByteCnt);
1209   Move(AFrom^, ATo^, c);
1210   AByteCnt := AByteCnt - c;
1211   if AByteCnt = 0 then
1212     exit;
1213   ATo := FMem.DataPointer; // ATo + c - AByteCap;
1214   AFrom := AFrom + c;
1215 
1216   Move(AFrom^, ATo^, AByteCnt);
1217 end;
1218 
TLazRoundBufferListObjBase.GrowCapacitynull1219 function TLazRoundBufferListObjBase.GrowCapacity(ARequired: Integer): Integer;
1220 begin
1221   Result := Min(ARequired * 2, ARequired + $8000);
1222 end;
1223 
TLazRoundBufferListObjBase.ShrinkCapacitynull1224 function TLazRoundBufferListObjBase.ShrinkCapacity(ARequired: Integer): Integer;
1225 begin
1226   assert(ARequired <= Capacity, 'TLazShiftBufferListObjBase.ShrinkCapacity: ARequired <= Capacity');
1227   if ARequired * 4 < Capacity then
1228     Result := ARequired * 2
1229   else
1230     Result := -1;
1231 end;
1232 
SetCapacityExnull1233 function TLazRoundBufferListObjBase.SetCapacityEx(AValue, AnInsertPos,
1234   AnInsertSize: Integer): TPItemT;
1235 var
1236   NewMem: TLazListClassesInternalMem;
1237   Pos1, Cnt, NewCnt, siz, siz2: Integer;
1238   PTarget, PSource, m: PByte;
1239 begin
1240   Result := nil;
1241   Cnt := Count;
1242   NewCnt := Cnt + AnInsertSize;
1243   if AValue < NewCnt then
1244     AValue := NewCnt;
1245 
1246   if AValue = 0 then begin
1247     FMem.Free;
1248     exit;
1249   end;
1250 
1251   if AnInsertSize = 0 then begin;
1252     if (AValue = Capacity) then
1253       exit;
1254     AnInsertPos := 0;
1255   end;
1256 
1257   {%H-}NewMem.Init;
1258   NewMem.Alloc(AValue * FItemSize.ItemSize);
1259 
1260   Pos1 := Cardinal(AValue-NewCnt) div 2;
1261   PTarget := NewMem.DataPointer + (Pos1 * FItemSize.ItemSize);
1262 
1263   NewMem.FirstItemIndex:= Pos1;
1264   NewMem.Count := NewCnt;
1265   NewMem.Capacity := AValue;
1266   assert((NewMem.FirstItemIndex >= 0) and (NewMem.FirstItemIndex {%H-}< NewMem.Capacity), 'TLazShiftBufferListObjBase.InsertRowsEx: (NewMem.FirstItemIndex >= NewMem.NewMem+NewMem.DATA_OFFS) and (NewMem.FirstItemIndex < NewMem.NewMem+NewMem.DATA_OFFS + NewMem.Capacity)');
1267 
1268   if Cnt > 0 then begin
1269     m := FMem.DataPointer;
1270     PSource := m + (FMem.FirstItemIndex * FItemSize.ItemSize);
1271     m := m + FMem.Capacity * Cardinal(FItemSize.ItemSize);
1272     if AnInsertPos > 0 then begin
1273       siz := (AnInsertPos * FItemSize.ItemSize);
1274       siz2 := m - PSource;
1275       if siz > siz2 then begin
1276         Move(PSource^, PTarget^, siz2);
1277         Move(FMem.DataPointer^, (PTarget+siz2)^, siz - siz2);
1278       end
1279       else
1280         Move(PSource^, PTarget^, siz);
1281       Result := TPItemT(PTarget + siz);
1282     end
1283     else
1284       Result := TPItemT(PTarget);
1285 
1286     if AnInsertPos < Cnt then begin
1287       PSource := PByte(ItemPointer[AnInsertPos]);
1288       PTarget := PTarget + ((AnInsertPos + AnInsertSize) * FItemSize.ItemSize);
1289       siz := ((Cnt - AnInsertPos) * FItemSize.ItemSize);
1290       siz2 := m - PSource;
1291       if siz > siz2 then begin
1292         Move(PSource^, PTarget^, siz2);
1293         Move(FMem.DataPointer^, (PTarget+siz2)^, siz - siz2);
1294       end
1295       else
1296         Move(PSource^, PTarget^, siz);
1297     end;
1298   end
1299   else begin
1300     assert(AnInsertPos=0, 'TLazShiftBufferListObjBase.SetCapacityEx: AnInsertPos=0');
1301     Result := TPItemT(PTarget);
1302   end;
1303 
1304   FMem.Free;
1305   FMem := NewMem;
1306 end;
1307 
InsertRowsExnull1308 function TLazRoundBufferListObjBase.InsertRowsEx(AIndex, ACount: Integer;
1309   AGrowProc: TLazStorageMemGrowProc): TPItemT;
1310 var
1311   Cnt, Cap: Integer;
1312   siz, PSourceIdx, PTargetIdx: Integer;
1313   PTarget, PSource, m: PByte;
1314 begin
1315   Result := nil;
1316   if ACount = 0 then exit;
1317 
1318   Cnt := Count;
1319   Cap := FMem.Capacity * Cardinal(FItemSize.ItemSize);
1320   assert((ACount>0) and (AIndex>=0) and (AIndex<=Cnt), 'TLazShiftBufferListObj.InsertRows: (ACount>0) and (AIndex>=0) and (AIndex<=Cnt)');
1321 
1322   if Cnt + ACount > Capacity then begin
1323     if not Assigned(AGrowProc) then
1324       AGrowProc := @GrowCapacity;
1325     Result := SetCapacityEx(AGrowProc(Cnt + ACount), AIndex, ACount);
1326     exit;
1327   end;
1328 
1329   if (AIndex = 0) or (Cardinal(AIndex) < Cardinal(Cnt) div 2) then begin
1330     // use space at front of list
1331     PSourceIdx := FMem.FirstItemIndex;
1332     PTargetIdx := PSourceIdx - ACount;
1333     if PtrInt(PTargetIdx) < 0 then
1334       PTargetIdx := PTargetIdx + Capacity;
1335     FMem.FirstItemIndex := PTargetIdx;
1336     FMem.Count := Cnt + ACount;
1337 
1338     PTarget := FMem.DataPointer + PTargetIdx * FItemSize.ItemSize;
1339 
1340     if AIndex > 0 then begin
1341       PSource := FMem.DataPointer + PSourceIdx * FItemSize.ItemSize;
1342       siz := AIndex * FItemSize.ItemSize;
1343       Result := TPItemT(PTarget + siz);
1344       m := FMem.DataPointer + Cap;
1345       if PByte(Result) >= m then
1346         Result := TPItemT(PByte(Result) - Cap);
1347       InternalMoveDown(PSource, PTarget, siz, m);
1348     end
1349     else
1350       Result := TPItemT(PTarget);
1351   end
1352   else
1353   begin
1354     // use space at end of list
1355     PSource := PByte(ItemPointer[Cnt]);
1356     PTarget := PSource + (ACount * FItemSize.ItemSize);
1357     if PTarget > FMem.DataPointer + Cap then
1358       PTarget := PTarget - Cap;
1359 
1360     FMem.Count := Cnt + ACount;
1361 
1362     if AIndex < Cnt then begin
1363       siz := (Cnt-AIndex) * FItemSize.ItemSize;
1364       m := FMem.DataPointer;
1365       Result := TPItemT(PSource - siz);
1366       if PByte(Result) < m then
1367         Result := TPItemT(PByte(Result) + Cap);
1368       InternalMoveUp(PSource, PTarget, siz, Cap);
1369     end
1370     else
1371       Result := TPItemT(PSource);
1372   end;
1373 
1374   assert((FMem.FirstItemIndex >= 0) and (FMem.FirstItemIndex {%H-}< FMem.Capacity), 'TLazShiftBufferListObjBase.InsertRowsEx: (FMem.FirstItemPointer >= FMem.FMem+FMem.DATA_OFFS) and (FMem.FirstItemPointer < FMem.FMem+FMem.DATA_OFFS + FMem.Capacity)');
1375 end;
1376 
1377 procedure TLazRoundBufferListObjBase.DeleteRowsEx(AIndex, ACount: Integer;
1378   AShrinkProc: TLazStorageMemShrinkProc);
1379 var
1380   Cnt, Cap, Middle, i, siz, siz2: Integer;
1381   PTarget, PSource, m: PByte;
1382 begin
1383   if ACount = 0 then exit;
1384 
1385   Cnt := Count;
1386   Cap := FMem.Capacity * Cardinal(FItemSize.ItemSize);
1387   assert((ACount>0) and (AIndex>=0) and (AIndex+ACount<=Cnt), 'TLazShiftBufferListObjBase.DeleteRowsEx: (ACount>0) and (AIndex>=0) and (AIndex+ACount<=Cnt)');
1388   Middle := Cardinal(Cnt) div 2;
1389 
1390   if (AIndex < Middle) or (AIndex = 0) then begin
1391     // make space at front of list
1392     PTarget := PByte(ItemPointer[AIndex+ACount]);
1393 
1394     if AIndex > 0 then begin
1395       PSource := PByte(ItemPointer[AIndex]);
1396       siz := AIndex * FItemSize.ItemSize;
1397       m := FMem.DataPointer;
1398       while siz > 0 do begin
1399         siz2 := Min(siz, PSource - m);
1400         siz2 := Min(siz2, PTarget - m);
1401         Move((PSource-siz2)^, (PTarget-siz2)^, siz2);
1402         siz := siz - siz2;
1403         dec(PSource, siz2);
1404         if PSource <= m then
1405           PSource := PSource + Cap;
1406         dec(PTarget, siz2);
1407         if PTarget <= m then
1408           PTarget := PTarget + Cap;
1409       end;
1410       if PTarget = m + Cap then
1411         PTarget := m;
1412     end;
1413 
1414     i := FMem.FirstItemIndex + ACount;
1415     if i >= Capacity then
1416       i := i - Capacity;
1417     FMem.FirstItemIndex := i;
1418     FMem.Count := Cnt - ACount;
1419   end
1420   else begin
1421     // make space at end of list
1422     if AIndex < Cnt-ACount then begin
1423       PSource := PByte(ItemPointer[AIndex+ACount]);
1424       PTarget := PByte(ItemPointer[AIndex]);
1425       siz := (cnt - (AIndex+ACount)) * FItemSize.ItemSize;
1426       m := FMem.DataPointer + Cap;
1427       while siz > 0 do begin
1428         siz2 := Min(siz, m - PSource);
1429         siz2 := Min(siz2, m - PTarget);
1430         Move(PSource^, PTarget^, siz2);
1431         siz := siz - siz2;
1432         inc(PSource, siz2);
1433         if PSource >= m then
1434           PSource := PSource - Cap;
1435         inc(PTarget, siz2);
1436         if PTarget >= m then
1437           PTarget := PTarget - Cap;
1438       end;
1439     end;
1440 
1441     FMem.Count := Cnt - ACount;
1442   end;
1443 
1444   if not Assigned(AShrinkProc) then
1445     i := ShrinkCapacity(Count)
1446   else
1447     i := AShrinkProc(Count);
1448   if i >= 0 then
1449     SetCapacityEx(i, 0, 0)
1450   else
1451   if (Count = 0) then
1452     FMem.FirstItemIndex:= 0;
1453   assert((not FMem.IsAllocated) or ((FMem.FirstItemIndex >= 0) and (FMem.FirstItemIndex {%H-}< FMem.Capacity)), 'TLazShiftBufferListObjBase.DeleteRowsEx: (FMem.FirstItemPointer >= FMem.FMem+FMem.DATA_OFFS) and (FMem.FirstItemPointer < FMem.FMem+FMem.DATA_OFFS + FMem.Capacity)');
1454 end;
1455 
1456 procedure TLazRoundBufferListObjBase.Create;
1457 begin
1458   FMem.Init;
1459 end;
1460 
1461 procedure TLazRoundBufferListObjBase.Destroy;
1462 begin
1463   FMem.Free;
1464 end;
1465 
InsertRowsnull1466 function TLazRoundBufferListObjBase.InsertRows(AIndex, ACount: Integer): TPItemT;
1467 begin
1468   Result := InsertRowsEx(AIndex, ACount, @GrowCapacity);
1469 end;
1470 
1471 procedure TLazRoundBufferListObjBase.DeleteRows(AIndex, ACount: Integer);
1472 begin
1473   DeleteRowsEx(AIndex, ACount, @ShrinkCapacity);
1474 end;
1475 
1476 procedure TLazRoundBufferListObjBase.MoveRows(AFromIndex, AToIndex, ACount: Integer);
1477 var
1478   Cnt, CapBytes, c, Diff: Integer;
1479   BytesToMove: Integer;
1480   u, pFrom, pTo: PByte;
1481   Cap: Cardinal;
1482 begin
1483   assert((AFromIndex>=0) and (AToIndex>=0) and (AFromIndex+ACount<=Count) and (AToIndex+ACount<=Count), 'TLazShiftBufferListObjBase.MoveRows: (AFromIndex>=0) and (AToIndex>=0) and (AFromIndex+ACount<=Count) and (AToIndex+ACount<=Count)');
1484 
1485   Cnt := Count;
1486   Cap := Capacity;
1487   CapBytes := Cap * Cardinal(FItemSize.ItemSize);
1488 
1489   if (ACount * 2) >= Cnt then begin
1490     if AToIndex < AFromIndex then begin
1491       // FirstItemIndex = FirstItemIndex + Diff; // move ALL down
1492       Diff := AFromIndex-AToIndex;
1493       // Save data in front of AToIndex; move it in front of AFromIndex;
1494       InternalMoveUp(PByte(GetItemPointer(AToIndex)), PByte(GetItemPointer(AFromIndex)),
1495                      AToIndex * FItemSize.ItemSize, CapBytes);
1496       // Move data after END-OF-SOURCE up (moving behind current count (wrap around capacity if needed))
1497       c := Cnt + Diff;
1498       if Cardinal(c) > Cap then
1499         c := Cardinal(c) - Cap;
1500       InternalMoveUp(PByte(GetItemPointer(Cnt)),
1501                      PByte(GetItemPointer(c)), // Cnt=Cap will be handled by GetItemPointer
1502                      (Cnt - (AFromIndex+ACount)) * FItemSize.ItemSize, CapBytes);
1503       c := FMem.FirstItemIndex + Diff;
1504       if Cardinal(c) >= Cap then
1505         c := Cardinal(c) - Cap;
1506       FMem.FirstItemIndex := c;
1507     end
1508     else begin
1509       // FirstItemIndex = FirstItemIndex - Diff; // move ALL up
1510       Diff := AToIndex-AFromIndex;
1511       u := FMem.DataPointer + CapBytes;
1512       // Save data from after Target; move it after Source
1513       InternalMoveDown(PByte(GetItemPointer(AToIndex+ACount)),
1514                        PByte(GetItemPointer(AFromIndex+ACount)),
1515                        (Cnt - (AToIndex+ACount)) * FItemSize.ItemSize, u);
1516       // Move data before SOURCE down (may be below 0 / wrap)
1517       InternalMoveDown(PByte(GetItemPointer(0)), PByte(GetItemPointer(Cap-Diff)),
1518                        AFromIndex * FItemSize.ItemSize, u);
1519       c := FMem.FirstItemIndex - Diff;
1520       if c < 0 then
1521         c := Cap - Cardinal(-c);
1522       FMem.FirstItemIndex := c;
1523     end;
1524   end
1525   else begin
1526     // normal move
1527     BytesToMove := FItemSize.ItemSize * ACount;
1528     if AFromIndex > AToIndex then begin
1529       pFrom := PByte(GetItemPointer(AFromIndex));
1530       pTo   := PByte(GetItemPointer(AToIndex));
1531       InternalMoveDown(pFrom, pTo, BytesToMove, FMem.DataPointer + CapBytes)
1532     end
1533     else begin
1534       pFrom := PByte(GetItemPointer(AFromIndex+ACount));
1535       pTo   := PByte(GetItemPointer(AToIndex+ACount));
1536       InternalMoveUp(pFrom, pTo, BytesToMove, CapBytes);
1537     end;
1538   end;
1539   assert((FMem.FirstItemIndex >= 0) and (FMem.FirstItemIndex < Capacity), 'TLazRoundBufferListObjBase.MoveRows: (FMem.FirstItemIndex >= 0) and (FMem.FirstItemIndex < Capacity)');
1540 end;
1541 
1542 procedure TLazRoundBufferListObjBase.SwapEntries(AIndex1, AIndex2: Integer);
1543 var
1544   t: PByte;
1545 begin
1546   t := Getmem(FItemSize.ItemSize);
1547   Move(PByte(GetItemPointer(AIndex1))^, t^, FItemSize.ItemSize);
1548   Move(PByte(GetItemPointer(AIndex2))^, PByte(GetItemPointer(AIndex1))^, FItemSize.ItemSize);
1549   Move(t^, PByte(GetItemPointer(AIndex2))^, FItemSize.ItemSize);
1550   FreeMem(t);
1551 end;
1552 
1553 procedure TLazRoundBufferListObjBase.DebugDump;
1554 var i , c: integer; s:string;
1555 begin
1556   if fmem.IsAllocated then begin
1557     dbgout(['TLazRoundBufferListObjBase.Dump ', FMem.Capacity, ' , ',FMem.Count,
1558     ' --- ', fmem.datapointer, ' , ',FMem.FirstItemIndex,' --- ', ': ']);
1559      s :='';
1560     c := FMem.Count;
1561     for i := 0 to FMem.Capacity - 1 do begin
1562       if i = c then s := s + '# ';
1563       s := s +dbgMemRange(itempointer[i], FItemSize.ItemSize )+ ', ';
1564     end;
1565     debugln(s);
1566   end
1567   else debugln(['TLazRoundBufferListObjBase.Dump NONE']);
1568 end;
1569 
1570 { TLazRoundBufferListObj }
1571 
1572 procedure TLazRoundBufferListObj.Create(AnItemSize: Integer);
1573 begin
1574   FItemSize.ItemSize := AnItemSize;
1575   inherited Create;
1576 end;
1577 
1578 { TLazRoundBufferListObjGen }
1579 
GetCapacitynull1580 function TLazRoundBufferListObjGen.GetCapacity: Integer;
1581 begin
1582   Result := FList.GetCapacity;
1583 end;
1584 
GetCountnull1585 function TLazRoundBufferListObjGen.GetCount: Integer;
1586 begin
1587   Result := FList.GetCount;
1588 end;
1589 
TLazRoundBufferListObjGen.GetItemPointernull1590 function TLazRoundBufferListObjGen.GetItemPointer(Index: Integer): PT;
1591 begin
1592   Result := FList.GetItemPointer(Index);
1593 end;
1594 
TLazRoundBufferListObjGen.GetItemPointerFastnull1595 function TLazRoundBufferListObjGen.GetItemPointerFast(Index: Integer): PT;
1596 begin
1597   Result := FList.GetItemPointer(Index);
1598 end;
1599 
1600 procedure TLazRoundBufferListObjGen.SetCapacity(AValue: Integer);
1601 begin
1602   FList.SetCapacity(AValue);
1603 end;
1604 
InsertRowsExnull1605 function TLazRoundBufferListObjGen.InsertRowsEx(AIndex, ACount: Integer;
1606   AGrowProc: TLazStorageMemGrowProc): PT;
1607 begin
1608   Result := FList.InsertRowsEx(AIndex, ACount, AGrowProc);
1609 end;
1610 
1611 procedure TLazRoundBufferListObjGen.DeleteRowsEx(AIndex, ACount: Integer;
1612   AShrinkProc: TLazStorageMemShrinkProc);
1613 begin
1614   FList.DeleteRowsEx(AIndex, ACount, AShrinkProc);
1615 end;
1616 
1617 procedure TLazRoundBufferListObjGen.Create;
1618 begin
1619   FList.Create;
1620 end;
1621 
1622 procedure TLazRoundBufferListObjGen.Destroy;
1623 begin
1624   FList.Destroy;
1625 end;
1626 
InsertRowsnull1627 function TLazRoundBufferListObjGen.InsertRows(AIndex, ACount: Integer): PT;
1628 begin
1629   Result := FList.InsertRows(AIndex, ACount);
1630 end;
1631 
1632 procedure TLazRoundBufferListObjGen.DeleteRows(AIndex, ACount: Integer);
1633 begin
1634   FList.DeleteRows(AIndex, ACount);
1635 end;
1636 
1637 procedure TLazRoundBufferListObjGen.MoveRows(AFromIndex, AToIndex, ACount: Integer);
1638 begin
1639   FList.MoveRows(AFromIndex, AToIndex, ACount);
1640 end;
1641 
1642 procedure TLazRoundBufferListObjGen.SwapEntries(AIndex1, AIndex2: Integer);
1643 begin
1644   FList.SwapEntries(AIndex1, AIndex2);
1645 end;
1646 
1647 procedure TLazRoundBufferListObjGen.DebugDump;
1648 begin
1649   FList.DebugDump;
1650 end;
1651 
Getnull1652 function TLazRoundBufferListObjGen.Get(Index: Integer): T;
1653 begin
1654   Result := FList.ItemPointer[Index]^;
1655 end;
1656 
1657 procedure TLazRoundBufferListObjGen.Put(Index: Integer; AValue: T);
1658 begin
1659   FList.ItemPointer[Index]^ := AValue;
1660 end;
1661 
TLazRoundBufferListObjGen.IndexOfnull1662 function TLazRoundBufferListObjGen.IndexOf(AnItem: T): integer;
1663 var
1664   p: PT;
1665 begin
1666   Result := Count - 1;
1667   p := ItemPointer[Result];
1668   while Result >= 0 do begin
1669     if p^ = AnItem then exit;
1670     dec(p);
1671     dec(Result);
1672   end;
1673 end;
1674 
1675 { TLazFixedRoundBufferListMemBase }
1676 
TLazFixedRoundBufferListMemBase.GrowCapacitynull1677 function TLazFixedRoundBufferListMemBase.GrowCapacity(ARequired: Integer): Integer;
1678 begin
1679   assert(False, 'TLazFixedRoundBufferListMemBase.GrowCapacity: False');
1680   Result := Min(ARequired * 2, ARequired + $8000);
1681 end;
1682 
ShrinkCapacitynull1683 function TLazFixedRoundBufferListMemBase.ShrinkCapacity(ARequired: Integer): Integer;
1684 begin
1685   Result := -1;
1686 end;
1687 
1688 procedure TLazFixedRoundBufferListMemBase.AdjustFirstItemOffset(ACount, AMask: Integer);
1689 begin
1690   Mem.FirstItemIndex := (Mem.FirstItemIndex + ACount) and AMask;
1691 end;
1692 
1693 procedure TLazFixedRoundBufferListMemBase.InsertRowsAtStart(ACount, AMask: Integer);
1694 begin
1695   Mem.FirstItemIndex := (Mem.FirstItemIndex - ACount) and AMask;
1696   Mem.Count := Mem.Count + ACount;
1697 end;
1698 
1699 procedure TLazFixedRoundBufferListMemBase.InsertRowsAtEnd(ACount: Integer);
1700 begin
1701   Mem.Count := Mem.Count + ACount;
1702 end;
1703 
1704 procedure TLazFixedRoundBufferListMemBase.InsertRowsAtBoundary(AnAtStart: Boolean; ACount,
1705   AMask: Integer);
1706 begin
1707   if AnAtStart then begin
1708     Mem.FirstItemIndex := (Mem.FirstItemIndex - ACount) and AMask;
1709   end;
1710   Mem.Count := Mem.Count + ACount;
1711 end;
1712 
1713 procedure TLazFixedRoundBufferListMemBase.DeleteRowsAtStart(ACount, AMask: Integer);
1714 begin
1715   Mem.FirstItemIndex := (Mem.FirstItemIndex + ACount) and AMask;
1716   Mem.Count := Mem.Count - ACount;
1717 end;
1718 
1719 procedure TLazFixedRoundBufferListMemBase.DeleteRowsAtEnd(ACount: Integer);
1720 begin
1721   Mem.Count := Mem.Count - ACount;
1722 end;
1723 
1724 procedure TLazFixedRoundBufferListMemBase.DeleteRowsAtBoundary(AnAtStart: Boolean; ACount,
1725   AMask: Integer);
1726 begin
1727   if AnAtStart then begin
1728     Mem.FirstItemIndex := (Mem.FirstItemIndex + ACount) and AMask;
1729   end;
1730   Mem.Count := Mem.Count - ACount;
1731 end;
1732 
1733 procedure TLazFixedRoundBufferListMemBase.MoveRowsToOther(AFromOffset, AToOffset, ACount, ACap: Integer; AnOther: TLazFixedRoundBufferListMemBase);
1734 begin
1735   MoveBytesToOther(
1736     ((AFromOffset + Mem.FirstItemIndex) and (ACap-1)) * FItemSize.ItemSize,
1737     ((AToOffset + AnOther.Mem.FirstItemIndex) and (ACap-1)) * FItemSize.ItemSize,
1738     ACount * FItemSize.ItemSize,
1739     ACap * FItemSize.ItemSize,
1740     AnOther);
1741 end;
1742 
1743 procedure TLazFixedRoundBufferListMemBase.MoveBytesToOther(AFromByteOffset, AToByteOffset,
1744   AByteCount, AByteCap: Integer; AnOther: TLazFixedRoundBufferListMemBase);
1745 var
1746   CSrc, CDst: Integer;
1747   PSource, PTarget, SrcHigh, DstHigh: PByte;
1748 begin
1749   PSource := FMem.DataPointer;
1750   SrcHigh := PSource + AByteCap;
1751   PSource := PSource + AFromByteOffset;
1752   assert(PSource < SrcHigh, 'TLazFixedRoundBufferListMemBase.MoveBytesToOther: PSource < SrcHigh');
1753   //if PSource >= SrcHigh then PSource := PSource - AByteCap;
1754 
1755   PTarget := AnOther.FMem.DataPointer;
1756   DstHigh := PTarget + AByteCap;
1757   PTarget := PTarget + AToByteOffset;
1758   assert(PTarget < DstHigh, 'TLazFixedRoundBufferListMemBase.MoveBytesToOther: PTarget < DstHigh');
1759   //if PTarget >= DstHigh then PTarget := PTarget - AByteCap;
1760 
1761   CSrc := SrcHigh - PSource;
1762   CDst := DstHigh - PTarget;
1763 
1764   if CSrc > CDst then begin
1765     CDst := Min(CDst, AByteCount);
1766     Move(PSource^, PTarget^, CDst);
1767     AByteCount := AByteCount - CDst;
1768     if AByteCount = 0 then exit;
1769     PTarget := AnOther.FMem.DataPointer;
1770     PSource := PSource + CDst;
1771 
1772     CSrc := Min(SrcHigh - PSource, AByteCount);
1773     Move(PSource^, PTarget^, CSrc);
1774     AByteCount := AByteCount - CSrc;
1775     if AByteCount = 0 then exit;
1776     PSource := FMem.DataPointer;
1777     PTarget := PTarget + CSrc;
1778     Move(PSource^, PTarget^, AByteCount);
1779   end
1780   else if CSrc = CDst then begin
1781     CSrc := Min(CSrc, AByteCount);
1782     Move(PSource^, PTarget^, CSrc);
1783     AByteCount := AByteCount - CSrc;
1784     if AByteCount = 0 then exit;
1785     PSource := FMem.DataPointer;
1786     PTarget := AnOther.FMem.DataPointer;
1787     Move(PSource^, PTarget^, AByteCount);
1788   end
1789   else begin
1790     CSrc := Min(CSrc, AByteCount);
1791     Move(PSource^, PTarget^, CSrc);
1792     AByteCount := AByteCount - CSrc;
1793     if AByteCount = 0 then exit;
1794     PSource := FMem.DataPointer;
1795     PTarget := PTarget + CSrc;
1796 
1797     CDst := Min(DstHigh - PTarget, AByteCount);
1798     Move(PSource^, PTarget^, CDst);
1799     AByteCount := AByteCount - CDst;
1800     if AByteCount = 0 then exit;
1801     PTarget := AnOther.FMem.DataPointer;
1802     PSource := PSource + CDst;
1803     Move(PSource^, PTarget^, AByteCount);
1804   end;
1805 end;
1806 
TLazFixedRoundBufferListMemBase.GetItemPointerMaskednull1807 function TLazFixedRoundBufferListMemBase.GetItemPointerMasked(AnIndex, AMask: Integer): TPItemT;
1808 begin
1809   Result := TPItemT(
1810     FMem.DataPointer + ((AnIndex + Mem.FirstItemIndex) and AMask) * FItemSize.ItemSize
1811   );
1812 end;
1813 
GetItemByteOffsetMaskednull1814 function TLazFixedRoundBufferListMemBase.GetItemByteOffsetMasked(AnIndex,
1815   AMask: Integer): Integer;
1816 begin
1817   Result := ((AnIndex + Mem.FirstItemIndex) and AMask) * FItemSize.ItemSize;
1818 end;
1819 
GetFirstItemByteOffsetnull1820 function TLazFixedRoundBufferListMemBase.GetFirstItemByteOffset: Integer;
1821 begin
1822   Result := Mem.FirstItemIndex * FItemSize.ItemSize;
1823 end;
1824 
1825 procedure TLazFixedRoundBufferListMemBase.Create(AItemSize: TSizeT; ACapacity: Integer);
1826 begin
1827   inherited Create;
1828   FItemSize := AItemSize;
1829   SetCapacity(ACapacity);
1830 end;
1831 
InsertRowsnull1832 function TLazFixedRoundBufferListMemBase.InsertRows(AIndex, ACount: Integer): TPItemT;
1833 begin
1834   Result := InsertRowsEx(AIndex, ACount, @GrowCapacity);
1835 end;
1836 
1837 procedure TLazFixedRoundBufferListMemBase.DeleteRows(AIndex, ACount: Integer);
1838 begin
1839   DeleteRowsEx(AIndex, ACount, @ShrinkCapacity);
1840 end;
1841 
1842 { TLazPagedListObjBase }
1843 
TLazPagedListObjBase.GetPageSubIdxnull1844 function TLazPagedListObjBase.GetPageSubIdx(Index: Integer): Integer;
1845 begin
1846   Result := Cardinal(Index+FFirstPageEmpty) and FPageSizeMask;
1847 end;
1848 
TLazPagedListObjBase.GetPagePointernull1849 function TLazPagedListObjBase.GetPagePointer(PageIndex: Integer): PPageType;
1850 begin
1851   assert((PageIndex >= 0) and (PageIndex < PageCount), 'TLazPagedListObjBase.GetPageSubIdx: (PageIndex >= 0) and (PageIndex < PageCount)');
1852   Result := FPages.ItemPointerFast[PageIndex];
1853 end;
1854 
GetItemPageIdxnull1855 function TLazPagedListObjBase.GetItemPageIdx(Index: Integer): Integer;
1856 begin
1857   Result := (Index+FFirstPageEmpty) >> FPageSizeExp;
1858 end;
1859 
TLazPagedListObjBase.GetItemPointernull1860 function TLazPagedListObjBase.GetItemPointer(Index: Integer): TPItemT;
1861 var
1862   p: PPageType;
1863   i: Integer;
1864 begin
1865   assert((Index>=0) and (Index<FCount), 'TLazPagedListObjBase.GetItemPointer: (Index>=0) and (Index<FCount)');
1866   i := Index + FFirstPageEmpty;
1867   p := FPages.ItemPointerFast[i >> FPageSizeExp];
1868   assert(p<>nil, 'TLazPagedListObjBase.GetItemPointer: p<>nil');
1869   Result := p^.ItemPointerMasked[Cardinal(i), FPageSizeMask];
1870 end;
1871 
1872 procedure TLazPagedListObjBase.SetCapacity(AValue: Integer);
1873 begin
1874   if AValue < 0 then
1875     AValue := 0;
1876   AValue := (AValue + FPageSizeMask) >> FPageSizeExp;
1877   if AValue <= FPages.Count then
1878     exit;
1879   FPages.Capacity := AValue;
1880 end;
1881 
TLazPagedListObjBase.GetCapacitynull1882 function TLazPagedListObjBase.GetCapacity: Integer;
1883 begin
1884   Result := FPages.Capacity << FPageSizeExp;
1885 end;
1886 
GetPageCountnull1887 function TLazPagedListObjBase.GetPageCount: Integer;
1888 begin
1889   Result := FPages.Count;
1890 end;
1891 
1892 procedure TLazPagedListObjBase.JoinPageWithNext(APageIdx, AJoinEntryIdx,
1893   AnExtraDelPages: Integer);
1894 var
1895   PCap: Integer;
1896 begin
1897   // delete last page(s), if AJoinEntryIdx=0 // next page does not need to exist
1898   PCap := FPageSizeMask + 1;
1899   if AJoinEntryIdx * 2 <= FPageSizeMask then begin
1900     if AJoinEntryIdx > 0 then
1901       PagePointer[APageIdx]^.MoveRowsToOther(0, 0,
1902         AJoinEntryIdx, PCap, PagePointer[APageIdx + 1 + AnExtraDelPages]^);
1903     DeletePages(APageIdx, 1 + AnExtraDelPages);
1904   end
1905   else begin
1906     PagePointer[APageIdx + 1 + AnExtraDelPages]^.MoveRowsToOther(AJoinEntryIdx, AJoinEntryIdx,
1907       PCap - AJoinEntryIdx, PCap, PagePointer[APageIdx]^);
1908     DeletePages(APageIdx + 1, 1 + AnExtraDelPages);
1909   end;
1910 end;
1911 
1912 procedure TLazPagedListObjBase.SplitPageToFront(ASourcePageIdx, ASplitAtIdx,
1913   AnExtraPages: Integer; AExtraCapacityNeeded: Integer);
1914 begin
1915   // Can split the none-existing page[pagecount], IF ASplitAtIdx=0 // simply insert a page
1916   InsertFilledPages(ASourcePageIdx, AnExtraPages+1, AExtraCapacityNeeded);
1917   if ASplitAtIdx > 0 then
1918     PagePointer[ASourcePageIdx + AnExtraPages + 1]^.MoveRowsToOther(0, 0, ASplitAtIdx, FPageSizeMask+1, PagePointer[ASourcePageIdx]^);
1919 end;
1920 
1921 procedure TLazPagedListObjBase.SplitPageToBack(ASourcePageIdx, ASplitAtIdx,
1922   AnExtraPages: Integer; AExtraCapacityNeeded: Integer);
1923 var
1924   c: Integer;
1925 begin
1926   InsertFilledPages(ASourcePageIdx+1, AnExtraPages+1, AExtraCapacityNeeded);
1927   c := FPageSizeMask + 1 - ASplitAtIdx;
1928   if c > 0 then
1929     PagePointer[ASourcePageIdx]^.MoveRowsToOther(ASplitAtIdx, ASplitAtIdx, c, FPageSizeMask+1, PagePointer[ASourcePageIdx + AnExtraPages + 1]^);
1930 end;
1931 
1932 procedure TLazPagedListObjBase.SplitPage(ASourcePageIdx, ASplitAtIdx, AnExtraPages: Integer;
1933   AExtraCapacityNeeded: Integer);
1934 begin
1935   if ASplitAtIdx <= FPageSizeMask >> 1 then
1936     SplitPageToFront(ASourcePageIdx, ASplitAtIdx, AnExtraPages, AExtraCapacityNeeded)
1937   else
1938     SplitPageToBack(ASourcePageIdx, ASplitAtIdx, AnExtraPages, AExtraCapacityNeeded);
1939 end;
1940 
1941 procedure TLazPagedListObjBase.BubbleEntriesDown(ASourceStartIdx, ATargetEndIdx,
1942   AnEntryCount: Integer);
1943 var
1944   HighPage, LowPage: PPageType;
1945   ReverseEntryCount, PageCapacity: Integer;
1946 begin
1947   PageCapacity := FPageSizeMask + 1;
1948     (*   *** Reverse Bubble
1949 
1950     bubble 900 down
1951       |                    |                    |                    |                    |
1952        AA------------------ xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxBB
1953        xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxBB AA------------------   // page up
1954        xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxBB ------------------AA   // adjust (rotated page)
1955          xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx BB----------------AA   // bubbled 100 up // and adjust
1956        AAxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx BB----------------AA   // moved/restored
1957        AAxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx ------------------BB   // adjust (rotated page)
1958 
1959     *)
1960   if AnEntryCount*2 > PageCapacity then begin
1961     ReverseEntryCount := PageCapacity - AnEntryCount;
1962     SwapPagesUp(ATargetEndIdx, ASourceStartIdx, ASourceStartIdx);      // swapped
1963     HighPage := PagePointer[ASourceStartIdx];
1964     LowPage := PagePointer[ATargetEndIdx];
1965     HighPage^.AdjustFirstItemOffset(ReverseEntryCount, FPageSizeMask); // rotate
1966     InternalBubbleEntriesUp(ATargetEndIdx, ASourceStartIdx, ReverseEntryCount); // bubble
1967     LowPage^.AdjustFirstItemOffset(-ReverseEntryCount, FPageSizeMask); // adjust after bubble
1968     HighPage^.MoveRowsToOther(AnEntryCount, 0, ReverseEntryCount, PageCapacity, LowPage^); // moved/restored
1969     // TODO: the caller may just undo this last rotate....
1970     HighPage^.AdjustFirstItemOffset(ReverseEntryCount, FPageSizeMask); // rotated page
1971   end
1972   else
1973     InternalBubbleEntriesDown(ASourceStartIdx, ATargetEndIdx, AnEntryCount);
1974 end;
1975 
1976 procedure TLazPagedListObjBase.BubbleEntriesUp(ASourceStartIdx, ATargetEndIdx,
1977   AnEntryCount: Integer);
1978 var
1979   LowPage, HighPage: PPageType;
1980   ReverseEntryCount, PageCapacity: Integer;
1981 begin
1982   PageCapacity := FPageSizeMask + 1;
1983   if AnEntryCount*2 > PageCapacity then begin
1984     (*   *** Reverse Bubble
1985     l: Sourcte
1986     T: Target  // E: Target end
1987 
1988     bubble 900 up
1989       |  l                 |T                E  |                    |                    |
1990        AAxxxxxxxxxxxxxxxxxx ------------------BB
1991        ------------------BB AAxxxxxxxxxxxxxxxxxx     // page down
1992        BB------------------ AAxxxxxxxxxxxxxxxxxx     // adjust (rotated page)
1993        BB----------------AA xxxxxxxxxxxxxxxxxx       // bubbled 100 down // and adjust
1994        ------------------AA xxxxxxxxxxxxxxxxxxBB     // moved/restored
1995        AA------------------ xxxxxxxxxxxxxxxxxxBB     // adjust (rotated page)
1996 
1997     bubble 900 up
1998       |  l                 |T                   |                 E  |                    |
1999        AAxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx ------------------BB
2000        ------------------BB AAxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx   // 1 page down: E > l
2001        BB------------------ AAxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx   // adjust (rotated page)
2002        BB----------------AA xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx     // bubbled 100 down // and adjust
2003        ------------------AA xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxBB   // moved/restored
2004        AA------------------ xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxBB   // adjust (rotated page)
2005 
2006     *)
2007     ReverseEntryCount := PageCapacity - AnEntryCount;
2008     SwapPagesDown(ATargetEndIdx, ASourceStartIdx, ASourceStartIdx);     // swapped
2009     LowPage := PagePointer[ASourceStartIdx];
2010     HighPage := PagePointer[ATargetEndIdx];
2011     LowPage^.AdjustFirstItemOffset(-ReverseEntryCount, FPageSizeMask); // rotate page
2012     InternalBubbleEntriesDown(ATargetEndIdx, ASourceStartIdx, ReverseEntryCount); // bubble
2013     HighPage^.AdjustFirstItemOffset(ReverseEntryCount, FPageSizeMask); // adjust after bubble
2014     LowPage^.MoveRowsToOther(0, AnEntryCount, ReverseEntryCount, PageCapacity, HighPage^); // moved/restored
2015     // TODO: the caller may just undo this last rotate....
2016     LowPage^.AdjustFirstItemOffset(-ReverseEntryCount, FPageSizeMask); // rotate page
2017   end
2018   else
2019     InternalBubbleEntriesUp(ASourceStartIdx, ATargetEndIdx, AnEntryCount);
2020 end;
2021 
2022 procedure TLazPagedListObjBase.InternalBubbleEntriesDown(ASourceStartIdx, ATargetEndIdx,
2023   AnEntryCount: Integer);
2024 var
2025   CurPage, NextPage: PPageType;
2026   i, PageCapacity, PageCapacityBytes, CountBytes: Integer;
2027 begin
2028   assert(ASourceStartIdx > ATargetEndIdx, 'TLazPagedListObjBase.InternalBubbleEntriesDown: ASourceStartIdx > ATargetEndIdx');
2029 
2030   PageCapacity := (FPageSizeMask+1);
2031   PageCapacityBytes := PageCapacity * FItemSize.ItemSize;
2032   CountBytes := AnEntryCount * FItemSize.ItemSize;
2033 
2034   CurPage := PagePointer[ATargetEndIdx];
2035   NextPage := CurPage + 1;
2036   NextPage^.MoveBytesToOther(NextPage^.GetFirstItemByteOffset,
2037     CurPage^.GetItemByteOffsetMasked(CurPage^.Count - AnEntryCount, FPageSizeMask),
2038     CountBytes, PageCapacityBytes, CurPage^);
2039   if ASourceStartIdx - ATargetEndIdx = 1 then
2040     exit;
2041   NextPage^.AdjustFirstItemOffset(AnEntryCount, FPageSizeMask);
2042 
2043   CurPage := NextPage;
2044   for i := 0 to ASourceStartIdx - ATargetEndIdx - 3 do begin
2045     NextPage := CurPage + 1;
2046     NextPage^.MoveBytesToOther(NextPage^.GetFirstItemByteOffset,
2047       CurPage^.GetItemByteOffsetMasked(PageCapacity - AnEntryCount, FPageSizeMask),
2048       CountBytes, PageCapacityBytes, CurPage^);
2049     NextPage^.AdjustFirstItemOffset(AnEntryCount, FPageSizeMask);
2050     CurPage := NextPage;
2051   end;
2052 
2053   NextPage := CurPage + 1;
2054   NextPage^.MoveBytesToOther(NextPage^.GetFirstItemByteOffset,
2055     CurPage^.GetItemByteOffsetMasked(PageCapacity - AnEntryCount, FPageSizeMask),
2056     CountBytes, PageCapacityBytes, CurPage^);
2057 end;
2058 
2059 procedure TLazPagedListObjBase.InternalBubbleEntriesUp(ASourceStartIdx, ATargetEndIdx,
2060   AnEntryCount: Integer);
2061 var
2062   CurPage, NextPage: PPageType;
2063   i, PageCapacity, PageCapacityBytes, CountBytes: Integer;
2064 begin
2065   assert(ASourceStartIdx < ATargetEndIdx, 'TLazPagedListObjBase.InternalBubbleEntriesUp: ASourceStartIdx < ATargetEndIdx');
2066 
2067   PageCapacity := (FPageSizeMask+1);
2068   PageCapacityBytes := PageCapacity * FItemSize.ItemSize;
2069   CountBytes := AnEntryCount * FItemSize.ItemSize;
2070 
2071   CurPage := PagePointer[ATargetEndIdx];
2072   for i := 0 to ATargetEndIdx - ASourceStartIdx - 2 do begin
2073     NextPage := CurPage - 1;
2074     NextPage^.MoveBytesToOther(NextPage^.GetItemByteOffsetMasked(PageCapacity - AnEntryCount, FPageSizeMask),
2075       CurPage^.GetFirstItemByteOffset,
2076       CountBytes, PageCapacityBytes, CurPage^);
2077     NextPage^.AdjustFirstItemOffset(-AnEntryCount, FPageSizeMask);
2078     CurPage := NextPage;
2079   end;
2080 
2081   NextPage := CurPage - 1;
2082   NextPage^.MoveBytesToOther(NextPage^.GetItemByteOffsetMasked(NextPage^.Count - AnEntryCount, FPageSizeMask),
2083     CurPage^.GetFirstItemByteOffset,
2084     CountBytes, PageCapacityBytes, CurPage^);
2085 end;
2086 
2087 procedure TLazPagedListObjBase.SwapPagesUp(ASourceStartIndex, ATargetStartIndex,
2088   ATargetEndIndex: Integer);
2089 var
2090   Cnt, Diff: Integer;
2091   TempPages: Array of TPageType;
2092 begin
2093   Cnt := ATargetEndIndex - ATargetStartIndex + 1;
2094   Diff := ATargetStartIndex - ASourceStartIndex;
2095   assert(Diff > 0, 'TLazPagedListObjBase.MoveRows: Diff > 0');
2096 
2097   if Diff > Cnt then begin
2098     SetLength(TempPages, Cnt);
2099     move(PagePointer[ASourceStartIndex]^, TempPages[0], Cnt * SizeOf(TempPages[0]));
2100     FPages.MoveRows(ASourceStartIndex + Cnt, ASourceStartIndex, Diff);
2101     move(TempPages[0], PagePointer[ATargetStartIndex]^, Cnt * SizeOf(TempPages[0]));
2102   end else begin
2103     SetLength(TempPages, Diff);
2104     move(PagePointer[ATargetEndIndex - Diff + 1]^, TempPages[0], Diff * SizeOf(TempPages[0]));
2105     FPages.MoveRows(ASourceStartIndex, ATargetStartIndex, Cnt);
2106     move(TempPages[0], PagePointer[ASourceStartIndex]^, Diff * SizeOf(TempPages[0]));
2107   end;
2108 end;
2109 
2110 procedure TLazPagedListObjBase.SwapPagesDown(ASourceStartIndex, ATargetStartIndex,
2111   ATargetEndIndex: Integer);
2112 var
2113   Cnt, Diff: Integer;
2114   TempPages: Array of TPageType;
2115 begin
2116   Cnt := ATargetEndIndex - ATargetStartIndex + 1;
2117   Diff := ASourceStartIndex - ATargetStartIndex;
2118   assert(Diff > 0, 'TLazPagedListObjBase.MoveRows: Diff > 0');
2119 
2120   if Diff > Cnt then begin
2121     SetLength(TempPages, Cnt);
2122     move(PagePointer[ASourceStartIndex]^, TempPages[0], Cnt * SizeOf(TempPages[0]));
2123     FPages.MoveRows(ATargetStartIndex, ATargetStartIndex + Cnt, Diff);
2124     move(TempPages[0], PagePointer[ATargetStartIndex]^, Cnt * SizeOf(TempPages[0]));
2125   end else begin
2126     SetLength(TempPages, Diff);
2127     move(PagePointer[ATargetStartIndex]^, TempPages[0], Diff * SizeOf(TempPages[0]));
2128     FPages.MoveRows(ASourceStartIndex, ATargetStartIndex, Cnt);
2129     move(TempPages[0], PagePointer[ASourceStartIndex + Cnt - Diff]^, Diff * SizeOf(TempPages[0]));
2130   end;
2131 end;
2132 
TLazPagedListObjBase.GrowCapacitynull2133 function TLazPagedListObjBase.GrowCapacity(ARequiredPages: Integer): Integer;
2134 begin
2135   ARequiredPages := ARequiredPages + FExtraCapacityNeeded;
2136   FExtraCapacityNeeded := 0;
2137   if assigned(FGrowProc) then
2138     Result := FGrowProc(ARequiredPages)
2139   else
2140     Result := Min(ARequiredPages * 2, ARequiredPages + $8000);
2141 end;
2142 
ShrinkCapacitynull2143 function TLazPagedListObjBase.ShrinkCapacity(ARequiredPages: Integer): Integer;
2144 begin
2145   assert(ARequiredPages <= FPages.Capacity, 'TLazShiftBufferListObjBase.ShrinkCapacity: ARequired <= FPages.Capacity');
2146   if assigned(FShrinkProc) then
2147     Result := FShrinkProc(ARequiredPages)
2148   else
2149   if ARequiredPages * 4 < FPages.Capacity then
2150     Result := ARequiredPages * 2
2151   else
2152     Result := -1;
2153 end;
2154 
2155 procedure TLazPagedListObjBase.InsertFilledPages(AIndex, ACount: Integer;
2156   AExtraCapacityNeeded: Integer);
2157 var
2158   i, c, h: Integer;
2159   p: PPageType;
2160 begin
2161   FExtraCapacityNeeded := AExtraCapacityNeeded;
2162   p := FPages.InsertRowsEx(AIndex, ACount, @GrowCapacity);
2163   c := FPageSizeMask + 1;
2164   h := AIndex + ACount - 1;
2165   for i := AIndex to h do begin
2166     p^.Create(FItemSize, c);
2167     p^.InsertRowsAtEnd(c);
2168     inc(p);
2169   end;
2170 end;
2171 
2172 procedure TLazPagedListObjBase.DeletePages(AIndex, ACount: Integer);
2173 var
2174   i: Integer;
2175   p: PPageType;
2176 begin
2177   p := PagePointer[AIndex]; // pages are NOT a roundbuffer
2178   for i := AIndex to AIndex + ACount - 1 do begin
2179     //PagePointer[i]^.Destroy;
2180     p^.Destroy;
2181     inc(p);
2182   end;
2183   FPages.DeleteRowsEx(AIndex, ACount, @ShrinkCapacity);
2184 end;
2185 
2186 procedure TLazPagedListObjBase.Create(APageSizeExp: Integer);
2187 begin
2188   FPageSizeExp := APageSizeExp;
2189   FPageSizeMask := Integer(not(Cardinal(-1) << FPageSizeExp));
2190   FCount := 0;
2191   FFirstPageEmpty := 0;
2192   FPages.Create;
2193   FGrowProc := nil;
2194   FShrinkProc := nil;
2195 end;
2196 
2197 procedure TLazPagedListObjBase.Destroy;
2198 begin
2199   if FCount > 0 then
2200     DeletePages(0, PageCount);
2201   FPages.Destroy;
2202 end;
2203 
2204 procedure TLazPagedListObjBase.InsertRows(AIndex, ACount: Integer);
2205 var
2206   ExtraPagesNeeded, SubIndex, SubCount: Integer;
2207   PgCnt, PCap, InsertPageIdx, c,
2208     AIndexAdj, TmpDeleteRows, LastPageEmpty: Integer;
2209 begin
2210   assert((AIndex >= 0) and (AIndex <= FCount), 'TLazPagedListObjBase.InsertRows: (AIndex >= 0) and (AIndex <= FCount)');
2211   if ACount <= 0 then
2212     exit;
2213   PCap := FPageSizeMask + 1;
2214   PgCnt := PageCount;
2215   FCount := FCount + ACount;
2216   SubCount := ACount and FPageSizeMask;
2217 
2218 //DebugLn();debugln(['***### TLazPagedListObjBase.InsertRows Idx:',AIndex,' cnt:',ACount, ' Pcnt:',PgCnt, ' fcnt:', FCount, '  FFirstPageEmpty:', FFirstPageEmpty]);DebugDump;
2219   if PgCnt = 0 then begin
2220     // No data yet
2221     ExtraPagesNeeded := ((ACount-1) >> FPageSizeExp) + 1;
2222     InsertFilledPages(0, ExtraPagesNeeded);
2223     FFirstPageEmpty := (PCap - SubCount) and FPageSizeMask;
2224     FFirstPageEmpty := FFirstPageEmpty div 2; // keep some capacity in the last node too
2225   assert((((FPages.Count << FPageSizeExp)-FFirstPageEmpty-FCount)<=FPageSizeMask) and (((FPages.Count << FPageSizeExp)-FFirstPageEmpty-FCount)>=0), 'TLazPagedListObjBase.InsertRows: (((FPages.Count << FPageSizeExp)-FFirstPageEmpty-FCount)<=FPageSizeMask) and (((FPages.Count << FPageSizeExp)-FFirstPageEmpty-FCount)>0)');
2226     exit;
2227   end
2228   else if (FCount <= PCap) and (PgCnt = 1) then begin
2229     // keep it in one page
2230     FFirstPageEmpty := FFirstPageEmpty - SubCount;
2231     if FFirstPageEmpty < 0 then begin
2232       PagePointer[0]^.AdjustFirstItemOffset(FFirstPageEmpty, FPageSizeMask);
2233       FFirstPageEmpty := 0;
2234     end;
2235     if AIndex > 0 then
2236       MoveRows(SubCount, 0, AIndex);
2237   assert((((FPages.Count << FPageSizeExp)-FFirstPageEmpty-FCount)<=FPageSizeMask) and (((FPages.Count << FPageSizeExp)-FFirstPageEmpty-FCount)>=0), 'TLazPagedListObjBase.InsertRows: (((FPages.Count << FPageSizeExp)-FFirstPageEmpty-FCount)<=FPageSizeMask) and (((FPages.Count << FPageSizeExp)-FFirstPageEmpty-FCount)>0)');
2238     exit;
2239   end;
2240 
2241   AIndexAdj := AIndex + FFirstPageEmpty;
2242   InsertPageIdx := (AIndexAdj) >> FPageSizeExp;
2243   SubIndex := AIndexAdj and FPageSizeMask;
2244 
2245   ExtraPagesNeeded := ACount - SubCount;
2246   if (ExtraPagesNeeded > 0) then
2247     ExtraPagesNeeded := ExtraPagesNeeded >> FPageSizeExp;
2248 
2249   If Cardinal(InsertPageIdx) * 2 <= Cardinal(PgCnt) then begin
2250     if SubCount * 2 <= PCap then begin
2251       if SubCount > 0 then begin
2252         FFirstPageEmpty := FFirstPageEmpty - SubCount;
2253         if FFirstPageEmpty < 0 then begin
2254           InsertFilledPages(0, 1);
2255           FFirstPageEmpty := FFirstPageEmpty + PCap;
2256           inc(InsertPageIdx);
2257           assert(FFirstPageEmpty>0, 'TLazPagedListObjBase.InsertRows: FFirstPageEmpty>0');
2258         end;
2259         if AIndex > 0 then
2260           MoveRows(SubCount, 0, AIndex);
2261       end;
2262       if ExtraPagesNeeded > 0 then
2263         SplitPage(InsertPageIdx, SubIndex, ExtraPagesNeeded - 1);
2264     end
2265     else begin
2266       SplitPage(InsertPageIdx, SubIndex, ExtraPagesNeeded);
2267       TmpDeleteRows := PCap - SubCount;
2268       assert(TmpDeleteRows>0, 'TLazPagedListObjBase.InsertRows: TmpDeleteRows>0');
2269       if AIndex > 0 then
2270         MoveRows(0, TmpDeleteRows, AIndex);
2271       FFirstPageEmpty := FFirstPageEmpty + TmpDeleteRows;
2272       if FFirstPageEmpty > PCap then begin
2273         FFirstPageEmpty := FFirstPageEmpty - PCap;
2274         DeletePages(0, 1);
2275       end;
2276     end;
2277   end
2278   else begin
2279     c := FCount - ACount;
2280     LastPageEmpty := (PCap - (c + FFirstPageEmpty)) and FPageSizeMask;
2281     assert(LastPageEmpty >= 0, 'TLazPagedListObjBase.InsertRows: LastPageEmpty >= 0');
2282     if SubCount * 2 <= PCap then begin
2283       if SubCount > 0 then begin
2284         if LastPageEmpty < SubCount then
2285           InsertFilledPages(PgCnt, 1);
2286         if AIndex < c then begin
2287           MoveRows(AIndex, AIndex + SubCount, c - AIndex);
2288         end;
2289       end;
2290       if ExtraPagesNeeded > 0 then
2291         SplitPage(InsertPageIdx, SubIndex, ExtraPagesNeeded - 1);
2292     end
2293     else begin
2294       TmpDeleteRows := PCap - SubCount;
2295       SplitPage(InsertPageIdx, SubIndex, ExtraPagesNeeded);
2296       assert(TmpDeleteRows>0, 'TLazPagedListObjBase.InsertRows: TmpDeleteRows>0');
2297       if AIndex < c then begin
2298         c := FCount + TmpDeleteRows;
2299         AIndex := AIndex + ((ExtraPagesNeeded + 1) << FPageSizeExp);
2300         MoveRows(AIndex, AIndex - TmpDeleteRows, c - AIndex);
2301       end;
2302       if LastPageEmpty + TmpDeleteRows >= PCap then
2303         DeletePages(PageCount-1, 1);
2304     end;
2305   end;
2306 //debugln('<<< INS done'); DebugDump;
2307   assert((((FPages.Count << FPageSizeExp)-FFirstPageEmpty-FCount)<=FPageSizeMask) and (((FPages.Count << FPageSizeExp)-FFirstPageEmpty-FCount)>=0), 'TLazPagedListObjBase.InsertRows: (((FPages.Count << FPageSizeExp)-FFirstPageEmpty-FCount)<=FPageSizeMask) and (((FPages.Count << FPageSizeExp)-FFirstPageEmpty-FCount)>0)');
2308 end;
2309 
2310 procedure TLazPagedListObjBase.DeleteRows(AIndex, ACount: Integer);
2311 var
2312   PCap, c: Integer;
2313   DelPageIdx, SubIndex, AIndexAdj, SubCount, ExtraPagesNeeded, LastPageUsed, TmpInsertRows: Integer;
2314 begin
2315 //  debugln(['<<<<<<< TLazPagedListObjBase.DeleteRows ', AIndex, ', ', ACount]); DebugDump;
2316   assert((AIndex >= 0) and (AIndex + ACount <= FCount), 'TLazPagedListObjBase.DeleteRows: (AIndex >= 0) and (AIndex + ACount <= FCount)');
2317   if ACount <= 0 then
2318     exit;
2319   PCap := FPageSizeMask + 1;
2320   FCount := FCount - ACount;
2321 
2322   AIndexAdj := AIndex + FFirstPageEmpty;
2323   DelPageIdx:= (AIndexAdj) >> FPageSizeExp;
2324   SubIndex := AIndexAdj and FPageSizeMask;
2325 
2326   SubCount := ACount and FPageSizeMask;
2327   ExtraPagesNeeded := ACount - SubCount;
2328   if (ExtraPagesNeeded > 0) then
2329     ExtraPagesNeeded := ExtraPagesNeeded >> FPageSizeExp;
2330 
2331   If Cardinal(DelPageIdx) * 2 < Cardinal(PageCount) then begin
2332     if (SubCount * 2 <= PCap) or (PageCount = 1) then begin
2333       if ExtraPagesNeeded > 0 then
2334         JoinPageWithNext(DelPageIdx, SubIndex, ExtraPagesNeeded - 1);
2335       if (SubCount > 0) then begin
2336         if (AIndex > 0) then
2337           MoveRows(0, SubCount, AIndex);
2338         LastPageUsed := PCap - FFirstPageEmpty;
2339         if LastPageUsed > SubCount then begin
2340           FFirstPageEmpty := FFirstPageEmpty + SubCount;
2341         end
2342         else begin
2343           DeletePages(0, 1);
2344           FFirstPageEmpty := SubCount - LastPageUsed;
2345         end;
2346 assert(FFirstPageEmpty<PCap, 'TLazPagedListObjBase.DeleteRows: FFirstPageEmpty<PCap');
2347       end;
2348     end
2349     else begin
2350       TmpInsertRows := PCap - SubCount;
2351       assert(TmpInsertRows>0, 'TLazPagedListObjBase.DeleteRows: TmpInsertRows>0');
2352       FFirstPageEmpty := FFirstPageEmpty - TmpInsertRows;
2353       if FFirstPageEmpty < 0 then begin
2354         InsertFilledPages(0, 1); // TODO: what if this needs capacity??
2355         FFirstPageEmpty := FFirstPageEmpty + PCap;
2356         inc(DelPageIdx);
2357       end;
2358       if (AIndex > 0) then
2359         MoveRows(TmpInsertRows, 0, AIndex);
2360       SubIndex := SubIndex - TmpInsertRows;
2361       if SubIndex < 0 then begin
2362         SubIndex := SubIndex + PCap;
2363         DelPageIdx := DelPageIdx - 1;
2364       end;
2365       JoinPageWithNext(DelPageIdx, SubIndex, ExtraPagesNeeded);
2366     end;
2367   end
2368   else begin
2369     c := FCount + ACount;
2370     LastPageUsed := (c + FFirstPageEmpty) and FPageSizeMask;
2371     if LastPageUsed = 0 then LastPageUsed := PCap;
2372     if SubCount * 2 <= PCap then begin
2373       if ExtraPagesNeeded > 0 then begin
2374         JoinPageWithNext(DelPageIdx, SubIndex, ExtraPagesNeeded - 1);
2375         c := c - (ExtraPagesNeeded << FPageSizeExp);
2376       end;
2377       if (SubCount > 0) then begin
2378         if (c - AIndex - SubCount > 0) then
2379           MoveRows(AIndex + SubCount, AIndex, c - AIndex - SubCount);
2380         if LastPageUsed <= SubCount then
2381           DeletePages(PageCount - 1, 1);
2382       end;
2383     end
2384     else begin
2385       TmpInsertRows := PCap - SubCount;
2386       assert(TmpInsertRows>0, 'TLazPagedListObjBase.DeleteRows: TmpInsertRows>0');
2387       if LastPageUsed + TmpInsertRows > PCap then
2388         InsertFilledPages(PageCount, 1); // TODO: what if this needs capacity??
2389       AIndex := AIndex + SubCount;
2390       if (AIndex < c) then
2391         MoveRows(AIndex, AIndex + TmpInsertRows, c - AIndex);
2392       JoinPageWithNext(DelPageIdx, SubIndex, ExtraPagesNeeded);
2393     end;
2394   end;
2395 //debugln([' DEL DONE <<<<<<<<< ']);DebugDump;
2396   if (FCount = 0) and (PageCount > 0) then
2397     DeletePages(0, 1);
2398 assert((FPages.Count =0) or (FCount>0), 'TLazPagedListObjBase.DeleteRows: (FPages.Count =0) or (FCount>0)');
2399 assert((FPages.Count=0)or(((FPages.Count << FPageSizeExp)-FFirstPageEmpty-FCount)<=FPageSizeMask) and (((FPages.Count << FPageSizeExp)-FFirstPageEmpty-FCount)>=0), 'TLazPagedListObjBase.DeleteRows: (((FPages.Count << FPageSizeExp)-FFirstPageEmpty-FCount)<=FPageSizeMask) and (((FPages.Count << FPageSizeExp)-FFirstPageEmpty-FCount)>0)');
2400 end;
2401 
2402 procedure TLazPagedListObjBase.InternalMoveRowsDown(AFromIndex, AToIndex, ACount: Integer);
2403 var
2404   FromIndexAdj, ToIndexAdj: Integer;
2405   SourceStartPage, SourceStartSubIndex, TargetStartPage, TargetStartSubIndex: Integer;
2406   TargetEndPage, TargetEndSubIndex: Integer;
2407   Diff, PageCapacity, MovePgCnt, BblCnt: Integer;
2408   c, p1, p2: Integer;
2409   SrcPage, DstPage: PPageType;
2410 begin
2411   PageCapacity := FPageSizeMask+1;
2412 
2413   (* Calculate FROM page, and index in page *)
2414   FromIndexAdj := AFromIndex + FFirstPageEmpty;
2415   SourceStartPage     := FromIndexAdj >> FPageSizeExp;
2416   SourceStartSubIndex := FromIndexAdj and FPageSizeMask;
2417 
2418   (* Calculate TO page, and index in page *)
2419   ToIndexAdj := AToIndex + FFirstPageEmpty;
2420   TargetStartPage     := ToIndexAdj >> FPageSizeExp;
2421     TargetStartSubIndex := ToIndexAdj and FPageSizeMask;
2422 
2423   ToIndexAdj := ToIndexAdj + aCount - 1;
2424   TargetEndPage     := ToIndexAdj >> FPageSizeExp;
2425   TargetEndSubIndex := (ToIndexAdj and FPageSizeMask) + 1; // NEXT
2426 
2427 //debugln(['####******************** MoveRows DOWN ',AFromIndex, ' to ', AToIndex, ' cnt=',ACount, '   SourceStartPage=',SourceStartPage, ' SourceStartSubIndex=',SourceStartSubIndex, '   //  TargetStartPage=',TargetStartPage, ' TargetStartSubIndex=',TargetStartSubIndex,  ' / TargetEndPage=',TargetEndPage, ' TargetEndSubIndex=',TargetEndSubIndex]);{--}DebugDump;
2428 
2429   Diff := AFromIndex - AToIndex;
2430 
2431   (* The "gap" in the target-end page may be bigger/smaller than BubbleCnt.
2432    * (BubbleCnt is "Diff and FPageSizeMask")
2433    * Move appropriate amounts of entries into the target-end page.
2434    * If BubbleCnt=0, then this will leave no "gap" in the target-end page.
2435    *)
2436   c := Min(ACount, PageCapacity - SourceStartSubIndex);
2437   DstPage := PagePointer[TargetStartPage];
2438   if TargetStartPage = SourceStartPage then begin
2439     // Example: 1
2440     DstPage^.MoveRows(SourceStartSubIndex, TargetStartSubIndex, c);
2441     SourceStartSubIndex := SourceStartSubIndex + c;
2442     TargetStartSubIndex := TargetStartSubIndex + c;  // TargetStartSubIndex is smaller than c
2443   end
2444   else begin
2445     // Example: 3
2446     c := Min(c, PageCapacity - TargetStartSubIndex);
2447     SrcPage := PagePointer[SourceStartPage];
2448     SrcPage^.MoveRowsToOther(SourceStartSubIndex, TargetStartSubIndex, c, PageCapacity, DstPage^);
2449     SourceStartSubIndex := SourceStartSubIndex + c;
2450     TargetStartSubIndex := TargetStartSubIndex + c;
2451   end;
2452 
2453   ACount := ACount - c;
2454   if ACount = 0 then
2455     exit;
2456 
2457   if SourceStartSubIndex = PageCapacity then begin
2458     SourceStartSubIndex := 0;
2459     Inc(SourceStartPage);
2460   end;
2461   if TargetStartSubIndex = PageCapacity then begin
2462     TargetStartSubIndex := 0;
2463     Inc(TargetStartPage);
2464   end;
2465   assert((TargetStartSubIndex = 0) or (PageCapacity - TargetStartSubIndex = Diff and FPageSizeMask), 'TLazPagedListObjBase.MoveRows: TargetStartSubIndex at end-of-cell OR at BubbleCnt');
2466   assert((TargetStartSubIndex = 0) or (SourceStartSubIndex = 0), 'TLazPagedListObjBase.MoveRows: (TargetStartSubIndex = PageCapacity) or (SourceStartSubIndex = PageCapacity)');
2467 
2468 
2469   // Full Pages AND/OR bubble
2470   if (TargetStartPage < TargetEndPage) then begin
2471     (*
2472      * Move full pages, if needed
2473      *)
2474     p1 := TargetStartPage;
2475     if TargetStartSubIndex > 0 then inc(p1);
2476     p2 := TargetEndPage;
2477     if TargetEndSubIndex < PageCapacity then dec(p2); // TODO: TargetEndSubIndex < TargetPage.Count
2478     MovePgCnt := p2 - p1 + 1;
2479     if (SourceStartPage > p1) and (MovePgCnt > 0) then begin
2480       SwapPagesDown(SourceStartPage, p1, p2);
2481 //debugln(['SWAP:',SourceStartPage,',  ',p1,',',p2]);{--}DebugDump;
2482     end;
2483 
2484 
2485     (*
2486      * Bubble
2487      * or adjust Source/Target... for the move // (p2 <= TargetEndPage)
2488      *)
2489     BblCnt := Diff and FPageSizeMask;
2490     if (BblCnt > 0) and ((p2 > TargetStartPage) or (TargetStartSubIndex = 0)) then begin
2491       assert((p1 = TargetStartPage) or (PageCapacity - TargetStartSubIndex = BblCnt), 'TLazPagedListObjBase.InternalMoveRowsDown: (p2 = TargetEndPage) or (PageCapacity TargetStartSubIndex = BblCnt)');
2492       if TargetStartSubIndex = 0 then begin
2493         // Example: 3  // may or may not bubble // maybe only adjust
2494         PagePointer[TargetStartPage]^.AdjustFirstItemOffset(BblCnt, FPageSizeMask);
2495         dec(MovePgCnt);  // only affects how "ACount" is adjusted
2496         SourceStartSubIndex := SourceStartSubIndex + (PageCapacity - BblCnt);
2497         if SourceStartSubIndex >= PageCapacity then begin
2498           SourceStartSubIndex := SourceStartSubIndex - PageCapacity;
2499           Inc(SourceStartPage);
2500         end;
2501         ACount := ACount - (PageCapacity - BblCnt);
2502         TargetStartSubIndex := PageCapacity - BblCnt;
2503       end;
2504 
2505       if p2 > TargetStartPage then begin
2506         BubbleEntriesDown(p2, TargetStartPage, BblCnt);
2507         PagePointer[p2]^.AdjustFirstItemOffset(BblCnt, FPageSizeMask);
2508       end;
2509     end;
2510 
2511 
2512     if MovePgCnt > 0 then begin
2513       ACount := ACount - (MovePgCnt << FPageSizeExp);
2514       assert(ACount>=0, 'TLazPagedListObjBase.InternalMoveRowsDown: ACount>=0');
2515       if ACount = 0 then
2516         exit;
2517       TargetStartPage := TargetStartPage + MovePgCnt;
2518       SourceStartPage := SourceStartPage + MovePgCnt;
2519     end;
2520   end;
2521 //{--}DebugDump;
2522   assert((TargetEndPage-TargetStartPage<=1), 'TLazPagedListObjBase.InternalMoveRowsDown / Bubbled all but the last page: (TargetEndPage-TargetStartPage<=1)');
2523 
2524 
2525   // move
2526   while ACount > 0 do begin
2527     c := min(min(PageCapacity - TargetStartSubIndex, PageCapacity - SourceStartSubIndex), ACount);
2528     assert(c>0, 'TLazPagedListObjBase.MoveRows: c>0');
2529     SrcPage := PagePointer[SourceStartPage];
2530     if SourceStartPage = TargetStartPage then
2531       SrcPage^.MoveRows(SourceStartSubIndex, TargetStartSubIndex, c)
2532     else
2533       SrcPage^.MoveRowsToOther(SourceStartSubIndex, TargetStartSubIndex, c, PageCapacity, PagePointer[TargetStartPage]^);
2534 
2535     ACount := ACount - c;
2536     if ACount = 0 then
2537       exit;
2538 
2539     TargetStartSubIndex := TargetStartSubIndex + c;
2540     SourceStartSubIndex := SourceStartSubIndex + c;
2541     if SourceStartSubIndex = PageCapacity then begin
2542       SourceStartSubIndex := 0;
2543       Inc(SourceStartPage);
2544     end;
2545     if TargetStartSubIndex = PageCapacity then begin
2546       TargetStartSubIndex := 0;
2547       Inc(TargetStartPage);
2548     end;
2549   end;
2550 
2551 end;
2552 
2553 procedure TLazPagedListObjBase.InternalMoveRowsUp(AFromIndex, AToIndex, ACount: Integer);
2554 var
2555   FromIndexAdj, ToIndexAdj: Integer;
2556   TargetStartPage, TargetStartSubIndex: Integer;
2557   SourceEndPage, SourceEndSubIndex, TargetEndPage, TargetEndSubIndex: Integer;
2558   Diff, PageCapacity, MovePgCnt, BblCnt: Integer;
2559   c, p1, p2: Integer;
2560   SrcPage, DstPage: PPageType;
2561 begin
2562   PageCapacity := FPageSizeMask+1;
2563 
2564   (* Calculate FROM page, and index in page *)
2565   FromIndexAdj := AFromIndex + FFirstPageEmpty + aCount - 1;
2566   SourceEndPage       := FromIndexAdj >> FPageSizeExp;
2567   SourceEndSubIndex   := (FromIndexAdj and FPageSizeMask) + 1; // NEXT
2568 
2569   (* Calculate TO page, and index in page *)
2570   ToIndexAdj := AToIndex + FFirstPageEmpty;
2571   TargetStartPage     := ToIndexAdj >> FPageSizeExp;
2572   TargetStartSubIndex := ToIndexAdj and FPageSizeMask;
2573 
2574   ToIndexAdj := ToIndexAdj + aCount - 1;
2575   TargetEndPage     := ToIndexAdj >> FPageSizeExp;
2576   TargetEndSubIndex := (ToIndexAdj and FPageSizeMask) + 1; // NEXT
2577 
2578 //debugln(['******************** MoveRows UP ',AFromIndex, ' to ', AToIndex, ' cnt=',ACount, ' / SourceEndPage=',SourceEndPage, ' SourceEndSubIndex=',SourceEndSubIndex, '  //  TargetStartPage=',TargetStartPage, ' TargetStartSubIndex=',TargetStartSubIndex,  ' / TargetEndPage=',TargetEndPage, ' TargetEndSubIndex=',TargetEndSubIndex]);{--}DebugDump;
2579 
2580   Diff := AToIndex - AFromIndex;
2581 
2582   (* Examples
2583 
2584 1)  move up cnt=2300 from 3850-6150 to 4150-6450  dist 300
2585          |                    |                    |                    |                    |
2586       xxx xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx xxx
2587       xxx xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx       $$$  // moved 150
2588       xxx       $$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$ $$$$$$$$$  // bubbled 300
2589              $$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$ $$$$$$$$$  // moved 150
2590 
2591 2)  move up cnt=2300 from 3850-6150 to 5150-7450  dist 1300
2592          |                    |                    |                    |                    |
2593       xxx xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx xxx
2594       xxx xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx                            $$$  // moved 150
2595       xxx                      xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx       $$$  // moved page
2596 .... bubble 300 / move 150
2597 
2598 3)  move up cnt=2300 from 3450-5750 to 3750-6050  dist 300
2599                  |                    |                    |                    |                    |
2600       xxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxx
2601       xxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxx       $  // moved 50 // TargetEndSubIndex = 0
2602       xxxxxxxxxxx       $$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$ $  // bubbled 300
2603       xxxxx       $$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$ $  // moved 300
2604 ... move 250
2605 
2606 
2607   *** Reverse Bubble
2608 
2609   move up cnt=2300 from 3850-6150 to 4750-7050  dist 900
2610          |                    |                    |                    |                    |
2611       xxx xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx xxx
2612       xxx xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx xx                   $  // moved 50 into high target node
2613 instead of bubble 900 up
2614       xxx xx                ~~ xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx $  // swapped high source down
2615       xxx xx                $$ $$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$   $  // bubbled 100 down
2616       xxx                   $$ $$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$~~ $  // moved 100 back to target
2617 ----                        ^^ belong to start of node
2618                          $$$$$ $$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$ $  // moved 150
2619 
2620       *)
2621 
2622   (* The "gap" in the target-end page may be bigger/smaller than BubbleCnt.
2623    * (BubbleCnt is "Diff and FPageSizeMask")
2624    * Move appropriate amounts of entries into the target-end page.
2625    * If BubbleCnt=0, then this will leave no "gap" in the target-end page.
2626    *)
2627   c := Min(ACount, SourceEndSubIndex);
2628   DstPage := PagePointer[TargetEndPage];
2629   if TargetEndPage = SourceEndPage then begin
2630     // Example: 1
2631     SourceEndSubIndex := SourceEndSubIndex - c;
2632     TargetEndSubIndex := TargetEndSubIndex - c;  // TargetEndSubIndex is greater than c
2633     DstPage^.MoveRows(SourceEndSubIndex, TargetEndSubIndex, c);
2634   end
2635   else begin
2636     // Example: 3
2637     c := Min(c, TargetEndSubIndex);
2638     SrcPage := PagePointer[SourceEndPage];
2639     SourceEndSubIndex := SourceEndSubIndex - c;
2640     TargetEndSubIndex := TargetEndSubIndex - c;
2641     SrcPage^.MoveRowsToOther(SourceEndSubIndex, TargetEndSubIndex, c, PageCapacity, DstPage^);
2642   end;
2643 
2644   ACount := ACount - c;
2645   if ACount = 0 then
2646     exit;
2647 
2648   if SourceEndSubIndex = 0 then begin
2649     SourceEndSubIndex := PageCapacity;
2650     Dec(SourceEndPage);
2651   end;
2652   if TargetEndSubIndex = 0 then begin
2653     TargetEndSubIndex := PageCapacity;
2654     Dec(TargetEndPage);
2655   end;
2656   assert((TargetEndSubIndex = PageCapacity) or (TargetEndPage=0) or (TargetEndSubIndex = Diff and FPageSizeMask), 'TLazPagedListObjBase.MoveRows: TargetEndSubIndex at end-of-cell OR at BubbleCnt');
2657   assert((TargetEndSubIndex = PageCapacity) or (TargetEndPage=0) or (SourceEndSubIndex = PageCapacity) or (SourceEndPage=0), 'TLazPagedListObjBase.MoveRows: (TargetEndSubIndex = PageCapacity) or (SourceEndSubIndex = PageCapacity)');
2658 
2659 
2660 
2661   // Full Pages AND/OR bubble
2662   if (TargetStartPage < TargetEndPage) then begin
2663     (*
2664      * Move full pages, if needed
2665      *)
2666     p1 := TargetStartPage;
2667     if TargetStartSubIndex > 0 then inc(p1);
2668     p2 := TargetEndPage;
2669     if TargetEndSubIndex < PageCapacity then dec(p2);  // TODO: TargetEndSubIndex < TargetPage.Count ????
2670     MovePgCnt := p2 - p1 + 1;
2671     if (SourceEndPage < p2) and (MovePgCnt > 0) then begin
2672       SwapPagesUp(SourceEndPage - MovePgCnt + 1, p1, p2);
2673     end;
2674 
2675 
2676     (*
2677      * Bubble
2678      * or adjust Source/Target... for the move // (p1 >= TargetEndPage)
2679      *)
2680     BblCnt := Diff and FPageSizeMask;
2681     if (BblCnt > 0) and ((p1 < TargetEndPage) or (TargetEndSubIndex = PageCapacity)) then begin
2682       assert((p2 = TargetEndPage) or (p2=0) or (TargetEndSubIndex = BblCnt), 'TLazPagedListObjBase.MoveRows: (p2 < TargetEndPage) or (TargetEndSubIndex = BblCnt)');
2683       if TargetEndSubIndex = PageCapacity then begin
2684         // Example: 3  // may or may not bubble // maybe only adjust
2685         PagePointer[TargetEndPage]^.AdjustFirstItemOffset(-BblCnt, FPageSizeMask);
2686         dec(MovePgCnt); // only affects how "ACount" is adjusted
2687         SourceEndSubIndex := SourceEndSubIndex - (PageCapacity - BblCnt);
2688         if SourceEndSubIndex <= 0 then begin
2689           SourceEndSubIndex := SourceEndSubIndex + PageCapacity;
2690           dec(SourceEndPage);
2691         end;
2692         ACount := ACount - (PageCapacity - BblCnt);
2693         TargetEndSubIndex := BblCnt;
2694       end;
2695 
2696       if p1 < TargetEndPage then begin
2697         BubbleEntriesUp(p1, TargetEndPage, BblCnt);
2698         PagePointer[p1]^.AdjustFirstItemOffset(-BblCnt, FPageSizeMask);
2699       end;
2700     end;
2701 
2702 
2703     if MovePgCnt > 0 then begin
2704       ACount := ACount - (MovePgCnt << FPageSizeExp);
2705       assert(ACount>=0, 'TLazPagedListObjBase.InternalMoveRowsUp: ACount>=0');
2706       if ACount = 0 then
2707         exit;
2708       TargetEndPage := TargetEndPage - MovePgCnt;
2709       SourceEndPage := SourceEndPage - MovePgCnt;
2710     end;
2711   end;
2712   assert((TargetEndPage>=0), 'TLazPagedListObjBase.MoveRows: (TargetEndPage>=0)');
2713   assert(TargetEndSubIndex > 0, 'TLazPagedListObjBase.MoveRows: TargetEndSubIndex > 0');
2714   assert((TargetEndPage-TargetStartPage<=1), 'TLazPagedListObjBase.InternalMoveRowsDown / Bubbled all but the last page: (TargetEndPage-TargetStartPage<=1)');
2715 
2716 
2717 
2718   // move
2719   while ACount > 0 do begin
2720     c := min(min(TargetEndSubIndex, SourceEndSubIndex), ACount);
2721     assert(c>0, 'TLazPagedListObjBase.MoveRows: c>0');
2722     TargetEndSubIndex := TargetEndSubIndex - c;
2723     SourceEndSubIndex := SourceEndSubIndex - c;
2724     SrcPage := PagePointer[SourceEndPage];
2725     if SourceEndPage = TargetEndPage then
2726       SrcPage^.MoveRows(SourceEndSubIndex, TargetEndSubIndex, c)
2727     else
2728       SrcPage^.MoveRowsToOther(SourceEndSubIndex, TargetEndSubIndex, c, PageCapacity, PagePointer[TargetEndPage]^);
2729 
2730     ACount := ACount - c;
2731     if ACount = 0 then
2732       exit;
2733     if SourceEndSubIndex = 0 then begin
2734       SourceEndSubIndex := PageCapacity;
2735       Dec(SourceEndPage);
2736     end;
2737     if TargetEndSubIndex = 0 then begin
2738       TargetEndSubIndex := PageCapacity;
2739       Dec(TargetEndPage);
2740     end;
2741   end;
2742 
2743 end;
2744 
2745 procedure TLazPagedListObjBase.MoveRows(AFromIndex, AToIndex, ACount: Integer);
2746 begin
2747   assert((AFromIndex>=0) and (AToIndex>=0), 'TLazPagedListObjBase.MoveRows: (AFromIndex>=0) and (AToIndex>=0)');
2748 
2749   if AFromIndex < AToIndex then
2750     InternalMoveRowsUp(AFromIndex, AToIndex, ACount)
2751   else
2752     InternalMoveRowsDown(AFromIndex, AToIndex, ACount);
2753 end;
2754 
2755 procedure TLazPagedListObjBase.DebugDump;
2756 var i : integer;
2757 begin
2758   if fpages.fmem.IsAllocated then begin
2759     debugln(['PAGED .Dump  Pages.Capacity: ', fpages.fmem.Capacity, ', P.Count: ',fpages.fmem.Count,' -- Count:',FCount,'   FirstPageEmpty: ', FFirstPageEmpty, ': ']);
2760     for i := 0 to fpages.Count - 1 do FPages.ItemPointer[i]^.DebugDump;
2761   end
2762   else debugln(['PAGED .Dump NONE']);
2763 end;
2764 
2765 { TLazPagedListObj }
2766 
2767 procedure TLazPagedListObj.Create(APageSizeExp: Integer; AnItemSize: Integer);
2768 begin
2769   FItemSize.ItemSize := AnItemSize;
2770   inherited Create(APageSizeExp);
2771 end;
2772 
2773 { TLazShiftBufferList }
2774 
TLazShiftBufferList.GetCapacitynull2775 function TLazShiftBufferList.GetCapacity: Integer;
2776 begin
2777   Result := FListMem.Capacity;
2778 end;
2779 
TLazShiftBufferList.GetCountnull2780 function TLazShiftBufferList.GetCount: Integer;
2781 begin
2782   Result := FListMem.Count;
2783 end;
2784 
TLazShiftBufferList.GetItemPointernull2785 function TLazShiftBufferList.GetItemPointer(Index: Integer): Pointer;
2786 begin
2787   Result := FListMem.ItemPointer[Index];
2788 end;
2789 
2790 procedure TLazShiftBufferList.SetCapacity(AValue: Integer);
2791 begin
2792   FListMem.Capacity := AValue;
2793 end;
2794 
2795 procedure TLazShiftBufferList.SetCount(AValue: Integer);
2796 begin
2797   if AValue > FListMem.Count then
2798     FListMem.InsertRows(FListMem.Count, AValue - FListMem.Count)
2799   else
2800   if AValue < FListMem.Count then
2801     FListMem.DeleteRows(AValue, FListMem.Count - AValue);
2802 end;
2803 
2804 constructor TLazShiftBufferList.Create(AnItemSize: Integer);
2805 begin
2806   FListMem.Create(AnItemSize);
2807 end;
2808 
2809 destructor TLazShiftBufferList.Destroy;
2810 begin
2811   FListMem.Destroy;
2812 end;
2813 
Addnull2814 function TLazShiftBufferList.Add(ItemPointer: Pointer): Integer;
2815 begin
2816   Result := FListMem.Count;
2817   FListMem.InsertRows(Result, 1);
2818   Move(ItemPointer^, FListMem.ItemPointer[Result]^, FListMem.FItemSize.ItemSize);
2819 end;
2820 
2821 procedure TLazShiftBufferList.Clear;
2822 begin
2823   FListMem.Capacity := 0;
2824 end;
2825 
2826 procedure TLazShiftBufferList.Delete(Index: Integer);
2827 begin
2828   FListMem.DeleteRows(Index, 1);
2829 end;
2830 
2831 procedure TLazShiftBufferList.Insert(Index: Integer; ItemPointer: Pointer);
2832 begin
2833   FListMem.InsertRows(Index, 1);
2834   Move(ItemPointer^, FListMem.ItemPointer[Index]^, FListMem.FItemSize.ItemSize);
2835 end;
2836 
2837 { TLazShiftBufferListGen }
2838 
TLazShiftBufferListGen.GetCapacitynull2839 function TLazShiftBufferListGen.GetCapacity: Integer;
2840 begin
2841   Result := FListMem.Capacity;
2842 end;
2843 
Getnull2844 function TLazShiftBufferListGen.Get(Index: Integer): T;
2845 begin
2846   Result := FListMem.ItemPointer[Index]^;
2847 end;
2848 
GetCountnull2849 function TLazShiftBufferListGen.GetCount: Integer;
2850 begin
2851   Result := FListMem.Count;
2852 end;
2853 
GetItemPointernull2854 function TLazShiftBufferListGen.GetItemPointer(Index: Integer): PT;
2855 begin
2856   Result := FListMem.ItemPointer[Index];
2857 end;
2858 
2859 procedure TLazShiftBufferListGen.Put(Index: Integer; AValue: T);
2860 begin
2861   FListMem.ItemPointer[Index]^ := AValue;
2862 end;
2863 
2864 procedure TLazShiftBufferListGen.SetCapacity(AValue: Integer);
2865 begin
2866   FListMem.Capacity := AValue;
2867 end;
2868 
2869 procedure TLazShiftBufferListGen.SetCount(AValue: Integer);
2870 begin
2871   if AValue > FListMem.Count then
2872     FListMem.InsertRows(FListMem.Count, AValue - FListMem.Count)
2873   else
2874   if AValue < FListMem.Count then
2875     FListMem.DeleteRows(AValue, FListMem.Count - AValue);
2876 end;
2877 
2878 constructor TLazShiftBufferListGen.Create;
2879 begin
2880   FListMem.Create;
2881 end;
2882 
2883 destructor TLazShiftBufferListGen.Destroy;
2884 begin
2885   FListMem.Destroy;
2886 end;
2887 
Addnull2888 function TLazShiftBufferListGen.Add(Item: T): Integer;
2889 begin
2890   Result := FListMem.Count;
2891   FListMem.InsertRows(Result, 1);
2892   FListMem.ItemPointer[Result]^ := Item;
2893 end;
2894 
2895 procedure TLazShiftBufferListGen.Clear;
2896 begin
2897   FListMem.Capacity := 0;
2898 end;
2899 
2900 procedure TLazShiftBufferListGen.Delete(Index: Integer);
2901 begin
2902   FListMem.DeleteRows(Index, 1);
2903 end;
2904 
IndexOfnull2905 function TLazShiftBufferListGen.IndexOf(Item: T): Integer;
2906 begin
2907   Result := FListMem.IndexOf(Item);
2908 end;
2909 
2910 procedure TLazShiftBufferListGen.Insert(Index: Integer; Item: T);
2911 begin
2912   FListMem.InsertRows(Index, 1);
2913   FListMem.ItemPointer[Index]^ := Item;
2914 end;
2915 
TLazShiftBufferListGen.Removenull2916 function TLazShiftBufferListGen.Remove(Item: T): Integer;
2917 begin
2918   Result := IndexOf(Item);
2919   if Result >= 0 then
2920     Delete(Result);
2921 end;
2922 
2923 end.
2924 
2925