mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-08 22:38:05 +02:00
911 lines
21 KiB
ObjectPascal
911 lines
21 KiB
ObjectPascal
{ $Id$ }
|
|
{
|
|
*****************************************************************************
|
|
This file is part of the Lazarus Component Library (LCL)
|
|
|
|
See the file COPYING.modifiedLGPL.txt, included in this distribution,
|
|
for details about the license.
|
|
*****************************************************************************
|
|
|
|
Author: Marc Weustink
|
|
|
|
Abstract:
|
|
The Map maps an unique ID to arbitrary data. The ID->Data is stored in a
|
|
Average Level binary Tree for fast indexing.
|
|
The map maintans also a linked list beween the ordered items for fast
|
|
iterating through all elements.
|
|
The ID can be signed or unsigned, with a size of 1,2,4,8,16 or 32 bytes
|
|
The data can be of any (constant) size.
|
|
}
|
|
unit maps;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, Math, FPCAdds, AvgLvlTree, typinfo;
|
|
|
|
type
|
|
TMapIdType = (itu1, its1, itu2, its2, itu4, its4, itu8, its8, itu16, its16,
|
|
itu32, its32);
|
|
|
|
const
|
|
{$ifdef CPU64}
|
|
itsPtrSize = its8;
|
|
ituPtrSize = itu8;
|
|
{$else}
|
|
itsPtrSize = its4;
|
|
ituPtrSize = itu4;
|
|
{$endif}
|
|
|
|
type
|
|
PMapItem = ^TMapItem;
|
|
|
|
PMapLink = ^TMapLink;
|
|
TMapLink = record
|
|
Previous, Next: PMapItem;
|
|
end;
|
|
|
|
PMapID = ^TMapID;
|
|
TMapID = record
|
|
case TMapIdType of
|
|
itu1: (U1: Byte);
|
|
its1: (S1: ShortInt);
|
|
itu2: (U2: Word);
|
|
its2: (S2: SmallInt);
|
|
itu4: (U4: LongWord);
|
|
its4: (S4: LongInt);
|
|
itu8: (U8: QWord);
|
|
its8: (S8: Int64);
|
|
{$IFDEF ENDIAN_LITTLE}
|
|
itu16: (U16L: QWord; U16H: QWord);
|
|
its16: (S16L: QWord; S16H: Int64);
|
|
itu32: (U32LL: QWord; U32LH: QWord; U32HL: QWord; U32HH: QWord);
|
|
its32: (S32LL: QWord; S32LH: QWord; S32HL: QWord; S32HH: Int64);
|
|
{$ELSE}
|
|
itu16: (U16H: QWord; U16L: QWord);
|
|
its16: (S16H: Int64; S16L: QWord);
|
|
itu32: (U32HH: QWord; U32HL: QWord; U32LH: QWord; U32LL: QWord);
|
|
its32: (S32HH: Int64; S32HL: QWord; S32LH: QWord; S32LL: QWord);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
TMapItem = packed record
|
|
Link: TMapLink;
|
|
ID: TMapID
|
|
{ Data: record end; }
|
|
{ Data follows imediately }
|
|
end;
|
|
|
|
{ TBaseMap }
|
|
|
|
TBaseMapIterator = class;
|
|
|
|
TBaseMap = class(TPersistent)
|
|
private
|
|
FTree: TAvgLvlTree;
|
|
FIdType: TMapIdType;
|
|
FDataSize: Cardinal;
|
|
FFirst: PMapItem; // First element of our linkedlist
|
|
FLast: PMapItem; // Last element of our linkedlist
|
|
FIterators: TList; // A List of iterators iterating us
|
|
function FindNode(const AId): TAvgLvlTreeNode;
|
|
function FindItem(const AId): PMapItem;
|
|
procedure FreeData(ANode: TAvgLvlTreeNode);
|
|
function TreeCompareID(Sender: TAvgLvlTree; AItem1, AItem2: Pointer): Integer;
|
|
//--
|
|
procedure IteratorAdd(AIterator: TBaseMapIterator);
|
|
procedure IteratorRemove(AIterator: TBaseMapIterator);
|
|
protected
|
|
procedure InternalAdd(const AId, AData);
|
|
function InternalGetData(AItem: PMapItem; out AData): Boolean;
|
|
function InternalGetDataPtr(AItem: PMapItem): Pointer;
|
|
function InternalGetId(AItem: PMapItem; out AID): Boolean;
|
|
function InternalSetData(AItem: PMapItem; const AData): Boolean;
|
|
procedure ReleaseData(ADataPtr: Pointer); virtual;
|
|
public
|
|
constructor Create(AIdType: TMapIdType; ADataSize: Cardinal);
|
|
procedure Clear;
|
|
function Count: SizeInt;
|
|
function Delete(const AId): Boolean;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ TBaseMapIterator }
|
|
|
|
TBaseMapIterator = class(TObject)
|
|
private
|
|
FMap: TBaseMap;
|
|
FCurrent: PMapItem;
|
|
FInValid: Boolean; // Set if our current is removed or when a locate failed, in those cases current is next
|
|
// will be set to next
|
|
FBOM: Boolean; // Begin Of Map
|
|
FEOM: Boolean; // End Of Map
|
|
procedure MapDestroyed; // Called when our map is destroyed
|
|
procedure MapCleared; // Called when our map is cleared
|
|
procedure ItemRemove(AData: Pointer); // Called when an Item is removed from the map
|
|
protected
|
|
procedure InternalCreate(AMap: TBaseMap);
|
|
function InternalLocate(const AId): Boolean; //True if match found. If not found, current is next and Invalid is set
|
|
procedure Validate;
|
|
procedure ValidateMap;
|
|
property Current: PMapItem read FCurrent;
|
|
public
|
|
destructor Destroy; override;
|
|
procedure First;
|
|
procedure Next;
|
|
procedure Previous;
|
|
procedure Last;
|
|
function Valid: Boolean; // false if our map is gone
|
|
|
|
property BOM: Boolean read FBOM;
|
|
property EOM: Boolean read FEOM;
|
|
end;
|
|
|
|
{ TMap }
|
|
|
|
TMap = class(TBaseMap)
|
|
private
|
|
protected
|
|
public
|
|
procedure Add(const AId, AData);
|
|
function HasId(const AID): Boolean;
|
|
function GetData(const AId; out AData): Boolean;
|
|
function GetDataPtr(const AId): Pointer;
|
|
function SetData(const AId, AData): Boolean;
|
|
end;
|
|
|
|
{ TMapIterator }
|
|
|
|
TMapIterator = class(TBaseMapIterator)
|
|
private
|
|
protected
|
|
public
|
|
constructor Create(AMap: TMap);
|
|
function DataPtr: Pointer;
|
|
procedure GetData(out AData);
|
|
procedure GetID(out AID);
|
|
function Locate(const AId): Boolean;
|
|
procedure SetData(const AData);
|
|
end;
|
|
|
|
{ TTypedMap }
|
|
|
|
TTypedMap = class(TBaseMap)
|
|
private
|
|
FTypeInfo: POinter;
|
|
protected
|
|
function InternalSetData(AItem: PMapItem; const AData): Boolean;
|
|
procedure ReleaseData(ADataPtr: Pointer); override;
|
|
public
|
|
procedure Add(const AId, AData);
|
|
constructor Create(AIdType: TMapIdType; ATypeInfo: PTypeInfo);
|
|
destructor Destroy; override;
|
|
function HasId(const AID): Boolean;
|
|
function GetData(const AId; out AData): Boolean;
|
|
function GetDataPtr(const AId): Pointer;
|
|
function SetData(const AId, AData): Boolean;
|
|
end;
|
|
|
|
{ TTypedMapIterator }
|
|
|
|
TTypedMapIterator = class(TBaseMapIterator)
|
|
private
|
|
protected
|
|
public
|
|
constructor Create(AMap: TTypedMap);
|
|
procedure GetData(out AData);
|
|
procedure GetID(out AID);
|
|
function Locate(const AId): Boolean;
|
|
procedure SetData(const AData);
|
|
end;
|
|
|
|
|
|
function MapReport(AMap: TBaseMap): String;
|
|
|
|
implementation
|
|
|
|
const
|
|
DUPLICATE_ID = 'Duplicate ID: %s';
|
|
|
|
const
|
|
ID_LENGTH: array[TMapIdType] of Byte = (1,1,2,2,4,4,8,8,16,16,32,32);
|
|
|
|
|
|
function MapReport(AMap: TBaseMap): String;
|
|
begin
|
|
if (AMap = nil) or (AMap.FTree = nil)
|
|
then Result := ''
|
|
else Result := AMap.FTree.ReportAsString;
|
|
end;
|
|
|
|
|
|
{ TBaseMap }
|
|
|
|
procedure TBaseMap.Clear;
|
|
var
|
|
n: Integer;
|
|
begin
|
|
FreeData(FTree.Root);
|
|
FTree.Clear;
|
|
FFirst := nil;
|
|
FLast := nil;
|
|
|
|
// notify our iterators
|
|
if FIterators <> nil
|
|
then begin
|
|
for n := 0 to FIterators.Count - 1 do
|
|
TBaseMapIterator(FIterators[n]).MapCleared;
|
|
end;
|
|
end;
|
|
|
|
function TBaseMap.Count: SizeInt;
|
|
begin
|
|
Result := FTree.Count;
|
|
end;
|
|
|
|
constructor TBaseMap.Create(AIdType: TMapIdType; ADataSize: Cardinal);
|
|
begin
|
|
inherited Create;
|
|
FIdType := AIdType;
|
|
FDataSize := ADataSize;
|
|
FTree := TAvgLvlTree.CreateObjectCompare(@TreeCompareID);
|
|
end;
|
|
|
|
function TBaseMap.Delete(const AId): Boolean;
|
|
var
|
|
Node: TAvgLvlTreeNode;
|
|
n: integer;
|
|
begin
|
|
Node := FindNode(AId);
|
|
Result := Node <> nil;
|
|
if not result then Exit;
|
|
|
|
// Remove from linked list
|
|
if PMapItem(Node.Data)^.Link.Next = nil //we were last
|
|
then FLast := PMapItem(Node.Data)^.Link.Previous
|
|
else PMapItem(Node.Data)^.Link.Next^.Link.Previous := PMapItem(Node.Data)^.Link.Previous;
|
|
if PMapItem(Node.Data)^.Link.Previous = nil //we were fist
|
|
then FFirst := PMapItem(Node.Data)^.Link.Next
|
|
else PMapItem(Node.Data)^.Link.Previous^.Link.Next := PMapItem(Node.Data)^.Link.Next;
|
|
|
|
// CheckIterators
|
|
if FIterators <> nil
|
|
then begin
|
|
for n := 0 to FIterators.Count - 1 do
|
|
TBaseMapIterator(FIterators[n]).ItemRemove(Node.Data);
|
|
end;
|
|
|
|
ReleaseData(InternalGetDataPtr(Node.Data));
|
|
FreeMem(Node.Data);
|
|
FTree.Delete(Node);
|
|
end;
|
|
|
|
destructor TBaseMap.Destroy;
|
|
var
|
|
n: Integer;
|
|
begin
|
|
// notify our iterators
|
|
if FIterators <> nil
|
|
then begin
|
|
for n := 0 to FIterators.Count - 1 do
|
|
TBaseMapIterator(FIterators[n]).MapDestroyed;
|
|
end;
|
|
|
|
FreeData(FTree.Root);
|
|
FTree.Clear;
|
|
FreeAndNil(FTree);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TBaseMap.FindItem(const AId): PMapItem;
|
|
var
|
|
Node: TAvgLvlTreeNode;
|
|
begin
|
|
Node := FindNode(AId);
|
|
if Node = nil
|
|
then Result := nil
|
|
else Result := Node.Data;
|
|
end;
|
|
|
|
function TBaseMap.FindNode(const AId): TAvgLvlTreeNode;
|
|
var
|
|
Item: TMapItem;
|
|
begin
|
|
Move(AID, Item.ID, ID_LENGTH[FIdType]);
|
|
Result := FTree.Find(@Item);
|
|
end;
|
|
|
|
procedure TBaseMap.FreeData(ANode: TAvgLvlTreeNode);
|
|
begin
|
|
if ANode = nil then Exit;
|
|
FreeData(ANode.Left);
|
|
FreeData(ANode.Right);
|
|
ReleaseData(InternalGetDataPtr(ANode.Data));
|
|
FreeMem(ANode.Data);
|
|
end;
|
|
|
|
procedure TBaseMap.InternalAdd(const AId, AData);
|
|
procedure Error;
|
|
const
|
|
a: PChar = '0123456789ABCDEF';
|
|
var
|
|
n: Integer;
|
|
S: String;
|
|
p: PByte;
|
|
begin
|
|
SetLength(S, ID_LENGTH[FIdType] * 2);
|
|
{$IFDEF ENDIAN_BIG}
|
|
p := @AId;
|
|
{$ELSE}
|
|
p := @AId + ID_LENGTH[FIdType] - 1;
|
|
{$ENDIF}
|
|
for n := 1 to ID_LENGTH[FIdType] do
|
|
begin
|
|
S[2 * n - 1] := a[(p^ shr 4) and $F];
|
|
S[2 * n] := a[p^ and $F];
|
|
{$IFDEF ENDIAN_BIG}
|
|
Inc(p);
|
|
{$ELSE}
|
|
Dec(p);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
raise EListError.CreateFmt(DUPLICATE_ID, [S]);
|
|
end;
|
|
var
|
|
item: PMapItem;
|
|
p: Pointer;
|
|
Node, NewNode: TAvgLvlTreeNode;
|
|
begin
|
|
if FindNode(AId) <> nil
|
|
then begin
|
|
Error;
|
|
Exit;
|
|
end;
|
|
|
|
Item := GetMem(SizeOF(TMapLink) + cardinal(ID_LENGTH[FIdType]) + FDataSize);
|
|
p := @item^.ID;
|
|
Move(AId, p^, ID_LENGTH[FIdType]);
|
|
inc(p, ID_LENGTH[FIdType]);
|
|
Move(Adata, p^, FDataSize);
|
|
NewNode := FTree.Add(item);
|
|
|
|
// Update linked list
|
|
Node := FTree.FindPrecessor(NewNode);
|
|
if Node = nil
|
|
then begin
|
|
// no previous node
|
|
FFirst := Item;
|
|
Node := FTree.FindSuccessor(NewNode);
|
|
Item^.Link.Previous := nil;
|
|
if Node = nil
|
|
then begin
|
|
// We're the only one
|
|
Item^.Link.Next := nil;
|
|
FLast := Item;
|
|
end
|
|
else begin
|
|
Item^.Link.Next := Node.Data;
|
|
PMapItem(Node.Data)^.Link.Previous := Item;
|
|
end;
|
|
end
|
|
else begin
|
|
// there is a prevous node
|
|
Item^.Link.Previous := Node.Data;
|
|
Item^.Link.Next := PMapItem(Node.Data)^.Link.Next;
|
|
PMapItem(Node.Data)^.Link.Next := Item;
|
|
if Item^.Link.Next = nil // no item after us, so we're last
|
|
then FLast := Item
|
|
else Item^.Link.Next^.Link.Previous := Item;
|
|
end;
|
|
end;
|
|
|
|
function TBaseMap.InternalGetData(AItem: PMapItem; out AData): Boolean;
|
|
var
|
|
p: Pointer;
|
|
begin
|
|
Result := AItem <> nil;
|
|
if not result then Exit;
|
|
|
|
p := @AItem^.ID;
|
|
Inc(p, ID_LENGTH[FIdType]);
|
|
Move(p^, AData, FDataSize);
|
|
end;
|
|
|
|
function TBaseMap.InternalGetDataPtr(AItem: PMapItem): Pointer;
|
|
begin
|
|
if AItem = nil
|
|
then begin
|
|
Result := nil;
|
|
Exit;
|
|
end;
|
|
|
|
Result := @AItem^.ID;
|
|
Inc(Result, ID_LENGTH[FIdType]);
|
|
end;
|
|
|
|
function TBaseMap.InternalGetId(AItem: PMapItem; out AID): Boolean;
|
|
begin
|
|
Result := AItem <> nil;
|
|
if not Result then Exit;
|
|
|
|
Move(AItem^.ID, AID, ID_LENGTH[FIdType]);
|
|
end;
|
|
|
|
function TBaseMap.InternalSetData(AItem: PMapItem; const AData): Boolean;
|
|
var
|
|
p: Pointer;
|
|
begin
|
|
Result := AItem <> nil;
|
|
if not Result then Exit;
|
|
|
|
p := @AItem^.ID;
|
|
Inc(p, ID_LENGTH[FIdType]);
|
|
Move(AData, p^, FDataSize);
|
|
end;
|
|
|
|
procedure TBaseMap.IteratorAdd(AIterator: TBaseMapIterator);
|
|
begin
|
|
if FIterators = nil then FIterators := TList.Create;
|
|
FIterators.Add(AIterator);
|
|
end;
|
|
|
|
procedure TBaseMap.IteratorRemove(AIterator: TBaseMapIterator);
|
|
begin
|
|
if FIterators = nil then Exit;
|
|
FIterators.Remove(AIterator);
|
|
if FIterators.Count = 0 then FreeAndNil(FIterators);
|
|
end;
|
|
|
|
procedure TBaseMap.ReleaseData(ADataPtr: Pointer);
|
|
begin
|
|
end;
|
|
|
|
function TBaseMap.TreeCompareID(Sender: TAvgLvlTree; AItem1, AItem2: Pointer): Integer;
|
|
var
|
|
Item1: PMapItem absolute AItem1;
|
|
Item2: PMapItem absolute AItem2;
|
|
begin
|
|
case FIdType of
|
|
itu1: Result := CompareValue(Item1^.ID.U1, Item2^.ID.U1);
|
|
its1: Result := CompareValue(Item1^.ID.S1, Item2^.ID.S1);
|
|
itu2: Result := CompareValue(Item1^.ID.U2, Item2^.ID.U2);
|
|
its2: Result := CompareValue(Item1^.ID.S2, Item2^.ID.S2);
|
|
itu4: Result := CompareValue(Item1^.ID.U4, Item2^.ID.U4);
|
|
its4: Result := CompareValue(Item1^.ID.S4, Item2^.ID.S4);
|
|
itu8: Result := CompareValue(Item1^.ID.U8, Item2^.ID.U8);
|
|
its8: Result := CompareValue(Item1^.ID.S8, Item2^.ID.S8);
|
|
itu16: begin
|
|
Result := CompareValue(Item1^.ID.U16H, Item2^.ID.U16H);
|
|
if Result = 0
|
|
then Result := CompareValue(Item1^.ID.U16L, Item2^.ID.U16L);
|
|
end;
|
|
its16: begin
|
|
Result := CompareValue(Item1^.ID.S16H, Item2^.ID.S16H);
|
|
if Result = 0
|
|
then Result := CompareValue(Item1^.ID.S16L, Item2^.ID.S16L);
|
|
end;
|
|
itu32: begin
|
|
Result := CompareValue(Item1^.ID.U32HH,Item2^.ID.U32HH);
|
|
if Result = 0
|
|
then Result := CompareValue(Item1^.ID.U32HL, Item2^.ID.U32HL);
|
|
if Result = 0
|
|
then Result := CompareValue(Item1^.ID.U32LH, Item2^.ID.U32LH);
|
|
if Result = 0
|
|
then Result := CompareValue(Item1^.ID.U32LL, Item2^.ID.U32LL);
|
|
end;
|
|
its32: begin
|
|
Result := CompareValue(Item1^.ID.S32HH, Item2^.ID.S32HH);
|
|
if Result = 0
|
|
then Result := CompareValue(Item1^.ID.S32HL, Item2^.ID.S32HL);
|
|
if Result = 0
|
|
then Result := CompareValue(Item1^.ID.S32LH, Item2^.ID.S32LH);
|
|
if Result = 0
|
|
then Result := CompareValue(Item1^.ID.S32LL, Item2^.ID.S32LL);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ TBaseMapIterator }
|
|
|
|
|
|
destructor TBaseMapIterator.Destroy;
|
|
begin
|
|
if FMap <> nil then FMap.IteratorRemove(Self);
|
|
FMap := nil;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TBaseMapIterator.First;
|
|
begin
|
|
if FMap = nil then Exit;
|
|
FCurrent := FMap.FFirst;
|
|
FBOM := FCurrent = nil;
|
|
FEOM := FCurrent = nil;
|
|
end;
|
|
|
|
procedure TBaseMapIterator.InternalCreate(AMap: TBaseMap);
|
|
begin
|
|
inherited Create;
|
|
FMap := AMap;
|
|
FMap.IteratorAdd(Self);
|
|
FCurrent := FMap.FFirst;
|
|
FBOM := FCurrent = nil;
|
|
FEOM := FCurrent = nil;
|
|
end;
|
|
|
|
function TBaseMapIterator.InternalLocate(const AId): Boolean;
|
|
var
|
|
C: Integer;
|
|
Node, LastNext: TAvgLvlTreeNode;
|
|
Item: TMapItem;
|
|
begin
|
|
ValidateMap;
|
|
|
|
FInvalid := True;
|
|
|
|
Node := FMap.FTree.Root;
|
|
if Node <> nil
|
|
then begin
|
|
Move(AID, Item.ID, ID_LENGTH[FMap.FIdType]);
|
|
LastNext := nil;
|
|
while True do
|
|
begin
|
|
C := FMap.TreeCompareID(nil, @Item, Node.Data);
|
|
if C = 0
|
|
then begin
|
|
FInvalid := False;
|
|
Break;
|
|
end;
|
|
if C < 0
|
|
then begin
|
|
if Node.Left = nil then Break; // no smaller node so we're at next best
|
|
LastNext := Node;
|
|
Node := Node.Left;
|
|
end
|
|
else begin
|
|
Node := Node.Right; // try a bigger one
|
|
if Node = nil
|
|
then begin
|
|
Node := LastNext;
|
|
Break; // not larger
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if Node = nil
|
|
then begin
|
|
FEOM := True;
|
|
FCurrent := FMap.FLast;
|
|
FBOM := FCurrent = nil;
|
|
end
|
|
else begin
|
|
FCurrent := Node.Data;
|
|
FBOM := FCurrent = nil;
|
|
FEOM := FCurrent = nil;
|
|
end;
|
|
Result := not FInvalid;
|
|
end;
|
|
|
|
procedure TBaseMapIterator.ItemRemove(AData: Pointer);
|
|
begin
|
|
if AData <> FCurrent then Exit;
|
|
FInvalid := True;
|
|
FCurrent := FCurrent^.Link.Next;
|
|
FEOM := FCurrent = nil;
|
|
end;
|
|
|
|
procedure TBaseMapIterator.Last;
|
|
begin
|
|
if FMap = nil then Exit;
|
|
FCurrent := FMap.FLast;
|
|
FEOM := FCurrent = nil;
|
|
FBOM := FCurrent = nil;
|
|
end;
|
|
|
|
procedure TBaseMapIterator.MapCleared;
|
|
begin
|
|
FCurrent := nil;
|
|
FBOM := True;
|
|
FEOM := True;
|
|
FInvalid := False;
|
|
end;
|
|
|
|
procedure TBaseMapIterator.MapDestroyed;
|
|
begin
|
|
FMap := nil;
|
|
FCurrent := nil;
|
|
FBOM := True;
|
|
FEOM := True;
|
|
FInvalid := False;
|
|
end;
|
|
|
|
procedure TBaseMapIterator.Next;
|
|
procedure Error;
|
|
begin
|
|
raise EInvalidOperation.Create('Cannot move past end');
|
|
end;
|
|
begin
|
|
if FInvalid
|
|
then begin
|
|
// We are already at the next
|
|
FInvalid := False;
|
|
Exit;
|
|
end;
|
|
if FEOM then Error;
|
|
|
|
if FBOM then
|
|
begin
|
|
// Get first element
|
|
FCurrent := FMap.FFirst;
|
|
FBOM := FCurrent = nil;
|
|
end
|
|
else begin
|
|
FCurrent := FCurrent^.Link.Next;
|
|
end;
|
|
|
|
FEOM := FCurrent = nil
|
|
end;
|
|
|
|
procedure TBaseMapIterator.Previous;
|
|
procedure Error;
|
|
begin
|
|
raise EInvalidOperation.Create('Cannot move before start');
|
|
end;
|
|
begin
|
|
if FBOM then Error;
|
|
FInvalid := False;
|
|
|
|
if FEOM then
|
|
begin
|
|
// Get last element
|
|
FCurrent := FMap.FLast;
|
|
FEOM := FCurrent = nil
|
|
end
|
|
else begin
|
|
FCurrent := FCurrent^.Link.Previous;
|
|
end;
|
|
|
|
FBOM := FCurrent = nil
|
|
end;
|
|
|
|
function TBaseMapIterator.Valid: Boolean;
|
|
begin
|
|
Result := FMap <> nil;
|
|
end;
|
|
|
|
procedure TBaseMapIterator.Validate;
|
|
begin
|
|
ValidateMap;
|
|
|
|
if FCurrent = nil
|
|
then raise EInvalidOperation.Create('No current item');
|
|
|
|
if FInvalid
|
|
then raise EInvalidOperation.Create('Current item removed');
|
|
end;
|
|
|
|
procedure TBaseMapIterator.ValidateMap;
|
|
begin
|
|
if FMap = nil
|
|
then raise EInvalidOperation.Create('Map destroyed');
|
|
end;
|
|
|
|
{ TMap }
|
|
|
|
procedure TMap.Add(const AId, AData);
|
|
begin
|
|
InternalAdd(AId, AData);
|
|
end;
|
|
|
|
function TMap.GetData(const AId; out AData): Boolean;
|
|
begin
|
|
Result := InternalGetData(FindItem(AId), AData);
|
|
end;
|
|
|
|
function TMap.GetDataPtr(const AId): Pointer;
|
|
begin
|
|
Result := InternalGetDataPtr(FindItem(AId));
|
|
end;
|
|
|
|
function TMap.HasId(const AID): Boolean;
|
|
begin
|
|
Result := FindNode(AId) <> nil;
|
|
end;
|
|
|
|
|
|
function TMap.SetData(const AId, AData): Boolean;
|
|
begin
|
|
Result := InternalSetData(FindItem(AId), AData);
|
|
end;
|
|
|
|
{ TTypedMap }
|
|
|
|
// some hack to get access to fpc internals
|
|
procedure fpc_AddRef(Data, TypeInfo: Pointer); external name 'FPC_ADDREF';
|
|
procedure fpc_DecRef(Data, TypeInfo: Pointer); external name 'FPC_DECREF';
|
|
|
|
procedure TTypedMap.Add(const AId, AData);
|
|
begin
|
|
InternalAdd(AId, AData);
|
|
fpc_AddRef(@Adata, FtypeInfo);
|
|
end;
|
|
|
|
constructor TTypedMap.Create(AIdType: TMapIdType; ATypeInfo: PTypeInfo);
|
|
var
|
|
Size: Integer;
|
|
TypeData: PTypeData;
|
|
begin
|
|
TypeData := GetTypedata(ATypeInfo);
|
|
case ATypeInfo^.Kind of
|
|
tkLString, tkAString, tkWString, tkInterface, tkDynArray, tkClass: begin
|
|
Size := SizeOf(Pointer);
|
|
end;
|
|
|
|
tkInteger, tkEnumeration, tkSet, tkWChar, tkChar: begin
|
|
case TypeData^.OrdType of
|
|
otSByte, otUByte: Size := SizeOf(Byte);
|
|
otSWord, otUWord: Size := SizeOf(Word);
|
|
otSLong, otULong: Size := SizeOf(LongWord);
|
|
else
|
|
Size := 0;
|
|
end;
|
|
end;
|
|
|
|
tkMethod : Size := SizeOf(TMethod);
|
|
tkVariant: Size := SizeOf(TVarData);
|
|
tkInt64 : Size := SizeOf(Int64);
|
|
tkQWord : Size := SizeOf(QWord);
|
|
tkBool : Size := SizeOf(Boolean);
|
|
|
|
tkFloat: begin
|
|
case TypeData^.FloatType of
|
|
ftSingle : Size := SizeOf(Single);
|
|
ftDouble : Size := SizeOf(Double);
|
|
ftExtended : Size := SizeOf(Extended);
|
|
ftComp : Size := SizeOf(Comp);
|
|
ftCurr : Size := SizeOf(Currency);
|
|
else
|
|
Size := 0;
|
|
end;
|
|
end;
|
|
|
|
tkSString: Size := TypeData^.MaxLength;
|
|
tkArray : Size := PInteger(TypeData)[0] * PInteger(TypeData)[1];
|
|
tkRecord,
|
|
tkObject : Size := PInteger(TypeData)[0];
|
|
else
|
|
Size := 0;
|
|
end;
|
|
|
|
if Size = 0 then raise EInvalidOperation.Create('Type must have a size');
|
|
|
|
FTypeInfo := ATypeInfo;
|
|
inherited Create(AIdType, Size);
|
|
end;
|
|
|
|
destructor TTypedMap.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TTypedMap.GetData(const AId; out AData): Boolean;
|
|
begin
|
|
Result := InternalGetData(FindItem(AId), AData);
|
|
if Result
|
|
then fpc_AddRef(@Adata, FtypeInfo);
|
|
end;
|
|
|
|
function TTypedMap.GetDataPtr(const AId): Pointer;
|
|
begin
|
|
Result := InternalGetDataPtr(FindItem(AId));
|
|
end;
|
|
|
|
function TTypedMap.HasId(const AID): Boolean;
|
|
begin
|
|
Result := FindNode(AId) <> nil;
|
|
end;
|
|
|
|
function TTypedMap.InternalSetData(AItem: PMapItem; const AData): Boolean;
|
|
var
|
|
Data: Pointer;
|
|
begin
|
|
// copy data first to allow Map[ID] := Map[ID]
|
|
GetMem(Data, FDataSize);
|
|
InternalGetData(AItem, Data^);
|
|
|
|
Result := inherited InternalSetData(AItem, AData);
|
|
if not Result then Exit;
|
|
|
|
fpc_AddRef(@AData, FtypeInfo);
|
|
fpc_DecRef(Data, FTypeInfo);
|
|
FreeMem(Data);
|
|
end;
|
|
|
|
procedure TTypedMap.ReleaseData(ADataPtr: Pointer);
|
|
begin
|
|
fpc_DecRef(ADataPtr, FTypeInfo);
|
|
end;
|
|
|
|
function TTypedMap.SetData(const AId, AData): Boolean;
|
|
var
|
|
Item: PMapItem;
|
|
begin
|
|
Item := FindItem(AId);
|
|
Result := Item <> nil;
|
|
if not Result then Exit;
|
|
InternalSetData(Item, AData);
|
|
end;
|
|
|
|
{ TMapIterator }
|
|
|
|
constructor TMapIterator.Create(AMap: TMap);
|
|
begin
|
|
InternalCreate(AMap);
|
|
end;
|
|
|
|
function TMapIterator.DataPtr: Pointer;
|
|
begin
|
|
Validate;
|
|
Result:=FMap.InternalGetDataPtr(FCurrent);
|
|
end;
|
|
|
|
procedure TMapIterator.GetData(out AData);
|
|
begin
|
|
Validate;
|
|
FMap.InternalGetData(FCurrent, AData);
|
|
end;
|
|
|
|
procedure TMapIterator.GetID(out AID);
|
|
begin
|
|
Validate;
|
|
FMap.InternalGetId(FCurrent, AId);
|
|
end;
|
|
|
|
function TMapIterator.Locate(const AId): Boolean;
|
|
begin
|
|
Result := InternalLocate(AID);
|
|
end;
|
|
|
|
procedure TMapIterator.SetData(const AData);
|
|
begin
|
|
Validate;
|
|
FMap.InternalSetData(FCurrent, AData);
|
|
end;
|
|
|
|
{ TTypedMapIterator }
|
|
|
|
constructor TTypedMapIterator.Create(AMap: TTypedMap);
|
|
begin
|
|
InternalCreate(AMap);
|
|
end;
|
|
|
|
procedure TTypedMapIterator.GetData(out AData);
|
|
begin
|
|
Validate;
|
|
FMap.InternalGetData(FCurrent, AData);
|
|
fpc_AddRef(@Adata, TTypedMap(FMap).FTypeInfo);
|
|
end;
|
|
|
|
procedure TTypedMapIterator.GetID(out AID);
|
|
begin
|
|
Validate;
|
|
FMap.InternalGetId(FCurrent, AId);
|
|
end;
|
|
|
|
function TTypedMapIterator.Locate(const AId): Boolean;
|
|
begin
|
|
Result := InternalLocate(AID);
|
|
end;
|
|
|
|
procedure TTypedMapIterator.SetData(const AData);
|
|
begin
|
|
Validate;
|
|
TTypedMap(FMap).InternalSetData(FCurrent, AData);
|
|
end;
|
|
|
|
end.
|