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