home *** CD-ROM | disk | FTP | other *** search
Wrap
{*******************************************************} { } { Borland Delphi Visual Component Library } { } { Copyright (c) 1997,99 Inprise Corporation } { } {*******************************************************} unit mxarrays; interface uses SysUtils, Windows, Classes, mxconsts; type { Exceptions } EArrayError = class(Exception); EUnsupportedTypeError = class(Exception); ELowCapacityError = class(Exception); TCompareProc = function(var item1, item2): Integer; TSortOrder = (tsNone, tsAscending, tsDescending); { These flags govern some of the behaviour of array methods } TArrayFlags = (afOwnsData, afAutoSize, afCanCompare, afSortUnique); TArrayFlagSet = Set of TArrayFlags; TDuplicates = (dupIgnore, dupAccept, dupError); TStringItem = record FString: string; FObject: TObject; end; { This is the base array object that all other array classes inheret from } TBaseArray = class(TPersistent) private FMemory: Pointer; { Pointer to item buffer } FCapacity: Integer; { The allocated size of the array } FItemSize: Integer; { Size of individual item in bytes } FCount: Integer; { Count of items in use } FSortOrder: TSortOrder; { True if array is considered sorted } FFlags: TArrayFlagSet; { Ability flags } FDuplicates: TDuplicates; { Signifies if duplicates are stored or not } FCompProc: TCompareProc; function GetItemPtr(index: Integer): Pointer; procedure CopyFrom(toIndex, numItems: Integer; var Source); procedure SetCount(NewCount: Integer); function GetLimit: Integer; protected function ValidIndex(Index: Integer): Boolean; function HasFlag(aFlag: TArrayFlags): Boolean; procedure SetFlag(aFlag: TArrayFlags); procedure ClearFlag(aFlag: TArrayFlags); procedure SetAutoSize(aSize: Boolean); procedure BlockCopy(Source: TBaseArray; fromIndex, toIndex, numitems: Integer); function GetAutoSize: Boolean; function ValidateBounds(atIndex: Integer; var numItems: Integer): Boolean; procedure RemoveRange(atIndex, numItems: Integer); procedure InternalHandleException; procedure InvalidateItems(atIndex, numItems: Integer); virtual; procedure SetCapacity(NewCapacity: Integer); virtual; procedure Grow; virtual; public constructor Create(itemcount, iSize: Integer); virtual; destructor Destroy; override; procedure Clear; procedure InsertAt(Index: Integer; var Value); procedure Insert(Index: Integer; var Value); virtual; procedure PutItem(index: Integer; var Value); procedure GetItem(index: Integer; var Value); procedure RemoveItem(Index: Integer); procedure Delete(Index: Integer); virtual; procedure Exchange(Index1, Index2: Integer); virtual; function IndexOf(var Item): Integer; virtual; function FindItem(var Index: Integer; var Value): Boolean; procedure Sort(Compare: TCompareProc); virtual; property CompareProc: TCompareProc read FCompProc write FCompProc; property Duplicates: TDuplicates read FDuplicates write FDuplicates; property SortOrder: TSortOrder read FSortOrder write FSortOrder; property Capacity: Integer read FCapacity write SetCapacity; property Limit: Integer read GetLimit write SetCapacity; property ItemSize: Integer read FItemSize; property AutoSize: Boolean read GetAutoSize write SetAutoSize; property Count: Integer read FCount write SetCount; property List: Pointer read FMemory; end; TSmallIntArray = class(TBaseArray) public constructor Create(itemcount, dummy: Integer); override; procedure PutItem(index: Integer; value: SmallInt); function GetItem(index: Integer): SmallInt; function Add(Value: SmallInt): Integer; procedure Assign(Source: TPersistent); override; property Items[Index:Integer]: SmallInt read GetItem write PutItem; default; end; TIntArray = class(TBaseArray) public constructor Create(itemcount, dummy: Integer); override; procedure PutItem(index: Integer; value: Integer); function GetItem(index: Integer): Integer; function Add(Value: Integer): Integer; procedure Assign(Source: TPersistent); override; function Find(var Index: Integer; Value: Integer): Boolean; property Items[Index: Integer]: Integer read GetItem write PutItem; default; end; TSingleArray = class(TBaseArray) public constructor Create(itemcount, dummy: Integer); override; procedure PutItem(index: Integer; value: Single); function GetItem(index: Integer): Single; function Add(Value: Single): Integer; function Find(var Index: Integer; Value: Single): Boolean; function IndexOf(var Item): Integer; override; procedure Assign(Source: TPersistent); override; property Items[Index: Integer]: Single read GetItem write PutItem; default; end; TDoubleArray = class(TBaseArray) public constructor Create(itemcount, dummy: Integer); override; procedure PutItem(index: Integer; value: Double); function GetItem(index: Integer): Double; function Add(Value: Double): Integer; function Find(var Index: Integer; Value: Double): Boolean; function IndexOf(var Item): Integer; override; procedure Assign(Source: TPersistent); override; property Items[Index: Integer]: Double read GetItem write PutItem; default; end; TCurrencyArray = class(TBaseArray) public constructor Create(itemcount, dummy: Integer); override; procedure PutItem(index: Integer; value: Currency); function GetItem(index: Integer): Currency; function Add(Value: Currency): Integer; function Find(var Index: Integer; Value: Currency): Boolean; function IndexOf(var Item): Integer; override; procedure Assign(Source: TPersistent); override; property Items[Index: Integer]: Currency read GetItem write PutItem; default; end; TWordArray = class(TBaseArray) public constructor Create(itemcount, dummy: Integer); override; procedure PutItem(index: Integer; value: Word); function GetItem(index: Integer): Word; function Add(Value: Word): Integer; function Find(var Index: Integer; Value: Word): Boolean; function IndexOf(var Item): Integer; override; procedure Assign(Source: TPersistent); override; property Items[Index: Integer]: Word read GetItem write PutItem; default; end; TPointerArray = class(TBaseArray) public constructor Create(itemcount, dummy: Integer); override; procedure PutData(index: Integer; value: Pointer); function GetData(index: Integer): Pointer; procedure CopyFrom(var Source; toIndex, numItems: Integer); procedure CopyTo(var Dest; fromIndex, numItems: Integer); procedure InvalidateItems(atIndex, numItems: Integer); override; function CloneItem(item: Pointer): Pointer; virtual; procedure FreeItem(item: Pointer); virtual; property AsPtr[Index: Integer]: Pointer read GetData write PutData; property Data[Index: Integer]: Pointer read GetData write PutData; end; TStringArray = class(TBaseArray) private procedure ExchangeItems(Index1, Index2: Integer); procedure QuickSort(L, R: Integer); procedure InsertItem(Index: Integer; const S: string); procedure AddStrings(Strings: TStringArray); protected function GetString(Index: Integer): string; procedure PutString(Index: Integer; const S: string); function GetObject(Index: Integer): TObject; procedure PutObject(Index: Integer; AObject: TObject); procedure InvalidateItems(atIndex, numItems: Integer); override; procedure Grow; override; public constructor Create(itemcount, dummy: Integer);override; function Add(const S: String): Integer; procedure Assign(Source: TPersistent); override; procedure Exchange(Index1, Index2: Integer); override; function Find(S: string; var Index: Integer): Boolean; function IndexOf(var Item): Integer; override; procedure Sort(Compare: TCompareProc); override; procedure Insert(Index: Integer; var Value); override; property Strings[Index: Integer]: String read GetString write PutString; default; end; Const vMaxRow = (High(Integer)-$f) div Sizeof(SmallInt); vMaxCol = High(Integer) div Sizeof(TSmallIntArray)-1; type TMatrixNDX = 0..vMaxCol; TDynArrayNDX = 0..vMaxRow; TMatrixElements = array[TMatrixNDX] of TSmallIntArray; PMatrixElements = ^TMatrixElements; EDynArrayRangeError = class(ERangeError); TTwoDimArray = class Private FRows: TDynArrayNDX; FColumns: TMatrixNDX; FMemAllocated: Integer; function GetElement(row: TDynArrayNDX; column: TMatrixNDX): SmallInt; procedure SetElement(row: TDynArrayNDX; column: TMatrixNDX; const NewValue: SmallInt); Protected mtxElements: PMatrixElements; Public constructor Create; Destructor Destroy; override; procedure SetSize(NumRows: TDynArrayNDX; NumColumns: TMatrixNDX); Property rows: TDynArrayNDX read FRows; Property columns: TMatrixNDX read FColumns; Property Element[row: TDynArrayNDX; column: TMatrixNDX]: SmallInt read GetElement write SetElement; default; end; TIndexNDX = 0..vMaxCol; TIndexElements = array[TIndexNDX] of TSmallIntArray; PIndexElements = ^TIndexElements; TIndexArray = class Private FMemAllocated: Integer; FCount: Integer; FCapacity: TIndexNDX; FAutosize: Boolean; function GetElement(Element: TIndexNDx): TSmallIntArray; procedure SetElement(Element: TIndexNDX; const NewValue: TSmallIntArray); Protected idxElements: PIndexElements; Public constructor Create; Destructor Destroy; override; procedure SetSize(Elements: TIndexNDX); procedure Expand; function Add(const NewValue: TSmallIntArray): Integer; property MemoryUsage: Integer read FMemAllocated; property Autosize: Boolean read FAutosize write FAutosize; property Capacity: TIndexNDX read FCapacity write SetSize; property Count: Integer read FCount; Property Items[Element: TIndexNDX]: TSmallIntArray read GetElement write SetElement; default; end; TCustomArray = class private FDataType: Integer; { The variant data type } FArray: Pointer; { Pointer to the array class } FBlankStringVal: string; FBlankDateVal: Variant; FBlankBoolVal: Word; FBlankCount: Integer; procedure UnsupportedTypeError(vType: Integer); protected function GetItem(Index: Integer): Variant; procedure SetItem(Index: Integer; Value: Variant); function GetCompProc: TCompareProc; procedure SetCompProc(Proc: TCompareProc); function GetMemberCount: Integer; function GetSort: Boolean; procedure SetSort(Value: Boolean); function GetDups: TDuplicates; procedure SetDups(Value: TDuplicates); public constructor Create(Items: Integer; VarType: Integer); destructor Destroy; override; function ConvertVar(Value: Variant): Variant; function Add(Value: Variant): Integer; function IsBlank(Index: Integer): Boolean; function MemoryUsage: Integer; virtual; procedure SetSize(size: Integer); function IndexOf(Value: Variant): Integer; procedure Assign(Value: TCustomArray; bSorted, bUnique: Boolean); function GetDouble(Index: Integer): Double; function GetCurrency(Index: Integer): Currency; function GetInteger(Index: Integer): Integer; property List: Pointer read FArray; property Duplicates: TDuplicates read GetDups write SetDups; property Sorted: Boolean read GetSort write SetSort; property BlankStringVal: string read FBlankStringVal write FBlankStringVal; property BlankDateVal: Variant read FBlankDateVal write FBlankDateVal; property BlankBoolVal: Word read FBlankBoolVal write FBlankBoolVal; property CompareProc: TCompareProc read GetCompProc write SetCompProc; property MemberCount: Integer read GetMemberCount; property DataType: Integer read FDataType; property BlankCount: Integer read FBlankCount; property Items[I: Integer]: Variant read GetItem write SetItem; default; end; TThreadCustomArray = class private FCustomArray: TCustomArray; FLock: TRTLCriticalSection; public constructor Create(Items: Integer; VarType: Integer); destructor Destroy; override; function Add(Item: Variant): Integer; function LockArray: TCustomArray; function GetItem(Index: Integer): Variant; function MemoryUsage: Integer; procedure UnlockArray; end; procedure SetMemoryCapacity(Value: Integer); function GetMemoryCapacity: Integer; implementation { Helper functions } var AvailableMemory: Integer; { Memory available to allocate } TotalAllocatedMemory: Integer; { Total allaocted by the array classes } function GetAvailableMem: Integer; var MemStats: TMemoryStatus; begin GlobalMemoryStatus(MemStats); Result := MemStats.dwAvailPhys + (MemStats.dwAvailPageFile div 2); end; procedure SetMemoryCapacity(Value: Integer); begin AvailableMemory := Value; end; function GetMemoryCapacity: Integer; begin Result := AvailableMemory; end; function CheckLowCapacity(oldSize, newSize: Integer): Boolean; var CheckMemSize: Integer; begin Result := False; Dec(TotalAllocatedMemory, oldSize); CheckMemSize := AllocMemSize; Inc(CheckMemSize, newSize); if (CheckMemSize > AvailableMemory) then Result := True; end; procedure LowCapacityError; begin raise ELowCapacityError.CreateRes(@sLowCapacityError); end; function CmpWord(var item1, item2): Integer; var w1: word absolute item1; w2: word absolute item2; i1, i2: Integer; begin if (w1 = 2) then i1 := -1 else i1 := Integer(w1); if (w2 = 2) then i2 := -1 else i2 := Integer(w2); if (i1 < i2) then Result := -1 else if (i1 > i2) then Result := 1 else Result := 0; end; function CmpSmallInt(var item1, item2): Integer; var i1: SmallInt absolute item1; i2: SmallInt absolute item2; begin Result := i1-i2; end; function CmpInteger(var item1, item2): Integer; var i1: Integer absolute item1; i2: Integer absolute item2; begin if (i1 < i2) then Result := -1 else if (i1 > i2) then Result := 1 else Result := 0; end; function CmpSingle(var item1, item2): Integer; var i1: Single absolute item1; i2: Single absolute item2; r: Single; begin r := i1-i2; if (Abs(r) < 1.0E-30) then Result := 0 else if (r < 0) then Result := -1 else Result := 1; end; function CmpDouble(var item1, item2): Integer; var i1: Double absolute item1; i2: Double absolute item2; r: Double; begin r := i1-i2; if (Abs(r) < 1.0E-100) then Result := 0 else if (r < 0) then Result := -1 else Result := 1; end; function CmpCurrency(var item1, item2): Integer; var i1: Currency absolute item1; i2: Currency absolute item2; r: Currency; begin r := i1-i2; if (Abs(r) < 1.0E-3000) then Result := 0 else if (r < 0) then Result := -1 else Result := 1; end; function CmpString(var item1, item2): Integer; var p1: String absolute item1; p2: String absolute item2; begin Result := AnsiCompareStr(p1, p2); end; procedure ArrayDuplicateError; begin raise EArrayError.CreateRes(@sDupeItem); end; procedure ArrayIndexError(Index: Integer); begin raise EArrayError.CreateResFMT(@sArrayIndexOutOfRange, [Index]); end; { TBaseArray class } constructor TBaseArray.Create(itemcount, iSize: Integer); begin inherited Create; FMemory := nil; FCapacity := 0; FCount := 0; FItemSize := iSize; FFlags := [afOwnsData, afAutoSize]; SetCapacity(itemcount); end; destructor TBaseArray.Destroy; begin if (FMemory <> nil) then begin Clear; FItemSize := 0; end; inherited Destroy; end; procedure TBaseArray.SetCount(NewCount: Integer); begin if (NewCount < 0) or (NewCount > MaxListSize) then ArrayIndexError(NewCount); if (NewCount > FCapacity) then SetCapacity(NewCount); if (NewCount > FCount) then FillMemory(GetItemPtr(FCount), (NewCount - FCount) * FItemSize, 0); FCount := NewCount; end; procedure TBaseArray.Clear; begin if (FCount <> 0) then begin InvalidateItems(0, FCount); FCount := 0; SetCapacity(0); { Has same affect as freeing memory } end; end; procedure TBaseArray.SetCapacity(NewCapacity: Integer); begin if (NewCapacity < FCount) or (NewCapacity > MaxListSize) then ArrayIndexError(NewCapacity); if (NewCapacity <> FCapacity) then begin { Check for available memory } if CheckLowCapacity((FCapacity * FItemSize), NewCapacity * FItemSize) then LowCapacityError; ReallocMem(FMemory, NewCapacity * FItemSize); FillChar(Pointer(Integer(FMemory) + FCapacity * FItemSize)^, (NewCapacity - FCapacity) * FItemSize, 0); FCapacity := NewCapacity; end; end; procedure TBaseArray.Grow; var Delta: Integer; begin if (FCapacity > 64) then Delta := FCapacity div 4 else if (FCapacity > 8) then Delta := 16 else Delta := 4; SetCapacity(FCapacity + Delta); end; function TBaseArray.GetLimit: Integer; begin if (FCount = 0) then Result := FCapacity else Result := FCount; end; procedure TBaseArray.Insert(Index: Integer; var Value); begin InsertAt(Index, Value); end; procedure TBaseArray.InsertAt(Index: Integer; var Value); begin if (Index < 0) or (Index > FCount) then ArrayIndexError(Index); { Increase the array size if needed } if AutoSize then SetCapacity(FCount+1); if (Index < FCount) then begin try MoveMemory(GetItemPtr(Index+1), GetItemPtr(Index), (FCount - Index) * FItemSize); except InternalHandleException; end; end; CopyFrom(Index, 1, Value); Inc(FCount); end; function TBaseArray.ValidIndex(Index: Integer): Boolean; begin Result := True; if (Index < 0) or (Index > FCount) then begin ArrayIndexError(Index); Result := False; end end; procedure TBaseArray.RemoveItem(Index: Integer); begin Delete(Index); end; procedure TBaseArray.Delete(Index: Integer); begin { We are removing only one item. } if ValidIndex(index) then begin InvalidateItems(Index, 1); Dec(FCount); if (Index < FCount) then begin try MoveMemory(GetItemPtr(Index), GetItemPtr(Index + 1), (FCount - Index) * FItemSize); except end; end; end; end; procedure TBaseArray.RemoveRange(atIndex, numItems: Integer); begin if (numItems = 0) then Exit; if ValidateBounds(atIndex, numItems) then begin { Invalidate the items about to be deleted so a derived class can do cleanup on them. } InvalidateItems(atIndex, numItems); { Move the items above those we delete down, if there are any } if ((atIndex+numItems) <= FCount) then begin MoveMemory(GetItemPtr(atIndex), GetItemPtr(atIndex+numItems), (FCount-atIndex-numItems+1)* FItemSize); end; if AutoSize then SetCapacity(FCount - numItems); end; end; procedure TBaseArray.Exchange(Index1, Index2: Integer); begin end; procedure TBaseArray.Sort(Compare: TCompareProc); begin end; procedure TBaseArray.CopyFrom(toIndex, numItems: Integer; var Source); begin if (numItems = 0) then Exit; if ValidateBounds(toIndex, numItems) then begin try InvalidateItems(toIndex, numItems); MoveMemory(GetItemPtr(toIndex), @Source, numItems*FItemSize); except InternalHandleException; end; end; end; procedure TBaseArray.PutItem(index: Integer; var Value); begin if AutoSize and (FCount = FCapacity) then Grow; if ValidIndex(index) then begin try CopyMemory(GetItemPtr(index), @Value, FItemSize); except InternalHandleException; end; if index > FCount-1 then Inc(FCount); end; end; procedure TBaseArray.GetItem(index: Integer; var Value); begin if ValidIndex(index) then begin try CopyMemory(@Value, GetItemPtr(index), FItemSize); except InternalHandleException; end; end; end; function TBaseArray.GetItemPtr(index: Integer): Pointer; begin Result := nil; if ValidIndex(index) then Result := Ptr(LongInt(FMemory) + (index*FItemSize)); end; function TBaseArray.ValidateBounds(atIndex: Integer; var numItems: Integer): Boolean; begin Result := True; if (atIndex < 0) or (atIndex > FCount) then Result := False; if Result then if (numItems > Succ(FCount)) or ((FCount-numItems+1) < atIndex) then numItems := FCount - atIndex + 1; end; procedure TBaseArray.InvalidateItems(atIndex, numItems: Integer); begin end; function TBaseArray.HasFlag(aFlag: TArrayFlags): Boolean; begin Result := aFlag in FFlags; end; procedure TBaseArray.SetFlag(aFlag: TArrayFlags); begin Include(FFLags, aFlag); end; procedure TBaseArray.ClearFlag(aFlag: TArrayFlags); begin Exclude(FFLags, aFlag); end; procedure TBaseArray.SetAutoSize(aSize: Boolean); begin if (aSize = True) then SetFlag(afAutoSize) else ClearFlag(afAutoSize); end; function TBaseArray.GetAutoSize : Boolean; begin Result := HasFlag(afAutoSize); end; function TBaseArray.IndexOf(var Item): Integer; var item2: Pointer; begin if (SortOrder = tsNone) then begin for Result := 0 to Count - 1 do begin GetItem(Result, item2); if (FCompProc(item2, Item) = 0) then Exit; end; Result := -1; end else if not FindItem(Result, Item) then Result := -1; end; function TBaseArray.FindItem(var Index: Integer; var Value): Boolean; var L, H, I, C: Integer; Value2: Pointer; begin Result := False; L := 0; H := Count - 1; while (L <= H) do begin I := (L + H) shr 1; GetItem(I, Value2); C := FCompProc(Value2, Value); 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; procedure TBaseArray.BlockCopy(Source: TBaseArray; fromIndex, toIndex, numitems: Integer); begin if (numitems = 0) then Exit; if (Source is ClassType) and (ItemSize = Source.ItemSize) then begin if Source.ValidateBounds(fromIndex, numItems) then begin try CopyFrom(toIndex, numItems, Source.GetItemPtr(fromIndex)^); except InternalHandleException; end; end; end; end; procedure TBaseArray.InternalHandleException; begin Clear; raise EArrayError.CreateRes(@sGeneralArrayError); end; { TIntArray } type TIArray = Array[0..High(Integer) div Sizeof(Integer)-1] of Integer; PIntArray = ^TIArray; constructor TIntArray.Create(itemcount, dummy: Integer); begin inherited Create(itemcount, Sizeof(integer)); FCompProc := CmpInteger; end; procedure TIntArray.PutItem(index: Integer ; value: Integer); begin if AutoSize and (FCount = FCapacity) then inherited Grow; try PIntArray(FMemory)^[index] := value; except InternalHandleException; end; if (index > FCount-1) then Inc(FCount); end; function TIntArray.GetItem(index: Integer): Integer; begin Result := 0; if ValidIndex(index) then begin try Result := PIntArray(FMemory)^[index]; except InternalHandleException; end; end; end; function TIntArray.Add(Value: Integer): Integer; begin if (SortOrder = tsNone) then Result := FCount else if FindItem(Result, Value) then case Duplicates of dupIgnore : Exit; dupError : ArrayDuplicateError; end; InsertAt(Result, Value); end; procedure TIntArray.Assign(Source: TPersistent); var I: Integer; begin if (Source is TIntArray) then begin try Clear; for I := 0 to TBaseArray(Source).Count - 1 do Add(TIntArray(Source)[I]); finally end; Exit; end; inherited Assign(Source); end; function TIntArray.Find(var Index: Integer; Value: Integer): Boolean; var L, H, I, C: Integer; Value2: Integer; begin Result := False; L := 0; H := Count - 1; while (L <= H) do begin I := (L + H) shr 1; Value2 := GetItem(I); C := FCompProc(Value2, Value); 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; { TSingleArray } Type TRArray = Array[0..High(Integer) div Sizeof(Single)-1] of Single; PSingleArray = ^TRArray; constructor TSingleArray.Create(itemcount, dummy: Integer); begin inherited Create(itemcount, Sizeof(Single)); FCompProc := CmpSingle; end; procedure TSingleArray.PutItem(index: Integer ; value: Single); begin if AutoSize and (FCount = FCapacity) then inherited Grow; try PSingleArray(FMemory)^[index] := value; except InternalHandleException; end; if (index > FCount-1) then Inc(FCount); end; function TSingleArray.GetItem(index: Integer): Single; begin Result := 0; if ValidIndex(index) then begin try Result := PSingleArray(FMemory)^[index]; except InternalHandleException; end; end; end; function TSingleArray.Add(Value: Single): Integer; begin if (SortOrder = tsNone) then Result := FCount else if FindItem(Result, Value) then case Duplicates of dupIgnore : Exit; dupError : ArrayDuplicateError; end; InsertAt(Result, Value); end; procedure TSingleArray.Assign(Source: TPersistent); var I: Integer; begin if (Source is TSingleArray) then begin try Clear; for I := 0 to TBaseArray(Source).Count - 1 do Add(TSingleArray(Source)[I]); finally end; Exit; end; inherited Assign(Source); end; function TSingleArray.Find(var Index: Integer; Value: Single): Boolean; var L, H, I, C: Integer; Value2: Single; begin Result := False; L := 0; H := Count - 1; while (L <= H) do begin I := (L + H) shr 1; Value2 := GetItem(I); C := FCompProc(Value2, Value); 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 TSingleArray.IndexOf(var Item): Integer; var item1: Single absolute Item; item2: Single; begin if (SortOrder = tsNone) then begin for Result := 0 to Count - 1 do begin item2 := GetItem(Result); if (FCompProc(item2, item1) = 0) then Exit; end; Result := -1; end else if not Find(Result, item1) then Result := -1; end; { TDoubleArray } type TDArray = array[0..High(Integer) div Sizeof(Double)-1] of Double; PDoubleArray = ^TDArray; constructor TDoubleArray.Create(itemcount, dummy: Integer); begin inherited Create(itemcount, Sizeof(Double)); FCompProc := CmpDouble; end; procedure TDoubleArray.PutItem(index: Integer ; value: Double); begin if AutoSize and (FCount = FCapacity) then inherited Grow; try PDoubleArray(FMemory)^[index] := value; except InternalHandleException; end; if (index > FCount-1) then Inc(FCount); end; function TDoubleArray.GetItem(index: Integer): Double; begin Result := 0; if ValidIndex(index) then begin try Result := PDoubleArray(FMemory)^[index]; except InternalHandleException; end; end; end; function TDoubleArray.Add(Value: Double): Integer; begin if (SortOrder = tsNone) then Result := FCount else if Find(Result, Value) then case Duplicates of dupIgnore : Exit; dupError : ArrayDuplicateError; end; InsertAt(Result, Value); end; procedure TDoubleArray.Assign(Source: TPersistent); var I: Integer; begin if (Source is TDoubleArray) then begin try Clear; for I := 0 to TBaseArray(Source).Count - 1 do Add(TDoubleArray(Source)[I]); finally end; Exit; end; inherited Assign(Source); end; function TDoubleArray.Find(var Index: Integer; Value: Double): Boolean; var L, H, I, C: Integer; Value2: Double; begin Result := False; L := 0; H := Count - 1; while (L <= H) do begin I := (L + H) shr 1; Value2 := GetItem(I); C := FCompProc(Value2, Value); 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 TDoubleArray.IndexOf(var Item): Integer; var item1: Double absolute Item; item2: Double; begin if (SortOrder = tsNone) then begin for Result := 0 to Count - 1 do begin item2 := GetItem(Result); if (FCompProc(item2, item1) = 0) then Exit; end; Result := -1; end else if not Find(Result, item1) then Result := -1; end; { TCurrencyArray } type TCArray = array[0..High(Integer) div Sizeof(Currency)-1] of Currency; PCurrencyArray = ^TCArray; constructor TCurrencyArray.Create(itemcount, dummy: Integer); begin inherited Create(itemcount, Sizeof(Currency)); FCompProc := CmpCurrency; end; procedure TCurrencyArray.PutItem(index: Integer ; value: Currency); begin if AutoSize and (FCount = FCapacity) then inherited Grow; try PCurrencyArray(FMemory)^[index] := value; except InternalHandleException; end; if (index > FCount-1) then Inc(FCount); end; function TCurrencyArray.GetItem(index: Integer): Currency; begin Result := 0; if ValidIndex(index) then begin try Result := PCurrencyArray(FMemory)^[index]; except InternalHandleException; end; end; end; function TCurrencyArray.Add(Value: Currency): Integer; begin if (SortOrder = tsNone) then Result := FCount else if Find(Result, Value) then case Duplicates of dupIgnore : Exit; dupError : ArrayDuplicateError; end; InsertAt(Result, Value); end; procedure TCurrencyArray.Assign(Source: TPersistent); var I: Integer; begin if (Source is TCurrencyArray) then begin try Clear; for I := 0 to TBaseArray(Source).Count - 1 do Add(TCurrencyArray(Source)[I]); finally end; Exit; end; inherited Assign(Source); end; function TCurrencyArray.Find(var Index: Integer; Value: Currency): Boolean; var L, H, I, C: Integer; Value2: Currency; begin Result := False; L := 0; H := Count - 1; while (L <= H) do begin I := (L + H) shr 1; Value2 := GetItem(I); C := FCompProc(Value2, Value); 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 TCurrencyArray.IndexOf(var Item): Integer; var item1: Currency absolute Item; item2: Currency; begin if (SortOrder = tsNone) then begin for Result := 0 to Count - 1 do begin item2 := GetItem(Result); if (FCompProc(item2, item1) = 0) then Exit; end; Result := -1; end else if not Find(Result, item1) then Result := -1; end; { TSmallIntArray } type TSIArray = array[0..High(Integer) div Sizeof(SmallInt)-1] of SmallInt; PSmallIntArray = ^TSIArray; constructor TSmallIntArray.Create(itemcount, dummy: Integer); begin inherited Create(itemcount, Sizeof(SmallInt)); FCompProc := CmpSmallInt; end; procedure TSmallIntArray.PutItem(index: Integer ; value: SmallInt); begin if AutoSize and (FCount = FCapacity) then inherited Grow; try PSmallIntArray(FMemory)^[index] := value; except InternalHandleException; end; if index > FCount-1 then Inc(FCount); end; function TSmallIntArray.GetItem(index: Integer): SmallInt; begin Result := 0; if ValidIndex(index) then begin try Result := PSmallIntArray(FMemory)^[index]; except InternalHandleException; end; end; end; function TSmallIntArray.Add(Value: SmallInt): Integer; begin if (SortOrder = tsNone) then Result := FCount else if FindItem(Result, Value ) then case Duplicates of dupIgnore : Exit; dupError : ArrayDuplicateError; end; InsertAt(Result, Value); end; procedure TSmallIntArray.Assign(Source: TPersistent); var I: Integer; begin if (Source is TSmallIntArray) then begin try Clear; for I := 0 to TBaseArray(Source).Count - 1 do Add(TSmallIntArray(Source)[I]); finally end; Exit; end; inherited Assign(Source); end; { TWordArray } type TArrayWord = array[0..High(Integer) div Sizeof(Word)-1] of Word; PWordArray = ^TArrayWord; constructor TWordArray.Create(itemcount, dummy: Integer); begin inherited Create(itemcount, Sizeof(Word)); FCompProc := CmpWord; end; procedure TWordArray.PutItem(index: Integer ; value: Word); begin if AutoSize and (FCount = FCapacity) then inherited Grow; try PWordArray(FMemory)^[index] := value; except InternalHandleException; end; if (index > FCount-1) then Inc(FCount); end; function TWordArray.GetItem(index: Integer): Word; begin Result := 0; if ValidIndex(index) then begin try Result := PWordArray(FMemory)^[index]; except InternalHandleException; end; end; end; function TWordArray.Add(Value: Word): Integer; begin if (SortOrder = tsNone) then Result := FCount else if Find(Result, Value) then case Duplicates of dupIgnore : Exit; dupError : ArrayDuplicateError; end; InsertAt(Result, Value); end; procedure TWordArray.Assign(Source: TPersistent); var I: Integer; begin if (Source is TWordArray) then begin try Clear; for I := 0 to TBaseArray(Source).Count - 1 do Add(TWordArray(Source)[I]); finally end; Exit; end; inherited Assign(Source); end; function TWordArray.Find(var Index: Integer; Value: Word): Boolean; var L, H, I, C: Integer; Value2: Word; begin Result := False; L := 0; H := Count - 1; while (L <= H) do begin I := (L + H) shr 1; Value2 := GetItem(I); C := FCompProc(Value2, Value); 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 TWordArray.IndexOf(var Item): Integer; var item1: Word absolute Item; item2: Word; begin if (SortOrder = tsNone) then begin for Result := 0 to Count - 1 do begin item2 := GetItem(Result); if (FCompProc(item2, item1) = 0) then Exit; end; Result := -1; end else if not Find(Result, item1) then Result := -1; end; { TPointerArray } Type TPArray = Array [0..High(Integer) div Sizeof(Pointer)-1] Of Pointer; PPArray = ^TPArray; constructor TPointerArray.Create(itemcount, dummy: Integer); begin inherited Create(itemcount, Sizeof(Pointer)); FFlags := [afAutoSize]; end; procedure TPointerArray.CopyFrom(var Source; toIndex, numItems: Integer); var i: Integer; p: PPArray; arr: TPArray absolute Source; begin if (numItems = 0) then Exit; if ValidateBounds(toIndex, numItems) then begin InvalidateItems(toIndex, numItems); p := PPArray(FMemory); for i:= 0 to Pred(numItems) Do p^[toIndex+i] := CloneItem(arr[i]); FSortOrder := tsNone; end; end; procedure TPointerArray.CopyTo(var Dest; fromIndex, numItems: Integer); var i: Integer; p: PPArray; arr: TPArray absolute Dest; begin if (numItems = 0) then Exit; if ValidateBounds(fromIndex, numItems) then begin p := PPArray(FMemory); for i:= 0 to Pred(numItems) Do arr[i] := CloneItem(p^[fromIndex+i]); end; end; procedure TPointerArray.PutData(index: Integer ; value: Pointer); begin if ValidIndex(index) then begin if (PPArray(FMemory)^[index] <> nil) and HasFlag(afOwnsData)then FreeItem(PPArray(FMemory)^[index]); PPArray(FMemory)^[index] := CloneItem(value); FSortOrder := tsNone; end; end; function TPointerArray.GetData(index: Integer): Pointer; begin if ValidIndex(index) then Result := PPArray(FMemory)^[index] else Result := nil; end; procedure TPointerArray.FreeItem(item: Pointer); begin { this is a nop for this class since we do not know what item points to } end; procedure TPointerArray.InvalidateItems(atIndex, numItems: Integer); var n: Integer; p: Pointer; begin if (numItems > 0) and HasFlag(afOwnsData) then begin if ValidateBounds(atIndex, numItems) then begin for n := atIndex to Pred(numItems+atIndex) Do begin p := AsPtr[n]; if (p <> nil) then begin FreeItem(p); p := nil; PutItem(n, p); end; end; end; end; end; function TPointerArray.CloneItem(item: Pointer): Pointer; begin Result := item; end; { TStringArray } type PStringItem = ^TStringItem; TStringItemList = array[0..MaxListSize] of TStringItem; PStringItemList = ^TStringItemList; constructor TStringArray.Create(itemcount, dummy: Integer); begin inherited Create(itemcount, Sizeof(TStringItem)); FFlags := [afAutoSize]; FCompProc := CmpString; { Note: if the language driver is available then we use it for compares. } end; function TStringArray.Add(const S: String): Integer; begin if (SortOrder = tsNone) then Result := FCount else if Find(S, Result) then case Duplicates of dupIgnore : Exit; dupError : raise EArrayError.CreateRes(@SDuplicateString); end; InsertItem(Result, S); end; procedure TStringArray.Exchange(Index1, Index2: Integer); begin if (Index1 < 0) or (Index1 >= FCount) or (Index2 < 0) or (Index2 >= FCount) then ArrayIndexError(Index1); ExchangeItems(Index1, Index2); end; procedure TStringArray.ExchangeItems(Index1, Index2: Integer); var Temp: Integer; Item1, Item2: PStringItem; begin Item1 := @PStringItemList(FMemory)^[Index1]; Item2 := @PStringItemList(FMemory)^[Index2]; Temp := Integer(Item1^.FString); Integer(Item1^.FString) := Integer(Item2^.FString); Integer(Item2^.FString) := Temp; Temp := Integer(Item1^.FObject); Integer(Item1^.FObject) := Integer(Item2^.FObject); Integer(Item2^.FObject) := Temp; end; function TStringArray.Find(S: string; var Index: Integer): Boolean; var L, H, I: Integer; C: SmallInt; { for compatability with the BDE LD } begin Result := False; L := 0; H := Count - 1; while (L <= H) do begin I := (L + H) shr 1; C := SmallInt(FCompProc(PStringItemList(FMemory)^[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 TStringArray.GetObject(Index: Integer): TObject; begin Result := nil; if ValidIndex(Index) then Result := PStringItemList(FMemory)^[Index].FObject; end; procedure TStringArray.Grow; var Delta: Integer; begin if (FCapacity > 64) then Delta := FCapacity div 4 else if (FCapacity > 8) then Delta := 16 else Delta := 4; inherited SetCapacity(FCapacity + Delta); end; function TStringArray.IndexOf(var Item): Integer; var S1: string; S2: string absolute Item; begin if (SortOrder = tsNone) then begin for Result := 0 to Count - 1 do begin S1 := GetString(Result); if (SmallInt(FCompProc(S1, S2)) = 0) then Exit; end; Result := -1; end else if not Find(S2, Result) then Result := -1; end; procedure TStringArray.Insert(Index: Integer; var Value); var S: string; begin S := Variant(Value); if (SortOrder <> tsNone) then raise EArrayError.CreateRes(@SSortedListError); if (Index < 0) or (Index > FCount) then ArrayIndexError(Index); InsertItem(Index, S); end; procedure TStringArray.InsertItem(Index: Integer; const S: string); begin if (FCount = FCapacity) then Grow; if (Index < FCount) then begin try System.Move(PStringItemList(FMemory)^[Index], PStringItemList(FMemory)^[Index + 1], (FCount - Index) * SizeOf(TStringItem)); except InternalHandleException; end; end; try PStringItemList(FMemory)^[Index].FObject := nil; Pointer(PStringItemList(FMemory)^[Index].FString) := nil; PStringItemList(FMemory)^[Index].FString := S; except InternalHandleException; end; Inc(FCount); end; procedure TStringArray.PutString(Index: Integer; const S: string); begin { Sorted items must be added } if (SortOrder <> tsNone) then raise EArrayError.CreateRes(@SSortedListError); if ValidIndex(Index) then begin try PStringItemList(FMemory)^[Index].FString := S; except InternalHandleException; end; end; end; function TStringArray.GetString(Index: Integer): string; begin {$IFOPT R+} if ValidIndex(Index) then {$ENDIF} begin try Result := PStringItemList(FMemory)^[Index].FString; except Clear; raise; end; end; end; procedure TStringArray.PutObject(Index: Integer; AObject: TObject); begin if ValidIndex(Index) then PStringItemList(FMemory)^[Index].FObject := AObject; end; procedure TStringArray.QuickSort(L, R: Integer); var I, J: Integer; P: string; begin repeat I := L; J := R; P := PStringItemList(FMemory)^[(L + R) shr 1].FString; repeat while (SmallInt(FCompProc(PStringItemList(FMemory)^[I].FString, P)) < 0) do Inc(I); while (SmallInt(FCompProc(PStringItemList(FMemory)^[J].FString, P)) > 0) do Dec(J); if (I <= J) then begin ExchangeItems(I, J); Inc(I); Dec(J); end; until (I > J); if (L < J) then QuickSort(L, J); L := I; until (I >= R); end; procedure TStringArray.Sort(Compare: TCompareProc); begin if (SortOrder <> tsNone) and (Count > 1) then QuickSort(0, Count - 1); end; procedure TStringArray.AddStrings(Strings: TStringArray); var I: Integer; begin try for I := 0 to Strings.Count - 1 do Add(Strings.Strings[I]); finally end; end; procedure TStringArray.Assign(Source: TPersistent); begin if (Source is TStringArray) then begin try Clear; AddStrings(TStringArray(Source)); finally end; Exit; end; inherited Assign(Source); end; procedure TStringArray.InvalidateItems(atIndex, numItems: Integer); begin Finalize(PStringItemList(FMemory)^[atIndex], numItems); end; { TCustomArray } function VariantTypeToName(vType: Integer): string; begin case vType of varEmpty : Result := 'Empty'; { Do not localize } varNull : Result := 'Null'; { Do not localize } varOleStr : Result := 'OleStr'; { Do not localize } varDispatch : Result := 'Dispatch'; { Do not localize } varError : Result := 'Error'; { Do not localize } varVariant : Result := 'Variant'; { Do not localize } varByte : Result := 'Byte'; { Do not localize } varTypeMask : Result := 'TypeMask'; { Do not localize } varArray : Result := 'Array'; { Do not localize } varByRef : Result := 'ByRef'; { Do not localize } else Result := 'Unknown'; { Do not localize } end; end; constructor TCustomArray.Create(Items: Integer; VarType: Integer); begin FDataType := VarType; FBlankDateVal := -650000; { Satisfies Variants, Lowest TDateTime is actually -693593 } FBlankStringVal := '0'; FBlankBoolVal := 2; FBlankCount := 0; case VarType of varSmallint: FArray := TSmallIntArray.Create(Items, 0); varInteger: FArray := TIntArray.Create(Items, 0); varDate, varDouble: FArray := TDoubleArray.Create(Items, 0); varBoolean: FArray := TWordArray.Create(Items, 0); varString: FArray := TStringArray.Create(Items, 0); varSingle: FArray := TSingleArray.Create(Items, 0); varCurrency: FArray := TCurrencyArray.Create(Items, 0); else UnsupportedTypeError(FDataType); end; end; destructor TCustomArray.Destroy; begin if Assigned(FArray) then begin TBaseArray(FArray).Destroy; FArray := nil; end; FDataType := 0; inherited Destroy; end; procedure TCustomArray.UnsupportedTypeError(vType: Integer); var sDataType: string; begin sDataType := VariantTypeToName(vType); raise EUnsupportedTypeError.CreateResFmt(@sUnsupportedDataType, [sDataType]); end; function TCustomArray.GetMemberCount: Integer; begin Result := TBaseArray(FArray).Count; end; function TCustomArray.GetItem(Index: Integer): Variant; var V: Variant; begin case FDataType of varSmallint: Result := TSmallIntArray(FArray)[Index]; varDate: begin V := TDoubleArray(FArray)[Index]; Result := VarAsType(V, varDate) end; varInteger: Result := TIntArray(FArray)[Index]; varDouble: Result := TDoubleArray(FArray)[Index]; varBoolean: Result := TWordArray(FArray)[Index]; varString: Result := TStringArray(FArray).GetString(Index); varSingle: Result := TSingleArray(FArray)[Index]; varCurrency: Result := TCurrencyArray(FArray)[Index]; else UnsupportedTypeError(FDataType); end; end; function TCustomArray.GetDouble(Index: Integer): Double; begin Result := TDoubleArray(FArray).GetItem(index); end; function TCustomArray.GetCurrency(Index: Integer): Currency; begin Result := TCurrencyArray(FArray).GetItem(index); end; function TCustomArray.GetInteger(Index: Integer): Integer; begin Result := TIntArray(FArray).GetItem(index); end; function TCustomArray.IsBlank(Index: Integer): Boolean; begin case FDataType of varDate: Result := (TDoubleArray(FArray)[Index] = BlankDateVal); varString: Result := (TStringArray(FArray).GetString(Index) = BlankStringVal); varBoolean: Result := (TWordArray(FArray)[Index] = BlankBoolVal); else Result := False; end; end; procedure TCustomArray.SetItem(Index: Integer; Value: Variant); var VarData: TVarData; begin VarData := TVarData(Value); { Handle blank values and misc conversion problems } if (FDataType <> VarData.vType) then begin case VarData.vType of varEmpty, varNull : begin Inc(FBlankCount); case FDataType of varDate : Value := VarAsType(BlankDateVal, varDouble); varString : Value := VarAsType(BlankStringVal, varString); varBoolean : Value := VarAsType(BlankBoolVal, varSmallInt); else Value := VarAsType(0, FDataType); end; end; varDouble : if (FDataType = varCurrency) then Value := VarAsType(Value, varCurrency); end; end; case FDataType of varSmallint: TSmallIntArray(FArray)[Index] := Value.VSmallInt; varDate: TDoubleArray(FArray)[Index] := TVarData(Value).VDouble; varInteger: TIntArray(FArray)[Index] := Value; varDouble: TDoubleArray(FArray)[Index] := Value; varBoolean: TWordArray(FArray)[Index] := Value; varString: TStringArray(FArray).Insert(Index, Value); varSingle: TSingleArray(FArray)[Index] := Value; varCurrency: TCurrencyArray(FArray)[Index] := Value; else UnsupportedTypeError(FDataType); end; end; function TCustomArray.IndexOf(Value: Variant): Integer; var vConv: Variant; strVal: String; iVal: Integer; siVal: SmallInt; dVal: Double; sgVal: Single; cVal: Currency; bVal: Word; begin { This should raise an array index exception if below fails } Result := -1; case FDataType of varSmallint: begin VarCast(vConv, Value, varSmallInt); siVal := SmallInt(TVarData(vConv).VSmallint); Result := TBaseArray(FArray).IndexOf(siVal); end; varDate, varDouble: begin VarCast(vConv, Value, varDouble); dVal := Double(TVarData(vConv).VDouble); Result := TBaseArray(FArray).IndexOf(dVal); end; varInteger: begin VarCast(vConv, Value, varInteger); iVal := Integer(TVarData(vConv).VInteger); Result := TBaseArray(FArray).IndexOf(iVal); end; varString: begin VarCast(vConv, Value, varString); strVal := String(TVarData(vConv).VString); Result := TStringArray(FArray).IndexOf(strVal) end; varSingle: begin VarCast(vConv, Value, varSingle); sgVal := Single(TVarData(vConv).VSingle); Result := TBaseArray(FArray).IndexOf(sgVal); end; varCurrency: begin VarCast(vConv, Value, varCurrency); cVal := Currency(TVarData(vConv).VCurrency); Result := TCurrencyArray(FArray).IndexOf(cVal); end; varBoolean: begin if (Value = BlankBoolVal) then begin bVal := BlankBoolVal; end else begin VarCast(vConv, Value, varBoolean); bVal := Word(TVarData(vConv).VBoolean); end; Result := TWordArray(FArray).IndexOf(bVal); end; else UnsupportedTypeError(FDataType); end; end; procedure TCustomArray.Assign(Value: TCustomArray; bSorted, bUnique: Boolean); begin if bSorted then TBaseArray(FArray).SortOrder := tsDescending; if bUnique then TBaseArray(FArray).Duplicates := dupIgnore; case FDataType of varString : TStringArray(FArray).Assign(TStringArray(Value.FArray)); varSmallint : TSmallIntArray(FArray).Assign(TSmallIntArray(Value.FArray)); varInteger : TIntArray(FArray).Assign(TIntArray(Value.FArray)); varDate, varDouble : TDoubleArray(FArray).Assign(TDoubleArray(Value.FArray)); varBoolean : TWordArray(FArray).Assign(TWordArray(Value.FArray)); varSingle : TSingleArray(FArray).Assign(TSingleArray(Value.FArray)); varCurrency : TCurrencyArray(FArray).Assign(TCurrencyArray(Value.FArray)); else UnsupportedTypeError(FDataType); end; end; function TCustomArray.Add(Value: Variant): Integer; var VarData: TVarData; begin VarData := TVarData(Value); Result := -1; { Error } { Handle blank values and misc conversion problems } if (FDataType <> VarData.vType) then begin case VarData.vType of varEmpty, varNull : begin Inc(FBlankCount); case FDataType of varDate : Value := VarAsType(BlankDateVal, varDouble); varString : Value := VarAsType(BlankStringVal, varString); varBoolean : Value := VarAsType(BlankBoolVal, varSmallInt); else Value := VarAsType(0, FDataType); end; end; varDouble : if (FDataType = varCurrency) then Value := VarAsType(Value, varCurrency); end; end; case FDataType of varString : Result := TStringArray(FArray).Add(VarToStr(Value)); varSmallint : Result := TSmallIntArray(FArray).Add(TVarData(Value).VSmallint); varInteger : Result := TIntArray(FArray).Add(TVarData(Value).VInteger); varDate, varDouble : Result := TDoubleArray(FArray).Add(TVarData(Value).VDouble); varBoolean : Result := TWordArray(FArray).Add(Word(TVarData(Value).VBoolean)); varSingle : Result := TSingleArray(FArray).Add(TVarData(Value).VSingle); varCurrency : Result := TCurrencyArray(FArray).Add(TVarData(Value).VCurrency); else UnsupportedTypeError(FDataType); end; end; function TCustomArray.GetCompProc: TCompareProc; begin Result := TBaseArray(FArray).CompareProc; end; procedure TCustomArray.SetCompProc(Proc: TCompareProc); begin TBaseArray(FArray).CompareProc := Proc; end; procedure TCustomArray.SetSize(size: Integer); begin TBaseArray(FArray).SetCapacity(size); end; function TCustomArray.MemoryUsage: Integer; begin Result := (TBaseArray(FArray).Capacity * TBaseArray(FArray).ItemSize); end; function TCustomArray.GetSort: Boolean; begin Result := (TBaseArray(FArray).SortOrder) <> tsNone; end; procedure TCustomArray.SetSort(Value: Boolean); begin TBaseArray(FArray).SortOrder := tsDescending; end; function TCustomArray.GetDups: TDuplicates; begin Result := TBaseArray(FArray).Duplicates; end; procedure TCustomArray.SetDups(Value: TDuplicates); begin TBaseArray(FArray).Duplicates := Value; end; function TCustomArray.ConvertVar(Value: Variant): Variant; begin case TVarData(Value).vType of varNull: begin case DataType of varDate : Result := VarAsType(BlankDateVal, DataType); varString : Result := VarAsType(BlankStringVal, DataType); varBoolean : Result := VarAsType(BlankBoolVal, DataType); else Result := VarAsType(0, DataType); end; end; varSmallint, varInteger, varDate, varDouble, varBoolean, varString, varSingle, varCurrency: Result := VarAsType(Value, DataType); else UnsupportedTypeError(TVarData(Value).vType); end; end; { TTwoDimArray } constructor TTwoDimArray.Create; begin inherited Create; FRows := 0; FColumns := 0; mtxElements := nil; end; destructor TTwoDimArray.Destroy; var col: TMatrixNDX; begin if Assigned(mtxElements) then begin try for col := FColumns-1 downto 0 do begin dec(FMemAllocated, mtxElements^[col].FCapacity); mtxElements^[col].Free; mtxElements^[col] := nil; end; finally FreeMem(mtxElements, FMemAllocated); end; end; inherited Destroy; end; procedure TTwoDimArray.SetSize(NumRows : TDynArrayNDX; NumColumns : TMatrixNDX); var col: TMatrixNDX; begin FRows := NumRows; FColumns := NumColumns; FMemAllocated := FColumns*sizeof(TSmallIntArray); if ((FMemAllocated + TotalAllocatedMemory) > AvailableMemory) then LowCapacityError; GetMem(mtxElements, FColumns*sizeof(TSmallIntArray)); Inc(TotalAllocatedMemory, FMemAllocated); { acquire memory for each column of the matrix } for col := 0 to FColumns-1 do begin mtxElements^[col] := TSmallIntArray.Create(FRows, 0); inc(FMemAllocated, mtxElements^[col].FCapacity); end; end; function TTwoDimArray.GetElement(row : TDynArrayNDX; column : TMatrixNDX) : SmallInt; begin if (row > FRows) then raise EDynArrayRangeError.CreateResFMT(@sRowOutOfRange, [row]); if (column > FColumns) then raise EDynArrayRangeError.CreateResFMT(@sColOutOfRange, [column]); Result := mtxElements^[column].Items[row]; end; procedure TTwoDimArray.SetElement(row : TDynArrayNDX; column : TMatrixNDX; const NewValue : SmallInt); begin if (row > FRows) then raise EDynArrayRangeError.CreateResFMT(@sRowOutOfRange, [row]); if (column > FColumns) then raise EDynArrayRangeError.CreateResFMT(@sColOutOfRange, [column]); mtxElements^[column].Items[row] := NewValue; end; { TIndexArray } constructor TIndexArray.Create; begin inherited Create; FCapacity := 0; FCount := 0; FMemAllocated := 0; FAutosize := False; idxElements := nil; end; destructor TIndexArray.Destroy; var Idx: TIndexNDX; begin if Assigned(idxElements) then begin try if (FCount > 0) then begin for Idx := FCount-1 downto 0 do begin dec(FMemAllocated, idxElements^[Idx].FCapacity); idxElements^[Idx].Free; idxElements^[Idx] := nil; end; end; finally FreeMem(idxElements, FMemAllocated); end; end; inherited Destroy; end; procedure TIndexArray.SetSize(Elements: TIndexNDX); begin FCapacity := Elements; FMemAllocated := FCapacity*sizeof(TSmallIntArray); if ((FMemAllocated + TotalAllocatedMemory) > AvailableMemory) then LowCapacityError; GetMem(idxElements, FCapacity*sizeof(TSmallIntArray)); Inc(TotalAllocatedMemory, FMemAllocated); end; procedure TIndexArray.expand; var Delta, NewCapacity, OldCapacity: Integer; begin if (FCapacity > 64) then Delta := FCapacity div 4 else if (FCapacity > 8) then Delta := 16 else Delta := 4; NewCapacity := FCapacity + Delta; OldCapacity := FCapacity; if (NewCapacity <> FCapacity) then begin try FMemAllocated := NewCapacity*sizeof(TSmallIntArray); if CheckLowCapacity(OldCapacity*sizeof(TSmallIntArray), newCapacity*sizeof(TSmallIntArray)) then LowCapacityError; ReallocMem(idxElements, NewCapacity*sizeof(TSmallIntArray)); Inc(TotalAllocatedMemory, NewCapacity*sizeof(TSmallIntArray)); except FreeMem(idxElements); raise; end; FCapacity := NewCapacity; end; end; function TIndexArray.GetElement(Element : TIndexNDX) : TSmallIntArray; begin if (Element > FCapacity) then ArrayIndexError(Element); Result := idxElements^[Element]; end; procedure TIndexArray.SetElement(Element : TIndexNDX; const NewValue: TSmallIntArray); begin if AutoSize and (FCount = FCapacity) then Expand; Assert(FCapacity >= FCount, Format('FCount = %d FCapacity = %d', [FCount, FCapacity])); if (Element > FCapacity) then ArrayIndexError(Element); idxElements^[Element] := NewValue; inc(FMemAllocated, NewValue.FCapacity); if (Element > FCount-1) then Inc(FCount); end; function TIndexArray.Add(const NewValue: TSmallIntArray): Integer; begin if AutoSize and (FCount = FCapacity) then Expand; Assert(FCapacity >= FCount, Format('FCount = %d FCapacity = %d', [FCount, FCapacity])); idxElements^[FCount] := NewValue; inc(FMemAllocated, NewValue.FCapacity); Inc(FCount); Result := FCount; end; { TThreadCustomArray } constructor TThreadCustomArray.Create(Items: Integer; VarType: Integer); begin inherited Create; InitializeCriticalSection(FLock); FCustomArray := TCustomArray.Create(Items, VarType); end; destructor TThreadCustomArray.Destroy; begin LockArray; // Make sure nobody else is inside the list. try FCustomArray.Free; inherited Destroy; finally UnlockArray; DeleteCriticalSection(FLock); end; end; function TThreadCustomArray.Add(Item: Variant): Integer; begin LockArray; Result := -1; try if (FCustomArray.IndexOf(Item) = -1) then Result := FCustomArray.Add(Item); finally UnlockArray; end; end; function TThreadCustomArray.LockArray: TCustomArray; begin EnterCriticalSection(FLock); Result := FCustomArray; end; function TThreadCustomArray.GetItem(Index: Integer): Variant; begin LockArray; try Result := FCustomArray.GetItem(Index); finally UnlockArray; end; end; function TThreadCustomArray.MemoryUsage: Integer; begin LockArray; try Result := FCustomArray.MemoryUsage; finally UnlockArray; end; end; procedure TThreadCustomArray.UnlockArray; begin LeaveCriticalSection(FLock); end; initialization { determine available memory } AvailableMemory := GetAvailableMem; TotalAllocatedMemory := 0; end.