 8037f9f23a
			
		
	
	
		8037f9f23a
		
	
	
	
	
		
			
			git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@44 8e941d3f-bd1b-0410-a28a-d453659cc2b4
		
			
				
	
	
		
			699 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			699 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
| {*********************************************************}
 | |
| {*                  OVCSPARY.PAS 4.06                    *}
 | |
| {*********************************************************}
 | |
| 
 | |
| {* ***** BEGIN LICENSE BLOCK *****                                            *}
 | |
| {* Version: MPL 1.1                                                           *}
 | |
| {*                                                                            *}
 | |
| {* The contents of this file are subject to the Mozilla Public License        *}
 | |
| {* Version 1.1 (the "License"); you may not use this file except in           *}
 | |
| {* compliance with the License. You may obtain a copy of the License at       *}
 | |
| {* http://www.mozilla.org/MPL/                                                *}
 | |
| {*                                                                            *}
 | |
| {* Software distributed under the License is distributed on an "AS IS" basis, *}
 | |
| {* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License   *}
 | |
| {* for the specific language governing rights and limitations under the       *}
 | |
| {* License.                                                                   *}
 | |
| {*                                                                            *}
 | |
| {* The Original Code is TurboPower Orpheus                                    *}
 | |
| {*                                                                            *}
 | |
| {* The Initial Developer of the Original Code is TurboPower Software          *}
 | |
| {*                                                                            *}
 | |
| {* Portions created by TurboPower Software Inc. are Copyright (C)1995-2002    *}
 | |
| {* TurboPower Software Inc. All Rights Reserved.                              *}
 | |
| {*                                                                            *}
 | |
| {* Contributor(s):                                                            *}
 | |
| {*                                                                            *}
 | |
| {* ***** END LICENSE BLOCK *****                                              *}
 | |
| 
 | |
| {$I OVC.INC}
 | |
| 
 | |
| {$B-} {Complete Boolean Evaluation}
 | |
| {$I+} {Input/Output-Checking}
 | |
| {$P+} {Open Parameters}
 | |
| {$T-} {Typed @ Operator}
 | |
| {.W-} {Windows Stack Frame}
 | |
| {$X+} {Extended Syntax}
 | |
| 
 | |
| unit ovcspary;
 | |
|   {-Orpheus - sparse array implementation}
 | |
| 
 | |
| interface
 | |
| 
 | |
| uses
 | |
|   {$IFNDEF LCL} Windows, {$ELSE} LclIntf, {$ENDIF}
 | |
|   SysUtils, OvcExcpt, OvcConst, OvcData;
 | |
| 
 | |
| const
 | |
|   MaxSparseArrayItems = 320000;  {maximum items in a sparse array}
 | |
| 
 | |
| type
 | |
|   TSparseArrayFunc = function (Index : longint; Item : pointer;
 | |
|                                ExtraData : pointer) : boolean;
 | |
|     {-Sparse array's iterator type. Should return true to continue iterating,
 | |
|       false otherwise.}
 | |
| 
 | |
|   {The sparse array class}
 | |
|   TOvcSparseArray = class
 | |
|     protected {private}
 | |
|       FCount : longint;       {Fake count of the items}
 | |
|       FArray : pointer;       {Sparse array}
 | |
| 
 | |
|       ChunkCount : word;      {Number of chunks}
 | |
|       ChunkArraySize : word;  {Size of FArray}
 | |
| 
 | |
|       procedure RecalcCount;
 | |
| 
 | |
|     protected
 | |
|       function GetActiveCount : longint;
 | |
|       function GetItem(Index : longint) : pointer;
 | |
|       procedure PutItem(Index : longint; Item : pointer);
 | |
| 
 | |
|     public
 | |
|       constructor Create;
 | |
|       destructor Destroy; override;
 | |
| 
 | |
|       function  Add(Item : pointer) : longint;
 | |
|         {-Add Item to end of array}
 | |
|       procedure Clear;
 | |
|         {-Clear array}
 | |
|       procedure Delete(Index : longint);
 | |
|         {-Delete item at Index, all items below move up one}
 | |
|       procedure Exchange(Index1, Index2 : longint);
 | |
|         {-Swap the items at Index1 and Index2}
 | |
|       function  First : pointer;
 | |
|         {-Return First item}
 | |
|       function  ForAll(Action : TSparseArrayFunc;
 | |
|                        Backwards : boolean;
 | |
|                        ExtraData : pointer) : longint;
 | |
|         {-Iterate through all active items, maybe backwards}
 | |
|       function  IndexOf(Item : pointer) : longint;
 | |
|         {-Get the index of Item}
 | |
|       procedure Insert(Index : longint; Item : pointer);
 | |
|         {-Insert Item at Index, it and all items below move down one}
 | |
|       function  Last : pointer;
 | |
|         {-Return Last item}
 | |
|       procedure Squeeze;
 | |
|         {-Pack the sparse array}
 | |
| 
 | |
|       property Count : longint
 | |
|         {-Logical count of the number of items (=IndexOf(Last)+1)}
 | |
|          read FCount;
 | |
|       property ActiveCount : longint
 | |
|         {-Count of non-nil items}
 | |
|          read GetActiveCount;
 | |
|       property Items[Index : longint] : pointer
 | |
|         {-Items array}
 | |
|          read GetItem write PutItem;
 | |
|          default;
 | |
|   end;
 | |
| 
 | |
| 
 | |
| implementation
 | |
| 
 | |
| 
 | |
| {Notes: the sparse array is implemented as an array of chunks, each
 | |
|         chunk contains 32 items (2^5). The array of chunks consists
 | |
|         of a set of elements, each with chunk index and a pointer to
 | |
|         the chunk. To find the item for a given index you do two
 | |
|         things: calculate the chunk index (divide by 32) and the
 | |
|         index into the chunk (the remainder once divided by 32).
 | |
|         For example: where is item 100? 100 = 3*32 + 4, so you try
 | |
|         and find chunk index 3 in the chunk array (the array is sorted
 | |
|         by chunk index, hence you do a binary search), and if found
 | |
|         return the 5th element (zero based arrays). If not found then
 | |
|         the item does not exist.
 | |
|         Thus setting item 10000 in a sparse array will allocate only
 | |
|         one chunk, the 9999 previous items are all assumed nil.
 | |
|         The sparse array can only accomodate pointers. An unused item
 | |
|         will be nil. A nil pointer will indicate an unused item. Hence
 | |
|         you cannot really use a sparse array for longints say, unless
 | |
|         you can guarantee that all your values will be non-nil.
 | |
| 
 | |
|         Sizing stuff: maximum number of pointers that can be stored is
 | |
|         just less than 350,000. For various reasons the maximum index
 | |
|         that is allowed for the sparse array is 319,999 meaning that a
 | |
|         sparse array could hold up to 320,000 pointers. To increase this
 | |
|         you could hold 64 or 128 pointers per chunk instead.
 | |
|         The minimum heap the sparse array will take is 896 bytes.
 | |
| 
 | |
|         Sparse arrays cannot really be used for keeping sorted items:
 | |
|         obviously all the items will appear at the start of the array,
 | |
|         there can be no holes.
 | |
| }
 | |
| 
 | |
| const
 | |
|   ShiftValue = 5;
 | |
|   ChunkElements = 1 shl ShiftValue; {Number of elements in a chunk: 32}
 | |
|   ChunkMask = pred(ChunkElements);  {Mask used for the item in a chunk: $1F}
 | |
| 
 | |
| type
 | |
|   PChunk = ^TChunk;                 {Definition of a chunk}
 | |
|   TChunk = array [0..pred(ChunkElements)] of pointer;
 | |
| 
 | |
|   TChunkArrayElement = packed record  {Definition of a chunk array element}
 | |
|     ChunkIndex : word;                {..index of the chunk}
 | |
|     Chunk : PChunk;                   {..the chunk itself}
 | |
|   end;
 | |
| 
 | |
| const
 | |
|   DefChunkArrayElements = 4;        {Initial size of the chunk array}
 | |
|   MaxChunkArrayElements = ($10000 div sizeof(TChunkArrayElement)) - 1;
 | |
|                                     {Absolute maximum of chunk array elements}
 | |
| 
 | |
| type
 | |
|   PChunkArray = ^TChunkArray;       {Definition of a chunk array}
 | |
|   TChunkArray = array [0..pred(MaxChunkArrayElements)] of TChunkArrayElement;
 | |
| 
 | |
| {===Helper routines==================================================}
 | |
| procedure RaiseException(ClassType : integer);
 | |
|   begin
 | |
|     case ClassType of
 | |
|       1 : raise ESAEAtMaxSize.Create(GetOrphStr(SCSAEAtMaxSize));
 | |
|       2 : raise ESAEOutOfBounds.Create(GetOrphStr(SCSAEOutOfBounds));
 | |
|     else
 | |
|       raise ESparseArrayError.Create(GetOrphStr(SCSAEGeneral));
 | |
|     end;{case}
 | |
|   end;
 | |
| {--------}
 | |
| function GrowChunkArray(A : PChunkArray; var CurSize : word) : PChunkArray;
 | |
|   {-Grow the chunk array, return the new size and the new pointer}
 | |
|   var
 | |
|     NewSize : longint;
 | |
|     NewSizeAdj : word;
 | |
|   begin
 | |
|     NewSize := longint(CurSize) +
 | |
|                (DefChunkArrayElements * sizeof(TChunkArrayElement));
 | |
|     NewSizeAdj := MaxChunkArrayElements * sizeof(TChunkArrayElement);
 | |
|     if (NewSize < NewSizeAdj) then
 | |
|       NewSizeAdj := NewSize;
 | |
|     GetMem(Result, NewSizeAdj);
 | |
|     {$IFOPT D+}
 | |
|     FillChar(Result^, NewSizeAdj, $CC);
 | |
|     {$ENDIF}
 | |
|     if (CurSize <> 0) then
 | |
|       begin
 | |
|         Move(A^, Result^, CurSize);
 | |
|         FreeMem(A, CurSize);
 | |
|       end;
 | |
|     CurSize := NewSizeAdj;
 | |
|   end;
 | |
| {--------}
 | |
| function GetChunk(A : PChunkArray;
 | |
|                   CI : word; NumChunks : word) : integer;
 | |
|   {-Find a chunk array element given the chunk index CI and the number of
 | |
|     chunks. Return the index into the chunk array, or -1 if not found.}
 | |
|   var
 | |
|     L, R : integer;
 | |
|     MsInx : word;
 | |
|   begin
 | |
|     L := 0;
 | |
|     R := pred(NumChunks);
 | |
|     repeat
 | |
|       Result := (L + R) div 2;
 | |
|       MsInx := A^[Result].ChunkIndex;
 | |
|       if (CI = MsInx) then
 | |
|         Exit
 | |
|       else if (CI < MsInx) then
 | |
|         R := pred(Result)
 | |
|       else
 | |
|         L := succ(Result);
 | |
|     until (L > R);
 | |
|     Result := -1;
 | |
|   end;
 | |
| {--------}
 | |
| function EnsureChunk(var A : PChunkArray; CI : word;
 | |
|                      var NumChunks, Size : word;
 | |
|                      DontCreate : boolean) : integer;
 | |
|   {-Makes sure that chunk CI is available for use. If it does not yet
 | |
|     exist and DontCreate is false, creates a new chunk, inserts it into
 | |
|     the chunk array (possibly growing the array). Return the index of
 | |
|     the chunk in the array.}
 | |
|   var
 | |
|     NumElements : word;
 | |
|     L, R, M : integer;
 | |
|     MsInx : word;
 | |
|   begin
 | |
|     L := 0;
 | |
|     if (NumChunks > 0) then
 | |
|       begin
 | |
|         R := pred(NumChunks);
 | |
|         repeat
 | |
|           M := (L + R) div 2;
 | |
|           MsInx := A^[M].ChunkIndex;
 | |
|           if (CI = MsInx) then
 | |
|             begin
 | |
|               Result := M;
 | |
|               Exit;
 | |
|             end
 | |
|           else if (CI < MsInx) then
 | |
|             R := pred(M)
 | |
|           else
 | |
|             L := succ(M);
 | |
|         until (L > R);
 | |
|       end;
 | |
| 
 | |
|     if DontCreate then
 | |
|       begin
 | |
|         Result := -1;
 | |
|         Exit;
 | |
|       end;
 | |
| 
 | |
|     Result := L;
 | |
| 
 | |
|     NumElements := Size div sizeof(TChunkArrayElement);
 | |
|     if (NumChunks = NumElements) then
 | |
|       A := GrowChunkArray(A, Size);
 | |
| 
 | |
|     if (Result < NumChunks) then
 | |
|       Move(A^[Result], A^[succ(Result)],
 | |
|            (NumChunks - Result) * sizeof(TChunkArrayElement));
 | |
| 
 | |
|     with A^[Result] do
 | |
|       begin
 | |
|         ChunkIndex := CI;
 | |
|         Chunk := New(PChunk);
 | |
|         FillChar(Chunk^, sizeof(TChunk), 0);
 | |
|       end;
 | |
| 
 | |
|     inc(NumChunks);
 | |
|   end;
 | |
| {--------}
 | |
| function ChunkIsBlank(A : PChunkArray; ArrayInx : word) : boolean;
 | |
|   {-Return true if the chunk has no items (all pointers are nil).}
 | |
|   const
 | |
|     ChunkSizeInWords = sizeof(TChunk) div 2;
 | |
|     ChunkSizeInDWords = sizeof(TChunk) div 4;
 | |
|   var
 | |
|     Chunk : PChunk;
 | |
| {$IFDEF NoAsm}
 | |
|     ItemNum : Integer;
 | |
| {$ENDIF}
 | |
|   begin
 | |
|     Chunk := A^[ArrayInx].Chunk;
 | |
| {$IFDEF NoAsm}
 | |
|      for ItemNum := 0 to Pred(ChunkElements) do
 | |
|        begin
 | |
|        if Chunk^[ItemNum] <> nil then
 | |
|          begin
 | |
|          Result := False;
 | |
|          Exit;
 | |
|          end;
 | |
|        end;
 | |
|      Result := True;
 | |
| {$ELSE}
 | |
|     asm
 | |
|       push edi
 | |
| {$IFDEF VERSION6}  { Delphi 6 codegen bug }
 | |
|       push ecx
 | |
| {$ENDIF}
 | |
|       lea eax, Chunk
 | |
|       mov edi, [eax]
 | |
|       xor eax, eax
 | |
|       mov edx, eax
 | |
|       mov ecx, ChunkSizeInDWords
 | |
|       repe scasd
 | |
|       jne @@Exit
 | |
|       inc edx
 | |
|     @@Exit:
 | |
| {$IFDEF VERSION6}  { Delphi 6 codegen bug }
 | |
|       pop ecx
 | |
| {$ENDIF}
 | |
|       mov @Result, dl
 | |
|       pop edi
 | |
|     end;
 | |
| {$ENDIF}
 | |
|   end;
 | |
| {--------}
 | |
| procedure DeleteChunk(A : PChunkArray; ArrayInx : word; var NumChunks : word);
 | |
|   {-Delete a chunk, moving chunks below up one.}
 | |
|   begin
 | |
|     Dispose(A^[ArrayInx].Chunk);
 | |
|     if ArrayInx < pred(NumChunks) then
 | |
|       Move(A^[succ(ArrayInx)], A^[ArrayInx],
 | |
|            (NumChunks - ArrayInx) * sizeof(TChunkArrayElement));
 | |
|     dec(NumChunks);
 | |
|     {$IFOPT D+}
 | |
|     FillChar(A^[NumChunks], sizeof(TChunkArrayElement), $CC);
 | |
|     {$ENDIF}
 | |
|   end;
 | |
| 
 | |
| {===TOvcSparseArray ForAll routines=====================================}
 | |
| function CountActiveElements(Index : longint;
 | |
|                              Item : pointer;
 | |
|                              ExtraData : pointer) : boolean; far;
 | |
|   var
 | |
|     ED : ^longint absolute ExtraData;
 | |
|   begin
 | |
|     Result := True;
 | |
|     inc(ED^);
 | |
|   end;
 | |
| {=====}
 | |
| 
 | |
| function Find1stOrLastElement(Index : longint;
 | |
|                               Item : pointer;
 | |
|                               ExtraData : pointer) : boolean; far;
 | |
|   var
 | |
|     ED : ^pointer absolute ExtraData;
 | |
|   begin
 | |
|     Find1stOrLastElement := false;
 | |
|     ED^ := Item;
 | |
|   end;
 | |
| {=====}
 | |
| 
 | |
| function FindSpecificElement(Index : longint;
 | |
|                              Item : pointer;
 | |
|                              ExtraData : pointer) : boolean; far;
 | |
|   begin
 | |
|     {continue looking if this Item is NOT the one we want}
 | |
|     FindSpecificElement := Item <> ExtraData;
 | |
|   end;
 | |
| {=====}
 | |
| 
 | |
| constructor TOvcSparseArray.Create;
 | |
|   begin
 | |
|     FArray := GrowChunkArray(FArray, ChunkArraySize);
 | |
|   end;
 | |
| {=====}
 | |
| 
 | |
| destructor TOvcSparseArray.Destroy;
 | |
|   begin
 | |
|     if Assigned(FArray) then
 | |
|       begin
 | |
|         Clear;
 | |
|         FreeMem(FArray, ChunkArraySize);
 | |
|       end;
 | |
|   end;
 | |
| {=====}
 | |
| 
 | |
| procedure TOvcSparseArray.RecalcCount;
 | |
|   var
 | |
|     Dummy : pointer;
 | |
|   begin
 | |
|     FCount := succ(ForAll(Find1stOrLastElement, true, @Dummy));
 | |
|   end;
 | |
| {--------}
 | |
| procedure TOvcSparseArray.Squeeze;
 | |
|   var
 | |
|     ArrayInx : word;
 | |
|   begin
 | |
|     ArrayInx := 0;
 | |
|     while ArrayInx <> ChunkCount do
 | |
|       if ChunkIsBlank(FArray, ArrayInx) then
 | |
|         DeleteChunk(FArray, ArrayInx, ChunkCount)
 | |
|       else
 | |
|         inc(ArrayInx);
 | |
|   end;
 | |
| {=======================================================================}
 | |
| 
 | |
| 
 | |
| {===TOvcSparseArray property access=====================================}
 | |
| function TOvcSparseArray.GetActiveCount : longint;
 | |
|   begin
 | |
|     Result := 0;
 | |
|     ForAll(CountActiveElements, true, @Result);
 | |
|   end;
 | |
| {--------}
 | |
| function TOvcSparseArray.GetItem(Index : longint) : pointer;
 | |
|   var
 | |
|     ChunkIndex : word;
 | |
|     ChunkNum   : integer;
 | |
|   begin
 | |
|     if (Index < 0) or (Index >= MaxSparseArrayItems) then
 | |
|       begin
 | |
|         RaiseException(2);
 | |
|       end;
 | |
| 
 | |
|     Result := nil;
 | |
|     if (ChunkCount > 0) then
 | |
|       begin
 | |
|         ChunkIndex := Index shr ShiftValue;
 | |
|         ChunkNum := GetChunk(FArray, ChunkIndex, ChunkCount);
 | |
|         if (ChunkNum <> -1) then
 | |
|           Result := PChunkArray(FArray)^[ChunkNum].Chunk^[Index and ChunkMask];
 | |
|       end;
 | |
|   end;
 | |
| {--------}
 | |
| procedure TOvcSparseArray.PutItem(Index : longint; Item : pointer);
 | |
|   var
 | |
|     ChunkIndex : word;
 | |
|     ChunkNum   : integer;
 | |
|   begin
 | |
|     if (Index < 0) or (Index >= MaxSparseArrayItems) then
 | |
|       begin
 | |
|         RaiseException(2);
 | |
|       end;
 | |
| 
 | |
|     ChunkIndex := Index shr ShiftValue;
 | |
|     ChunkNum := EnsureChunk(PChunkArray(FArray),
 | |
|                             ChunkIndex, ChunkCount, ChunkArraySize,
 | |
|                             (Item = nil));
 | |
| 
 | |
|     if (ChunkNum <> -1) then
 | |
|       begin
 | |
|         PChunkArray(FArray)^[ChunkNum].Chunk^[Index and ChunkMask] := Item;
 | |
|         if (Item = nil) then
 | |
|           Squeeze;
 | |
|         RecalcCount;
 | |
|       end;
 | |
|   end;
 | |
| {====================================================================}
 | |
| 
 | |
| 
 | |
| {===TOvcSparseArray item maintenance====================================}
 | |
| function TOvcSparseArray.Add(Item : pointer) : longint;
 | |
|   begin
 | |
|     if (FCount = MaxSparseArrayItems) then
 | |
|       RaiseException(1);
 | |
| 
 | |
|     Result := FCount;
 | |
|     PutItem(Result, Item);
 | |
|   end;
 | |
| {--------}
 | |
| procedure TOvcSparseArray.Clear;
 | |
|   var
 | |
|     i : integer;
 | |
|   begin
 | |
|     if (ChunkCount > 0) then
 | |
|       begin
 | |
|         for i := 0 to pred(ChunkCount) do
 | |
|           Dispose(PChunkArray(FArray)^[i].Chunk);
 | |
|         {$IFOPT D+}
 | |
|         FillChar(FArray^, ChunkArraySize, $CC);
 | |
|         {$ENDIF}
 | |
|       end;
 | |
|     ChunkCount := 0;
 | |
|     FCount := 0;
 | |
|   end;
 | |
| {--------}
 | |
| procedure TOvcSparseArray.Delete(Index : longint);
 | |
|   const
 | |
|     LastPos = pred(ChunkElements);
 | |
|   var
 | |
|     MajorInx : word;
 | |
|     ChunkNum, Dummy : integer;
 | |
|     StartPos : word;
 | |
|     OurChunk  : PChunk;
 | |
|     Transferred : boolean;
 | |
|   begin
 | |
|     if (Index < 0) or (Index >= MaxSparseArrayItems) then
 | |
|       begin
 | |
|         RaiseException(2);
 | |
|       end;
 | |
| 
 | |
|     if (Index >= FCount) then
 | |
|       Exit;
 | |
| 
 | |
|     MajorInx := Index shr ShiftValue;
 | |
|     ChunkNum := EnsureChunk(PChunkArray(FArray),
 | |
|                             MajorInx, ChunkCount, ChunkArraySize,
 | |
|                             false);
 | |
| 
 | |
|     StartPos := Index and ChunkMask;
 | |
|     OurChunk := PChunkArray(FArray)^[ChunkNum].Chunk;
 | |
|     if (StartPos <> LastPos) then
 | |
|       Move(OurChunk^[succ(StartPos)], OurChunk^[StartPos],
 | |
|            (LastPos-StartPos)*sizeof(Pointer));
 | |
| 
 | |
|     inc(ChunkNum);
 | |
|     while (ChunkNum <> ChunkCount) do
 | |
|       begin
 | |
|         with PChunkArray(FArray)^[ChunkNum] do
 | |
|           begin
 | |
|             if (ChunkIndex = MajorInx+1) then
 | |
|               begin
 | |
|                 Transferred := true;
 | |
|                 OurChunk^[LastPos] := Chunk^[0];
 | |
|               end
 | |
|             else
 | |
|               begin
 | |
|                 Transferred := false;
 | |
|                 OurChunk^[LastPos] := nil;
 | |
|               end;
 | |
|             MajorInx := ChunkIndex;
 | |
|             OurChunk := Chunk;
 | |
|           end;
 | |
|         if (OurChunk^[0] <> nil) and (not Transferred) then
 | |
|           begin
 | |
|             Dummy := EnsureChunk(PChunkArray(FArray),
 | |
|                                  MajorInx-1, ChunkCount, ChunkArraySize,
 | |
|                                  true);
 | |
|             PChunkArray(FArray)^[Dummy].Chunk^[LastPos] :=
 | |
|                OurChunk^[0];
 | |
|           end;
 | |
|         Move(OurChunk^[1], OurChunk^[0], LastPos*sizeof(Pointer));
 | |
|         inc(ChunkNum);
 | |
|       end;
 | |
| 
 | |
|     OurChunk^[LastPos] := nil;
 | |
|     Squeeze;
 | |
|     RecalcCount;
 | |
|   end;
 | |
| {--------}
 | |
| procedure TOvcSparseArray.Exchange(Index1, Index2 : longint);
 | |
|   var
 | |
|     Item1, Item2 : pointer;
 | |
|   begin
 | |
|     if (Index1 = Index2) then
 | |
|       Exit;
 | |
| 
 | |
|     if (Index1 < 0) or (Index1 >= MaxSparseArrayItems) then
 | |
|       begin
 | |
|         RaiseException(2);
 | |
|       end;
 | |
|     if (Index2 < 0) or (Index2 >= MaxSparseArrayItems) then
 | |
|       begin
 | |
|         RaiseException(2);
 | |
|       end;
 | |
| 
 | |
|     Item1 := GetItem(Index1);
 | |
|     Item2 := GetItem(Index2);
 | |
|     PutItem(Index2, Item1);
 | |
|     PutItem(Index1, Item2);
 | |
|   end;
 | |
| {--------}
 | |
| function TOvcSparseArray.First : pointer;
 | |
|   begin
 | |
|     Result := nil;
 | |
|     ForAll(Find1stOrLastElement, false, @Result);
 | |
|   end;
 | |
| {--------}
 | |
| function TOvcSparseArray.ForAll(Action : TSparseArrayFunc;
 | |
|                                 Backwards : boolean;
 | |
|                                 ExtraData : pointer) : longint;
 | |
|   var
 | |
|     MajorInx : word;
 | |
|     MinorInx : word;
 | |
|     MajorStub : longint;
 | |
|   label
 | |
|     ExitLoopsReverse, ExitLoopsForwards;
 | |
|   begin
 | |
|     if (ChunkCount = 0) then
 | |
|       Result := -1
 | |
|     else if Backwards then
 | |
|       begin
 | |
|         for MajorInx := pred(ChunkCount) downto 0 do
 | |
|           with PChunkArray(FArray)^[MajorInx] do
 | |
|             begin
 | |
|               MajorStub := longint(ChunkIndex) shl ShiftValue;
 | |
|               for MinorInx := pred(ChunkElements) downto 0 do
 | |
|                 if (Chunk^[MinorInx] <> nil) then
 | |
|                   begin
 | |
|                     Result := MajorStub + MinorInx;
 | |
|                     if not Action(Result,
 | |
|                                   Chunk^[MinorInx],
 | |
|                                   ExtraData) then
 | |
|                       Goto ExitLoopsReverse;
 | |
|                   end;
 | |
|             end;
 | |
|         Result := -1;
 | |
|       ExitLoopsReverse:
 | |
|       end
 | |
|     else
 | |
|       begin
 | |
|         for MajorInx := 0 to pred(ChunkCount) do
 | |
|           with PChunkArray(FArray)^[MajorInx] do
 | |
|             begin
 | |
|               MajorStub := longint(ChunkIndex) shl ShiftValue;
 | |
|               for MinorInx := 0 to pred(ChunkElements) do
 | |
|                 if (Chunk^[MinorInx] <> nil) then
 | |
|                   begin
 | |
|                     Result := MajorStub + MinorInx;
 | |
|                     if not Action(Result,
 | |
|                                   Chunk^[MinorInx],
 | |
|                                   ExtraData) then
 | |
|                       Goto ExitLoopsForwards;
 | |
|                 end;
 | |
|             end;
 | |
|         Result := -1;
 | |
|       ExitLoopsForwards:
 | |
|       end;
 | |
|   end;
 | |
| {--------}
 | |
| function TOvcSparseArray.IndexOf(Item : pointer) : longint;
 | |
|   begin
 | |
|     Result := ForAll(FindSpecificElement, true, Item);
 | |
|   end;
 | |
| {--------}
 | |
| procedure TOvcSparseArray.Insert(Index : longint; Item : pointer);
 | |
|   const
 | |
|     LastPos = pred(ChunkElements);
 | |
|   var
 | |
|     MajorInx : word;
 | |
|     ChunkNum : integer;
 | |
|     CarryItem, NewCarryItem : pointer;
 | |
|     StartPos : word;
 | |
|   begin
 | |
|     if (Index < 0) or (Index >= MaxSparseArrayItems) then
 | |
|       begin
 | |
|         RaiseException(2);
 | |
|       end;
 | |
| 
 | |
|     if (FCount = MaxSparseArrayItems) then
 | |
|       RaiseException(1);
 | |
| 
 | |
|     if (Index >= FCount) then
 | |
|       begin
 | |
|         PutItem(Index, Item);
 | |
|         Exit;
 | |
|       end;
 | |
| 
 | |
|     MajorInx := Index shr ShiftValue;
 | |
|     ChunkNum := EnsureChunk(PChunkArray(FArray),
 | |
|                             MajorInx, ChunkCount, ChunkArraySize,
 | |
|                             false);
 | |
| 
 | |
|     CarryItem := Item;
 | |
|     StartPos := Index and ChunkMask;
 | |
|     repeat
 | |
|       with PChunkArray(FArray)^[ChunkNum] do
 | |
|         begin
 | |
|           MajorInx := ChunkIndex;
 | |
|           NewCarryItem := Chunk^[LastPos];
 | |
|           if (StartPos <> LastPos) then
 | |
|             Move(Chunk^[StartPos], Chunk^[succ(StartPos)],
 | |
|                  (LastPos-StartPos)*sizeof(Pointer));
 | |
|           Chunk^[StartPos] := CarryItem;
 | |
|           CarryItem := NewCarryItem;
 | |
|           StartPos := 0;
 | |
|         end;
 | |
|       inc(ChunkNum);
 | |
|       if (CarryItem <> nil) then
 | |
|         if (ChunkNum = ChunkCount) or
 | |
|            (PChunkArray(FArray)^[ChunkNum].ChunkIndex <> MajorInx+1) then
 | |
|           ChunkNum := EnsureChunk(PChunkArray(FArray),
 | |
|                                   MajorInx+1, ChunkCount, ChunkArraySize,
 | |
|                                   false);
 | |
|     until (ChunkNum = ChunkCount);
 | |
|     inc(FCount);
 | |
|   end;
 | |
| {--------}
 | |
| function TOvcSparseArray.Last : pointer;
 | |
|   begin
 | |
|     Result := nil;
 | |
|     ForAll(Find1stOrLastElement, true, @Result);
 | |
|   end;
 | |
| {====================================================================}
 | |
| 
 | |
| end.
 |