home *** CD-ROM | disk | FTP | other *** search
-
- unit QLists;
-
- (*
- TQList and TQStringList v1.02
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Robert R. Marsh, SJ
- rrm@sprynet.com
- http://home.sprynet.com/sprynet/rrm/
-
- Instead of using a single array of pointers to implement a list
- (which TList does) TQList uses a Hashed Array Tree as described
- by Edward Sitarski (Dr. Dobb's Journal, September 1996, 107-110).
- The advantage is an overall reduced memory requirement, much
- reduced memory fragmentation, and more efficient growth of the
- list. The trade-off is with access time which is about doubled.
- As implemented here TQList could hold (memory permitting) just
- short of 27 million pointers in both D1 and D2/3 .
-
- The technique uses a top array filled with pointers to leaf arrays
- which hold the pointers of the list. The top and leaf arrays are
- the same size (always a power of two). Leaf arrays are only added
- when required.
-
- I have tried to keep TQList and TQStringList as like TList and
- TStringList as possible and attempted to make 32-bit functionality
- available in D1 as well.
-
- If you like TQList and TQStringList and find yourself using them
- please consider making a donation to your favorite charity. I
- would also be pleased if you would make acknowledgement in any
- projects that make use of them.
-
- QLists is supplied as is. The author disclaims all warranties,
- expressed or implied, including, without limitation, the warranties
- of merchantability and of fitness for any purpose. The author
- assumes no liability for damages, direct or consequential, which
- may result from the use of QLists.
-
- Copyright (c) 1997 Robert R. Marsh, S.J. &
- the British Province of the Society of Jesus
-
- *)
-
- (*
-
- Version 1.02
- fixes a bug in the SetCapacity method when a list is being
- expanded before it is necessary to do so
-
- *)
-
- interface
-
- uses
- {$IFDEF WIN32}Windows{$ELSE}WinTypes, WinProcs{$ENDIF},
- SysUtils, Classes;
-
- { TQList class }
-
- const
- MaxBranchSize = (65536 div SizeOf(pointer)) - 1;
- MaxListSize = MaxBranchSize * MaxBranchSize;
-
- SOutOfMemory = 'Not enough memory to expand the list';
- SListIndexError = 'List index (%d) out of bounds';
- SListCapacityError = 'Invalid capacity value (%d)';
- SListCountError = 'Invalid count value (%d)';
- SDuplicateString = 'List will not accept duplicates';
- SNotStrings = 'Parameter is not a string list';
- SSortedListError = 'Operation not allowed on a sorted list';
-
- type
- EQListError = class(Exception);
-
- type
- PLeafList = ^TLeafList;
- TLeafList = array[0..MaxBranchSize - 1] of pointer;
- PTopList = ^TTopList;
- TTopList = array[0..MaxBranchSize - 1] of PLeafList;
-
- TQListCompare = function(item1, item2 : pointer) : longint;
-
- EListError = class(Exception);
-
- type
- TQList = class(TObject)
- private
- FList : PTopList;
- FCount : longint;
- FCapacity : longint;
- LeafMask : longint; { used to find the index into a leaf }
- LeafLength : longint; { the length of the Leaf array }
- LeafSize : longint; { the memory-size of the Leaf }
- TopSize : longint; { the memory-size of the Top array }
- Power : longint; { the power of two giving the length }
- TopUsed : longint; { the number of active leaves }
- procedure AddLeaf;
- procedure SetPower(p : longint);
- protected
- function Get(Index : longint) : pointer;
- procedure Grow; virtual;
- procedure QuickSort(L, R : longint; SCompare : TQListCompare);
- procedure Put(Index : longint; Item : pointer);
- procedure SetCapacity(NewCapacity : longint);
- procedure SetCount(NewCount : longint);
- public
- constructor Create;
- destructor Destroy; override;
- function Add(Item : pointer) : longint;
- procedure Clear;
- procedure Delete(Index : longint);
- class procedure Error(const Msg : string; Data : longint); virtual;
- procedure Exchange(Index1, Index2 : longint);
- function Expand : TQList;
- function First : pointer;
- function IndexOf(Item : pointer) : longint;
- procedure Insert(Index : longint; Item : pointer);
- function Last : pointer;
- procedure Move(CurIndex, NewIndex : longint);
- function Remove(Item : pointer) : longint;
- procedure Pack;
- procedure Sort(Compare : TQListCompare);
- property Capacity : longint Read FCapacity Write SetCapacity;
- property Count : longint Read FCount Write SetCount;
- property Items[Index : longint] : pointer Read Get Write Put;
- default;
- property List : PTopList Read FList;
- end;
-
-
- { TQStringList class }
-
- TDuplicates = (dupIgnore, dupAccept, dupError);
-
- TQStringList = class(TPersistent)
- private
- FList : TQList;
- FCaseSensitive : boolean;
- FSorted : boolean;
- FDuplicates : TDuplicates;
- FUpdateCount : longint;
- FOnChange : TNotifyEvent;
- FOnChanging : TNotifyEvent;
- function GetName(Index : longint) : string;
- function GetValue(const Name : string) : string;
- function GetValueByIndex(Index : longint) : string;
- procedure QuickSortA(L, R : longint); { case-insensitive }
- procedure QuickSortB(L, R : longint); { case-sensitive }
- procedure ReadData(Reader : TReader);
- procedure SetCaseSensitive(Value : boolean);
- procedure SetSorted(Value : boolean);
- procedure SetValue(const Name, Value : string);
- procedure SetValueByIndex(Index : longint; const Value : string);
- procedure WriteData(Writer : TWriter);
- protected
- procedure DefineProperties(Filer : TFiler); override;
- procedure Changed; virtual;
- procedure Changing; virtual;
- function Get(Index : longint) : string;
- function GetCapacity : longint;
- function GetCount : longint;
- function GetObject(Index : longint) : TObject;
- procedure Put(Index : longint; const S : string);
- procedure PutObject(Index : longint; AObject : TObject);
- procedure SetCapacity(NewCapacity : longint);
- procedure SetUpdateState(Updating : boolean);
- public
- constructor Create;
- destructor Destroy; override;
- function Add(const S : string) : longint;
- function AddObject(const S : string; AObject : TObject) : longint; virtual;
- procedure AddStrings(Strings : TPersistent); virtual;
- procedure Append(const S : string);
- procedure Assign(Source : TPersistent); override;
- procedure AssignTo(Dest : TPersistent); override;
- procedure BeginUpdate;
- procedure Clear;
- procedure Delete(Index : longint);
- procedure EndUpdate;
- function Equals(Strings : TPersistent) : boolean;
- class procedure Error(const Msg : string; Data : longint); virtual;
- procedure Exchange(Index1, Index2 : longint);
- function Find(const S : string; var Index : longint) : boolean; virtual;
- function GetText : pchar; virtual;
- function GetTextStr : string; virtual;
- function IndexOf(const S : string) : longint;
- function IndexOfName(const Name : string) : longint;
- function IndexOfObject(AObject : TObject) : longint;
- procedure Insert(Index : longint; const S : string);
- procedure InsertObject(Index : longint; const S : string; AObject : TObject);
- procedure LoadFromFile(const FileName : string); virtual;
- procedure LoadFromStream(Stream : TStream); virtual;
- procedure Move(CurIndex, NewIndex : longint); virtual;
- procedure Reverse(SetSorted : boolean);
- procedure SaveToFile(const FileName : string); virtual;
- procedure SaveToStream(Stream : TStream); virtual;
- procedure SetText(Text : pchar); virtual;
- procedure SetTextStr(const Value : string); virtual;
- procedure Sort; virtual;
- property CaseSensitive : boolean Read FCaseSensitive Write SetCaseSensitive;
- property Count : longint Read GetCount;
- property Duplicates : TDuplicates Read FDuplicates Write FDuplicates;
- property Sorted : boolean Read FSorted Write SetSorted;
- property Names[Index : longint] : string Read GetName;
- property Objects[Index : longint] : TObject Read GetObject Write PutObject;
- property Values[const Name : string] : string Read GetValue Write SetValue;
- property ValuesByIndex[Index : longint] : string Read GetValueByIndex Write SetValueByIndex;
- property Strings[Index : longint] : string Read Get Write Put;
- default;
- property Text : string Read GetTextStr Write SetTextStr;
- property OnChange : TNotifyEvent Read FOnChange Write FOnChange;
- property OnChanging : TNotifyEvent Read FOnChanging Write FOnChanging;
- end;
-
-
- implementation
-
- { TQList }
-
- const
- PowerMin = 1;
-
- constructor TQList.Create;
- begin
- inherited Create;
- FCount := 0;
- FList := nil;
- TopUsed := 0;
- SetPower(PowerMin);
- SetCapacity(0);
- end;
-
- destructor TQList.Destroy;
- begin
- while TopUsed > 0 do
- begin
- FreeMem(FList^[TopUsed - 1], LeafSize);
- dec(TopUsed);
- end;
- if FList <> nil then
- begin
- FreeMem(FList, TopSize);
- FList := nil;
- end;
- inherited Destroy;
- end;
-
- function TQList.Add(Item : pointer) : longint;
- begin
- Result := FCount;
- if Result = FCapacity then
- Grow;
- if (Result and LeafMask) = 0 then
- AddLeaf;
- FList^[(Result shr Power)]^[(Result and LeafMask)] := Item;
- inc(FCount);
- end;
-
- procedure TQList.AddLeaf;
- var
- NewLeaf : PLeafList;
- begin
- try
- GetMem(NewLeaf, LeafSize);
- FList^[TopUsed] := NewLeaf;
- inc(TopUsed);
- except
- on EOutOfMemory do
- Error(SOutOfMemory, 0)
- else
- raise;
- end;
- end;
-
- procedure TQList.Clear;
- begin
- while TopUsed > 0 do
- begin
- FreeMem(FList^[TopUsed - 1], LeafSize);
- dec(TopUsed);
- end;
- FCount := 0;
- SetCapacity(0);
- end;
-
- procedure TQList.Delete(Index : longint);
- var
- i : longint;
- amount : longint;
- begin
- if (Index < 0) or (Index >= FCount) then
- Error(SListIndexError, Index);
- amount := LeafLength - 1 - (Index and LeafMask);
- { move the first chunk }
- if amount > 0 then
- System.Move(FList^[(Index shr Power)]^[(Index + 1) and LeafMask],
- FList^[(Index shr Power)]^[Index and LeafMask], amount * SizeOf(pointer));
- { then for each leaf upwards }
- for i := (Index shr Power) to TopUsed - 2 do
- begin
- { bring one item down }
- FList^[i]^[LeafLength - 1] := FList^[i + 1]^[0];
- { shift the rest by one }
- System.Move(FList^[i + 1]^[1], FList^[i + 1]^[0], LeafSize - SizeOf(pointer));
- end;
- dec(FCount);
- if (FCount = 0) or (((FCount - 1) shr Power) < (TopUsed - 1)) then
- begin
- FreeMem(FList^[TopUsed - 1], LeafSize);
- dec(TopUsed);
- end;
- end;
-
- class procedure TQList.Error(const Msg : string; Data : longint);
- { this way of locating the return address under all Delphi versions }
- { was devised by Robert Ro▀mair }
- var
- StackTop : record
- end;
- Stack : record
- BPorEBP : integer; { 16 bit: BP, 32 bit: EBP }
- ReturnAddress : pointer;
- end absolute StackTop;
- begin
- raise EQListError.CreateFmt(Msg, [Data])at Stack.ReturnAddress;
- end;
-
- procedure TQList.Exchange(Index1, Index2 : longint);
- var
- Item : pointer;
- begin
- if (Index1 < 0) or (Index1 >= FCount) then
- Error(SListIndexError, Index1);
- if (Index2 < 0) or (Index2 >= FCount) then
- Error(SListIndexError, Index2);
- Item := FList^[(Index1 shr Power)]^[(Index1 and LeafMask)];
- FList^[(Index1 shr Power)]^[(Index1 and LeafMask)] := FList^[(Index2 shr Power)]^[(Index2 and LeafMask)];
- FList^[(Index2 shr Power)]^[(Index2 and LeafMask)] := Item;
- end;
-
- function TQList.Expand : TQList;
- begin
- if FCount = FCapacity then
- Grow;
- Result := Self;
- end;
-
- function TQList.First : pointer;
- begin
- Result := Get(0);
- end;
-
- function TQList.Get(Index : longint) : pointer;
- begin
- if (Index < 0) or (Index >= FCount) then
- Error(SListIndexError, Index);
- Result := FList^[(Index shr Power)]^[(Index and LeafMask)];
- end;
-
- procedure TQList.Grow;
- begin
- { SetCapacity will choose a suitable new value -- the list }
- { capacity grows by powers of two }
- SetCapacity(FCapacity + 1);
- end;
-
- function TQList.IndexOf(Item : pointer) : longint;
- begin
- Result := 0;
- while (Result < FCount) and (FList^[(Result shr Power)]^[(Result and LeafMask)] <> Item) do
- inc(Result);
- if Result = FCount then
- Result := -1;
- end;
-
- procedure TQList.Insert(Index : longint; Item : pointer);
- var
- i : longint;
- amount : longint;
- begin
- if (Index < 0) or (Index > FCount) then
- Error(SListIndexError, Index);
- if FCount = FCapacity then
- Grow;
- if (FCount and LeafMask) = 0 then
- AddLeaf;
- { for each leaf down to the place of insertion }
- for i := TopUsed - 1 downto ((Index shr Power) + 1) do
- begin
- { shift one place up }
- System.Move(FList^[i]^[0], FList^[i]^[1], LeafSize - SizeOf(pointer));
- { bring one item up }
- FList^[i]^[0] := FList^[i - 1]^[LeafLength - 1];
- end;
- amount := LeafLength - 1 - (Index and LeafMask);
- { shift to make room for new item }
- System.Move(FList^[Index shr Power]^[(Index and LeafMask)],
- FList^[Index shr Power]^[(Index and LeafMask) + 1], amount * SizeOf(pointer));
- { insert the item itself }
- FList^[(Index shr Power)]^[(Index and LeafMask)] := Item;
- inc(FCount);
- end;
-
- function TQList.Last : pointer;
- begin
- Result := Get(FCount - 1);
- end;
-
- procedure TQList.Move(CurIndex, NewIndex : longint);
- var
- Item : pointer;
- begin
- if CurIndex <> NewIndex then
- begin
- if (NewIndex < 0) or (NewIndex >= FCount) then
- Error(SListIndexError, NewIndex);
- Item := Get(CurIndex);
- Delete(CurIndex);
- Insert(NewIndex, Item);
- end;
- end;
-
- procedure TQList.Put(Index : longint; Item : pointer);
- begin
- if (Index < 0) or (Index >= FCount) then
- Error(SListIndexError, Index);
- FList^[(Index shr Power)]^[(Index and LeafMask)] := Item;
- end;
-
- function TQList.Remove(Item : pointer) : longint;
- begin
- Result := IndexOf(Item);
- if Result <> -1 then
- Delete(Result);
- end;
-
- procedure TQList.Pack;
- var
- i : longint;
- begin
- for i := FCount - 1 downto 0 do
- if Items[i] = nil then
- Delete(i);
- SetCapacity(FCount);
- end;
-
- { this is one of the chief engines of the Hashed Array Tree ... }
-
- procedure TQList.SetCapacity(NewCapacity : longint);
- var
- NewPower : longint;
- NewSize : longint;
- NewList : PTopList;
- NewLeaf : PLeafList;
- NewTopUsed : longint;
- Ratio : longint;
- i, j : longint;
-
- function RecommendedPower(NewCapacity : longint) : longint;
- begin
- { compute the root of s to the nearest greater power of 2 }
- Result := PowerMin;
- while NewCapacity >= (1 shl (Result shl 1)) do
- inc(Result);
- end;
-
- begin
- { calculate the parameters of the 'new' qlist }
- NewPower := RecommendedPower(NewCapacity);
- NewSize := (1 shl NewPower) * SizeOf(pointer);
- NewCapacity := (1 shl (NewPower shl 1));
- if (NewCapacity < FCount) or (NewCapacity > MaxListSize) then
- Error(SListCapacityError, NewCapacity);
- if NewCapacity <> FCapacity then
- begin
- { begin to build a new qlist }
- try
- GetMem(NewList, NewSize);
- except
- on EOutOfMemory do
- Error(SOutOfMemory, 0)
- else
- raise
- end;
- if FCount > 0 then
- begin
- { only relevant if the list has members }
- NewTopUsed := ((FCount - 1) shr NewPower) + 1;
- { how many old leaves fit into a new one }
- Ratio := (NewSize div LeafSize);
- { for each old leaf }
- for i := 0 to TopUsed - 1 do
- begin
- { if a new leaf is needed }
- if i mod Ratio = 0 then
- begin
- try
- { add a new leaf }
- GetMem(NewLeaf, NewSize);
- except
- on EOutOfMemory do
- { get rid of the partly built qlist }
- begin
- j := i;
- dec(j, Ratio);
- while j >= 0 do
- FreeMem(NewList^[j], NewSize);
- FreeMem(NewList, NewSize);
- Error(SOutOfMemory, 0);
- end
- else
- raise;
- end;
- { put the leaf into the tree }
- NewList^[i div Ratio] := NewLeaf;
- end;
- { move the old leaf to its place in the new }
- System.Move(FList^[i]^[0], NewList^[i div Ratio]^[(LeafLength * (i mod Ratio))], LeafSize);
- { get rid of the old leaf }
- FreeMem(FList^[i], LeafSize);
- end;
- TopUsed := NewTopUsed;
- end;
- { get rid of the old qlist }
- if FList <> nil then
- FreeMem(FList, TopSize);
- { assign the new qlist instead }
- FList := NewList;
- { adjust the qlist parameters }
- SetPower(NewPower);
- FCapacity := NewCapacity;
- end;
- end;
-
- procedure TQList.SetCount(NewCount : longint);
- var
- i : longint;
- begin
- if (NewCount < 0) or (NewCount > MaxListSize) then
- Error(SListCountError, NewCount);
- if NewCount > FCapacity then
- SetCapacity(NewCount);
- { if we are shrinking the list we blank out the unwanted }
- { items -- if they point to anything there'll be a leak }
- if NewCount > FCount then
- for i := FCount to NewCount do
- FList^[(i shr Power)]^[(i and LeafMask)] := nil;
- FCount := NewCount;
- end;
-
- procedure TQList.SetPower(p : longint);
- begin
- Power := p;
- LeafLength := (1 shl Power);
- LeafSize := LeafLength * SizeOf(pointer);
- LeafMask := LeafLength - 1;
- TopSize := LeafSize;
- end;
-
- procedure TQList.QuickSort(L, R : longint; SCompare : TQListCompare);
- var
- i, j : longint;
- p, T : pointer;
- begin
- repeat
- i := L;
- j := R;
- p := FList^[(((L + R) shr 1) shr Power)]^[(((L + R) shr 1) and LeafMask)];
- repeat
- while SCompare(FList^[(i shr Power)]^[(i and LeafMask)], p) < 0 do
- inc(i);
- while SCompare(FList^[(j shr Power)]^[(j and LeafMask)], p) > 0 do
- dec(j);
- if i <= j then
- begin
- T := FList^[(i shr Power)]^[(i and LeafMask)];
- FList^[(i shr Power)]^[(i and LeafMask)] := FList^[(j shr Power)]^[(j and LeafMask)];
- FList^[(j shr Power)]^[(j and LeafMask)] := T;
- inc(i);
- dec(j);
- end;
- until i > j;
- if L < j then
- QuickSort(L, j, SCompare);
- L := i;
- until i >= R;
- end;
-
- procedure TQList.Sort(Compare : TQListCompare);
- begin
- if (FList <> nil) and (Count > 0) then
- QuickSort(0, Count - 1, Compare);
- end;
-
-
- { TQStringList }
-
- const
- CR = #13;
- EoF = ^Z;
- LF = #10;
- NUL = #0;
-
- type
- PStrItem = ^TStrItem;
- TStrItem = record
- FString : pchar;
- FObject : TObject;
- end;
-
- function NewStrItem(const AString : string; AObject : TObject) : PStrItem;
- var
- p : PStrItem;
- c : pchar;
- begin
- GetMem(p, SizeOf(TStrItem));
- GetMem(c, Length(AString) + 1);
- StrPCopy(c, AString);
- p^.FObject := AObject;
- p^.FString := c;
- Result := p;
- end;
-
- procedure DisposeStrItem(p : PStrItem);
- begin
- FreeMem(p^.FString, StrLen(p^.FString) + 1);
- FreeMem(p, SizeOf(TStrItem));
- end;
-
- { The following routines to manipulate huge pointers and to copy }
- { and extract lines come from Secrets of Delphi 2, by Ray Lischner.}
- { (1996, Waite Group Press) Copyright ⌐ 1996 The Waite Group, Inc. }
-
- procedure HugeShift;
- far; external 'KERNEL' Index 113;
-
- {$IFDEF WIN32}
-
- function HugeOffset(p : pointer; size : longint) : pointer;
- begin
- Result := pchar(p) + size;
- end;
-
- procedure HMemCpy(PDest, PSource : pointer; size : longint);
- begin
- System.Move(PSource^, PDest^, size);
- end;
-
- {$ELSE}
-
- function HugeOffset(p : pointer; size : longint) : pointer;
- assembler;
- asm
- mov ax, Size.Word[0]
- mov dx, Size.Word[2]
- add ax, p.Word[0]
- adc dx, 0
- mov cx, OFFSET HugeShift
- shl dx, cl
- add dx, p.Word[2]
- end;
- {$ENDIF}
-
- { Copy the string, Str, to Text, and append a CR-LF. }
- { Even though this function copies no more than 256 bytes, use }
- { HMemCopy, since Text might cross a segment boundary. }
-
- function CopyLine(Text : pchar; Str : pchar) : pchar;
- begin
- HMemCpy(Text, Str, StrLen(Str));
- Result := HugeOffset(Text, StrLen(Str));
- { Now append the CR-LF }
- Result^ := CR;
- Result := HugeOffset(Result, 1);
- Result^ := LF;
- Result := HugeOffset(Result, 1);
- end;
-
- { Extract the next line from Text, and copy it into Str.
- Advance Text past the end of the string. A string ends with
- a line ending (CRLF, CR, or LF), at the end of the string
- (Ctrl+Z or zero byte), or at 255 bytes in Delphi 1.0. A string
- can cross a segment boundary, so SetString cannot be used in
- Delphi 1.0. The line ending is not included in the string Str. }
-
- function ExtractLine(Text : pchar; var Str : string) : pchar;
- var
- Len : integer;
- begin
- Len := 0;
- Result := Text;
- while not (Result^ in [NUL, LF, CR, EoF]) do
- begin
- Result := HugeOffset(Result, 1);
- inc(Len);
- {$IFNDEF WIN32}
- if Len = 255 then
- Break;
- {$ENDIF}
- end;
- {$IFDEF WIN32}
- SetString(Str, Text, Len);
- {$ELSE}
- { Cannot use SetString, since Text might cross a segment boundary. }
- Str[0] := chr(Len);
- HMemCpy(@Str[1], Text, Len);
- {$ENDIF}
- if Result^ = CR then
- Result := HugeOffset(Result, 1);
- if Result^ = LF then
- Result := HugeOffset(Result, 1);
- end;
-
- constructor TQStringList.Create;
- begin
- inherited Create;
- FList := TQList.Create;
- end;
-
- destructor TQStringList.Destroy;
- begin
- FOnChange := nil;
- FOnChanging := nil;
- Clear;
- FList.Free;
- inherited Destroy;
- end;
-
- function TQStringList.Add(const S : string) : longint;
- begin
- if not Sorted then
- Result := FList.Count
- else
- if Find(S, Result) then
- case Duplicates of
- dupIgnore :
- exit;
- dupError :
- Error(SDuplicateString, 0);
- end;
- Changing;
- FList.Insert(Result, NewStrItem(S, nil));
- Changed;
- end;
-
- function TQStringList.AddObject(const S : string; AObject : TObject) : longint;
- begin
- if not Sorted then
- Result := FList.Count
- else
- if Find(S, Result) then
- case Duplicates of
- dupIgnore :
- exit;
- dupError :
- Error(SDuplicateString, 0);
- end;
- Changing;
- FList.Insert(Result, NewStrItem(S, AObject));
- Changed;
- end;
-
- procedure TQStringList.AddStrings(Strings : TPersistent);
- var
- i : longint;
- begin
- if (Strings is TStrings) then
- begin
- BeginUpdate;
- try
- for i := 0 to TStrings(Strings).Count - 1 do
- AddObject(TStrings(Strings)[i], TStrings(Strings).Objects[i]);
- finally
- EndUpdate;
- end;
- exit;
- end;
- if (Strings is TQStringList) then
- begin
- BeginUpdate;
- try
- for i := 0 to TQStringList(Strings).Count - 1 do
- AddObject(TQStringList(Strings)[i], TQStringList(Strings).Objects[i]);
- finally
- EndUpdate;
- end;
- exit;
- end;
- Error(SNotStrings, 0);
- end;
-
- procedure TQStringList.Append(const S : string);
- begin
- Add(S);
- end;
-
- procedure TQStringList.Assign(Source : TPersistent);
- begin
- if (Source is TQStringList) or (Source is TStrings) then
- begin
- BeginUpdate;
- try
- Clear;
- AddStrings(Source);
- finally
- EndUpdate;
- end;
- exit;
- end;
- inherited Assign(Source);
- end;
-
- procedure TQStringList.AssignTo(Dest : TPersistent);
- var
- i : longint;
- p : PStrItem;
- begin
- if Dest is TStrings then
- begin
- TStrings(Dest).BeginUpdate;
- try
- TStrings(Dest).Clear;
- for i := 1 to Count do
- begin
- p := FList[i - 1];
- TStrings(Dest).AddObject(StrPas(p^.FString), p^.FObject);
- end;
- finally
- TStrings(Dest).EndUpdate;
- end;
- exit;
- end;
- if Dest is TQStringList then
- begin
- TQStringList(Dest).BeginUpdate;
- try
- TQStringList(Dest).Clear;
- for i := 1 to Count do
- begin
- p := FList[i - 1];
- TQStringList(Dest).AddObject(StrPas(p^.FString), p^.FObject);
- end;
- finally
- TQStringList(Dest).EndUpdate;
- end;
- exit;
- end;
- inherited AssignTo(Dest);
- end;
-
- procedure TQStringList.BeginUpdate;
- begin
- if FUpdateCount = 0 then
- SetUpdateState(true);
- inc(FUpdateCount);
- end;
-
- procedure TQStringList.Changed;
- begin
- if (FUpdateCount = 0) and Assigned(FOnChange) then
- FOnChange(Self);
- end;
-
- procedure TQStringList.Changing;
- begin
- if (FUpdateCount = 0) and Assigned(FOnChanging) then
- FOnChanging(Self);
- end;
-
- procedure TQStringList.Clear;
- var
- i : longint;
- begin
- Changing;
- for i := 1 to FList.Count do
- begin
- DisposeStrItem(FList[i - 1]);
- FList[i - 1] := nil;
- end;
- FList.Clear;
- Changed;
- end;
-
- procedure TQStringList.Delete(Index : longint);
- begin
- if (Index < 0) or (Index >= FList.Count) then
- Error(SListIndexError, Index);
- Changing;
- DisposeStrItem(FList[Index]);
- FList.Delete(Index);
- Changed;
- end;
-
- procedure TQStringList.DefineProperties(Filer : TFiler);
-
- {$IFDEF WIN32}
-
- function DoWrite : boolean;
- begin
- if Filer.Ancestor <> nil then
- begin
- Result := true;
- if Filer.Ancestor is TQStringList then
- Result := not Equals(TQStringList(Filer.Ancestor))
- end
- else
- Result := Count > 0;
- end;
-
- {$ENDIF}
-
- begin
- {$IFDEF WIN32}
- Filer.DefineProperty('Strings', ReadData, WriteData, DoWrite);
- {$ELSE}
- Filer.DefineProperty('Strings', ReadData, WriteData, Count > 0);
- {$ENDIF}
- end;
-
- procedure TQStringList.EndUpdate;
- begin
- dec(FUpdateCount);
- if FUpdateCount = 0 then
- SetUpdateState(false);
- end;
-
- function TQStringList.Equals(Strings : TPersistent) : boolean;
- var
- i : longint;
- begin
- Result := false;
- if Strings is TStrings then
- begin
- if Count <> TStrings(Strings).Count then
- exit;
- for i := 0 to Count - 1 do
- if Get(i) <> TStrings(Strings)[i] then
- exit;
- end;
- if Strings is TQStringList then
- begin
- if Count <> TQStringList(Strings).Count then
- exit;
- for i := 0 to Count - 1 do
- if Get(i) <> TQStringList(Strings).Get(i) then
- exit;
- end;
- Result := true;
- end;
-
- class procedure TQStringList.Error(const Msg : string; Data : longint);
- { this way of locating the return address under all Delphi versions }
- { was devised by Robert Ro▀mair }
- var
- StackTop : record
- end;
- Stack : record
- BPorEBP : integer; { 16 bit: BP, 32 bit: EBP }
- ReturnAddress : pointer;
- end absolute StackTop;
- begin
- raise EQListError.CreateFmt(Msg, [Data])at Stack.ReturnAddress;
- end;
-
- procedure TQStringList.Exchange(Index1, Index2 : longint);
- begin
- if (Index1 < 0) or (Index1 >= FList.Count) then
- Error(SListIndexError, Index1);
- if (Index2 < 0) or (Index2 >= FList.Count) then
- Error(SListIndexError, Index2);
- Changing;
- FList.Exchange(Index1, Index2);
- Changed;
- end;
-
- function TQStringList.Find(const S : string; var Index : longint) : boolean;
- var
- L, H, i, c : longint;
- begin
- Result := false;
- L := 0;
- H := FList.Count - 1;
- while L <= H do
- begin
- i := (L + H) shr 1;
- if CaseSensitive then
- c := AnsiCompareStr(StrPas(PStrItem(FList[i])^.FString), S)
- else
- c := AnsiCompareText(StrPas(PStrItem(FList[i])^.FString), S);
- if c < 0 then
- L := i + 1
- else
- begin
- H := i - 1;
- if c = 0 then
- begin
- Result := true;
- if Duplicates <> dupAccept then
- L := i;
- end;
- end;
- end;
- Index := L;
- end;
-
- function TQStringList.Get(Index : longint) : string;
- begin
- if (Index < 0) or (Index >= FList.Count) then
- Error(SListIndexError, Index);
- Result := StrPas(PStrItem(FList[Index])^.FString);
- end;
-
- function TQStringList.GetCapacity : longint;
- begin
- Result := FList.Capacity;
- end;
-
- function TQStringList.GetCount : longint;
- begin
- Result := FList.Count;
- end;
-
- function TQStringList.GetName(Index : longint) : string;
- var
- p : integer;
- begin
- Result := Get(Index);
- p := Pos('=', Result);
- {$IFDEF WIN32}
- if p <> 0 then
- SetLength(Result, p - 1)
- else
- SetLength(Result, 0);
- {$ELSE}
- if p <> 0 then
- Result[0] := char(p - 1)
- else
- Result[0] := #0;
- {$ENDIF}
- end;
-
- function TQStringList.GetObject(Index : longint) : TObject;
- begin
- if (Index < 0) or (Index >= FList.Count) then
- Error(SListIndexError, Index);
- Result := PStrItem(FList[Index])^.FObject;
- end;
-
- function TQStringList.GetText : pchar;
- var
- i, size : longint;
- p : pchar;
- begin
- { how big must the text be }
- size := 1; { for the trailing #0 byte }
- for i := 0 to Count - 1 do
- size := size + StrLen(PStrItem(FList.Items[i])^.FString) + 2; { for the CR-LF }
- Result := GlobalAllocPtr(GMem_Fixed or GMem_Share, size);
- if Result = nil then
- OutOfMemoryError;
- { then copy all the strings into the result }
- p := Result;
- for i := 0 to Count - 1 do
- p := CopyLine(p, PStrItem(FList.Items[i])^.FString);
- p[0] := NUL;
- end;
-
- function TQStringList.GetTextStr : string;
- begin
- Result := StrPas(GetText);
- end;
-
- function TQStringList.GetValue(const Name : string) : string;
- var
- i : longint;
- begin
- i := IndexOfName(Name);
- if i >= 0 then
- Result := Copy(Get(i), Length(Name) + 2, MaxInt)
- else
- Result := '';
- end;
-
- function TQStringList.GetValueByIndex(Index : longint) : string;
- var
- p : integer;
- S : string;
- begin
- if (Index < 0) or (Index >= FList.Count) then
- Error(SListIndexError, Index);
- S := Get(Index);
- p := Pos('=', S);
- if p <> 0 then
- Result := Copy(S, p + 1, MaxInt)
- else
- Result := '';
- end;
-
- function TQStringList.IndexOf(const S : string) : longint;
- begin
- if not Sorted then
- begin
- for Result := 0 to FList.Count - 1 do
- if AnsiCompareText(Get(Result), S) = 0 then
- exit;
- Result := -1;
- end
- else
- if not Find(S, Result) then
- Result := -1;
- end;
-
- function TQStringList.IndexOfName(const Name : string) : longint;
- var
- p : longint;
- S : string;
- begin
- for Result := 0 to FList.Count - 1 do
- begin
- S := Get(Result);
- p := Pos('=', S);
- if FCaseSensitive then
- begin
- if (p <> 0) and (AnsiCompareStr(Copy(S, 1, p - 1), Name) = 0) then
- exit;
- end
- else
- begin
- if (p <> 0) and (AnsiCompareText(Copy(S, 1, p - 1), Name) = 0) then
- exit;
- end;
- end;
- Result := -1;
- end;
-
- function TQStringList.IndexOfObject(AObject : TObject) : longint;
- begin
- for Result := 0 to GetCount - 1 do
- if GetObject(Result) = AObject then
- exit;
- Result := -1;
- end;
-
- procedure TQStringList.Insert(Index : longint; const S : string);
- begin
- if Sorted then
- Error(SSortedListError, 0);
- if (Index < 0) or (Index > FList.Count) then
- Error(SListIndexError, Index);
- Changing;
- FList.Insert(Index, NewStrItem(S, nil));
- Changed;
- end;
-
- procedure TQStringList.InsertObject(Index : longint; const S : string; AObject : TObject);
- begin
- if Sorted then
- Error(SSortedListError, 0);
- if (Index < 0) or (Index > FList.Count) then
- Error(SListIndexError, Index);
- Changing;
- FList.Insert(Index, NewStrItem(S, AObject));
- Changed;
- end;
-
- procedure TQStringList.LoadFromFile(const FileName : string);
- var
- Stream : TStream;
- begin
- Stream := TFileStream.Create(FileName, fmOpenRead);
- try
- LoadFromStream(Stream);
- finally
- Stream.Free;
- end;
- end;
-
- procedure TQStringList.LoadFromStream(Stream : TStream);
- const
- BufSize = 8192;
- var
- Len : longint;
- Buffer, BufPtr, BufEnd, BufLast : pchar;
- LineLast : pchar;
- S : string;
- begin
- BeginUpdate;
- try
- Clear;
- GetMem(Buffer, BufSize);
- try
- BufEnd := Buffer + BufSize;
- BufLast := BufEnd;
- LineLast := BufLast;
- repeat
- { Copy the last partial line to the start of the buffer }
- Len := BufLast - LineLast;
- if Len <> 0 then
- System.Move(LineLast^, Buffer^, Len);
- BufLast := Buffer + Len;
- { fill the rest of the buffer }
- BufLast := BufLast + Stream.Read(BufLast^, BufEnd - BufLast);
- { find the last end-of-line in the buffer }
- LineLast := BufLast;
- while LineLast > Buffer do
- begin
- dec(LineLast);
- if LineLast^ in [NUL, LF, CR, EoF] then
- Break;
- end;
- if LineLast^ = CR then
- inc(LineLast);
- if LineLast^ = LF then
- inc(LineLast);
- { Now split the buffer into lines }
- BufPtr := Buffer;
- while (BufPtr < LineLast) and (LineLast > Buffer) do
- begin
- BufPtr := ExtractLine(BufPtr, S);
- Add(S);
- end;
- until BufLast = Buffer;
- finally
- FreeMem(Buffer, BufSize);
- end;
- finally
- EndUpdate;
- end;
- end;
-
- procedure TQStringList.Move(CurIndex, NewIndex : longint);
- begin
- Changing;
- FList.Move(CurIndex, NewIndex);
- Changed;
- end;
-
- procedure TQStringList.Put(Index : longint; const S : string);
- var
- p : PStrItem;
- begin
- if Sorted then
- Error(SSortedListError, 0);
- if (Index < 0) or (Index >= FList.Count) then
- Error(SListIndexError, Index);
- Changing;
- p := FList[Index];
- FList[Index] := NewStrItem(S, p^.FObject);
- DisposeStrItem(p);
- Changed;
- end;
-
- procedure TQStringList.PutObject(Index : longint; AObject : TObject);
- var
- p : PStrItem;
- begin
- if (Index < 0) or (Index >= FList.Count) then
- Error(SListIndexError, Index);
- Changing;
- p := FList[Index];
- FList[Index] := NewStrItem(Strings[Index], AObject);
- DisposeStrItem(p);
- Changed;
- end;
-
- procedure TQStringList.QuickSortA(L, R : longint);
- var
- i, j : longint;
- p : string;
- begin
- repeat
- i := L;
- j := R;
- p := StrPas(PStrItem(FList[(L + R) shr 1])^.FString);
- repeat
- while AnsiCompareText(StrPas(PStrItem(FList[i])^.FString), p) < 0 do
- inc(i);
- while AnsiCompareText(StrPas(PStrItem(FList[j])^.FString), p) > 0 do
- dec(j);
- if i <= j then
- begin
- FList.Exchange(i, j);
- inc(i);
- dec(j);
- end;
- until i > j;
- if L < j then
- QuickSortA(L, j);
- L := i;
- until i >= R;
- end;
-
- procedure TQStringList.QuickSortB(L, R : longint);
- var
- i, j : longint;
- p : string;
- begin
- repeat
- i := L;
- j := R;
- p := StrPas(PStrItem(FList[(L + R) shr 1])^.FString);
- repeat
- while AnsiCompareStr(StrPas(PStrItem(FList[i])^.FString), p) < 0 do
- inc(i);
- while AnsiCompareStr(StrPas(PStrItem(FList[j])^.FString), p) > 0 do
- dec(j);
- if i <= j then
- begin
- FList.Exchange(i, j);
- inc(i);
- dec(j);
- end;
- until i > j;
- if L < j then
- QuickSortB(L, j);
- L := i;
- until i >= R;
- end;
-
- procedure TQStringList.ReadData(Reader : TReader);
- begin
- Reader.ReadListBegin;
- BeginUpdate;
- try
- Clear;
- while not Reader.EndOfList do
- Add(Reader.ReadString);
- finally
- EndUpdate;
- end;
- Reader.ReadListEnd;
- end;
-
-
- procedure TQStringList.Reverse(SetSorted : boolean);
- var
- n, m : longint;
- begin
- if Sorted then
- exit;
- n := 1;
- m := FList.Count;
- while n < m do
- begin
- FList.Exchange(n - 1, m - 1);
- inc(n);
- dec(m);
- end;
- if SetSorted then
- FSorted := true;
- end;
-
- procedure TQStringList.SaveToFile(const FileName : string);
- var
- Stream : TStream;
- begin
- Stream := TFileStream.Create(FileName, fmCreate);
- try
- SaveToStream(Stream);
- finally
- Stream.Free;
- end;
- end;
-
- procedure TQStringList.SaveToStream(Stream : TStream);
- const
- BufSize = 8192;
- var
- i : longint;
- Buffer, BufPtr : pchar;
- begin
- GetMem(Buffer, BufSize);
- try
- BufPtr := Buffer;
- for i := 0 to Count - 1 do
- begin
- { Is there room for a string + CRLF? }
- if BufPtr - Buffer >= BufSize - 256 then
- begin
- Stream.WriteBuffer(Buffer[0], BufPtr - Buffer);
- BufPtr := Buffer;
- end;
- StrCopy(BufPtr, PStrItem(FList.Items[i])^.FString);
- inc(BufPtr, StrLen(PStrItem(FList.Items[i])^.FString));
- BufPtr[0] := CR;
- BufPtr[1] := LF;
- inc(BufPtr, 2);
- end;
- Stream.WriteBuffer(Buffer[0], BufPtr - Buffer);
- finally
- FreeMem(Buffer, BufSize);
- end;
- end;
-
- procedure TQStringList.SetCapacity(NewCapacity : longint);
- begin
- FList.Capacity := NewCapacity;
- end;
-
- procedure TQStringList.SetCaseSensitive(Value : boolean);
- var
- n : longint;
- begin
- if Count = 0 then
- FCaseSensitive := Value;
- if FCaseSensitive <> Value then
- begin
- FCaseSensitive := Value;
- if not FCaseSensitive then
- begin
- {check for duplicates and delete them }
- n := Count - 1;
- while n > 0 do
- begin
- if AnsiCompareText(Get(n - 1), Get(n)) = 0 then
- Delete(n);
- dec(n);
- end;
- end;
- end;
- end;
-
- procedure TQStringList.SetSorted(Value : boolean);
- begin
- if FSorted <> Value then
- begin
- if Value then
- Sort;
- FSorted := Value;
- end;
- end;
-
- procedure TQStringList.SetText(Text : pchar);
- var
- S : string;
- begin
- BeginUpdate;
- try
- Clear;
- while not (Text^ in [NUL, EoF]) do
- begin
- Text := ExtractLine(Text, S);
- Add(S);
- end;
- finally
- EndUpdate;
- end;
- end;
-
- procedure TQStringList.SetTextStr(const Value : string);
- var
- p : pchar;
- begin
- BeginUpdate;
- try
- p := StrAlloc(Length(Value) + 1);
- try
- StrPCopy(p, Value);
- SetText(p);
- finally
- StrDispose(p);
- end;
- finally
- EndUpdate;
- end;
- end;
-
- procedure TQStringList.SetUpdateState(Updating : boolean);
- begin
- if Updating then
- Changing
- else
- Changed;
- end;
-
- procedure TQStringList.SetValue(const Name, Value : string);
- var
- i : longint;
- begin
- i := IndexOfName(Name);
- if Value <> '' then
- begin
- if i < 0 then
- i := Add('');
- Put(i, Name + '=' + Value);
- end
- else
- begin
- if i >= 0 then
- Delete(i);
- end;
- end;
-
- procedure TQStringList.SetValueByIndex(Index : longint; const Value : string);
- begin
- if Value <> '' then
- begin
- Put(Index, GetName(Index) + '=' + Value);
- end
- else
- begin
- if Index >= 0 then
- Delete(Index);
- end;
- end;
-
- procedure TQStringList.Sort;
- begin
- if not Sorted and (FList.Count > 1) then
- begin
- Changing;
- if FCaseSensitive then
- QuickSortB(0, FList.Count - 1)
- else
- QuickSortA(0, FList.Count - 1);
- Changed;
- end;
- end;
-
- procedure TQStringList.WriteData(Writer : TWriter);
- var
- i : longint;
- begin
- Writer.WriteListBegin;
- for i := 0 to Count - 1 do
- Writer.WriteString(Get(i));
- Writer.WriteListEnd;
- end;
-
-
- end.
-