unit ListClone; interface uses // SysUtils, // RTLConsts, uses_end; const { Maximum TListClone size } MaxListSize = Maxint div 16; resourcestring SListCapacityError = 'リストの容量が超えました (%d)'; SListCountError = 'リストの個数を超えました (%d)'; SListIndexError = 'リストのインデックスが範囲を超えています (%d)'; type { Exception classes } // EListError = class(Exception); { TListClone class } PPointerList = ^TPointerList; TPointerList = array[0..MaxListSize - 1] of Pointer; TListCloneSortCompare = function (Item1, Item2: Pointer): Integer; TListCloneNotification = (lnAdded, lnExtracted, lnDeleted); TListCloneAssignOp = (laCopy, laAnd, laOr, laXor, laSrcUnique, laDestUnique); TListClone = class(TObject) private FList: PPointerList; FCount: Integer; FCapacity: Integer; protected function Get(Index: Integer): Pointer; procedure Grow; virtual; procedure Put(Index: Integer; Item: Pointer); procedure Notify(Ptr: Pointer; Action: TListCloneNotification); virtual; procedure SetCapacity(NewCapacity: Integer); procedure SetCount(NewCount: Integer); public destructor Destroy; override; function Add(Item: Pointer): Integer; procedure Clear; virtual; procedure Delete(Index: Integer); class procedure Error(const Msg: string; Data: Integer); overload; virtual; class procedure Error(Msg: PResStringRec; Data: Integer); overload; procedure Exchange(Index1, Index2: Integer); function Expand: TListClone; function Extract(Item: Pointer): Pointer; function First: Pointer; function IndexOf(Item: Pointer): Integer; procedure Insert(Index: Integer; Item: Pointer); function Last: Pointer; procedure Move(CurIndex, NewIndex: Integer); function Remove(Item: Pointer): Integer; procedure Pack; procedure Sort(Compare: TListCloneSortCompare); procedure Assign(ListA: TListClone; AOperator: TListCloneAssignOp = laCopy; ListB: TListClone = nil); property Capacity: Integer read FCapacity write SetCapacity; property Count: Integer read FCount write SetCount; property Items[Index: Integer]: Pointer read Get write Put; default; property List: PPointerList read FList; end; implementation { TListClone } destructor TListClone.Destroy; begin Clear; end; function TListClone.Add(Item: Pointer): Integer; begin Result := FCount; if Result = FCapacity then Grow; FList^[Result] := Item; Inc(FCount); if Item <> nil then Notify(Item, lnAdded); end; procedure TListClone.Clear; begin SetCount(0); SetCapacity(0); end; procedure TListClone.Delete(Index: Integer); var Temp: Pointer; begin if (Index < 0) or (Index >= FCount) then Error(@SListIndexError, Index); Temp := Items[Index]; Dec(FCount); if Index < FCount then System.Move(FList^[Index + 1], FList^[Index], (FCount - Index) * SizeOf(Pointer)); if Temp <> nil then Notify(Temp, lnDeleted); end; class procedure TListClone.Error(const Msg: string; Data: Integer); function ReturnAddr: Pointer; asm MOV EAX,[EBP+4] end; begin // raise EListError.CreateFmt(Msg, [Data]) at ReturnAddr; Assert(False, 'Listエラー') end; class procedure TListClone.Error(Msg: PResStringRec; Data: Integer); begin TListClone.Error(LoadResString(Msg), Data); end; procedure TListClone.Exchange(Index1, Index2: Integer); 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]; FList^[Index1] := FList^[Index2]; FList^[Index2] := Item; end; function TListClone.Expand: TListClone; begin if FCount = FCapacity then Grow; Result := Self; end; function TListClone.First: Pointer; begin Result := Get(0); end; function TListClone.Get(Index: Integer): Pointer; begin if (Index < 0) or (Index >= FCount) then Error(@SListIndexError, Index); Result := FList^[Index]; end; procedure TListClone.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 TListClone.IndexOf(Item: Pointer): Integer; begin Result := 0; while (Result < FCount) and (FList^[Result] <> Item) do Inc(Result); if Result = FCount then Result := -1; end; procedure TListClone.Insert(Index: Integer; Item: Pointer); begin if (Index < 0) or (Index > FCount) then Error(@SListIndexError, Index); if FCount = FCapacity then Grow; if Index < FCount then System.Move(FList^[Index], FList^[Index + 1], (FCount - Index) * SizeOf(Pointer)); FList^[Index] := Item; Inc(FCount); if Item <> nil then Notify(Item, lnAdded); end; function TListClone.Last: Pointer; begin Result := Get(FCount - 1); end; procedure TListClone.Move(CurIndex, NewIndex: Integer); var Item: Pointer; begin if CurIndex <> NewIndex then begin if (NewIndex < 0) or (NewIndex >= FCount) then Error(@SListIndexError, NewIndex); Item := Get(CurIndex); FList^[CurIndex] := nil; Delete(CurIndex); Insert(NewIndex, nil); FList^[NewIndex] := Item; end; end; procedure TListClone.Put(Index: Integer; Item: Pointer); var Temp: Pointer; begin if (Index < 0) or (Index >= FCount) then Error(@SListIndexError, Index); if Item <> FList^[Index] then begin Temp := FList^[Index]; FList^[Index] := Item; if Temp <> nil then Notify(Temp, lnDeleted); if Item <> nil then Notify(Item, lnAdded); end; end; function TListClone.Remove(Item: Pointer): Integer; begin Result := IndexOf(Item); if Result >= 0 then Delete(Result); end; procedure TListClone.Pack; var I: Integer; begin for I := FCount - 1 downto 0 do if Items[I] = nil then Delete(I); end; procedure TListClone.SetCapacity(NewCapacity: Integer); begin if (NewCapacity < FCount) or (NewCapacity > MaxListSize) then Error(@SListCapacityError, NewCapacity); if NewCapacity <> FCapacity then begin ReallocMem(FList, NewCapacity * SizeOf(Pointer)); FCapacity := NewCapacity; end; end; procedure TListClone.SetCount(NewCount: Integer); var I: Integer; begin if (NewCount < 0) or (NewCount > MaxListSize) then Error(@SListCountError, NewCount); if NewCount > FCapacity then SetCapacity(NewCount); if NewCount > FCount then FillChar(FList^[FCount], (NewCount - FCount) * SizeOf(Pointer), 0) else for I := FCount - 1 downto NewCount do Delete(I); FCount := NewCount; end; procedure QuickSort(SorTListClone: PPointerList; L, R: Integer; SCompare: TListCloneSortCompare); var I, J: Integer; P, T: Pointer; begin repeat I := L; J := R; P := SorTListClone^[(L + R) shr 1]; repeat while SCompare(SorTListClone^[I], P) < 0 do Inc(I); while SCompare(SorTListClone^[J], P) > 0 do Dec(J); if I <= J then begin T := SorTListClone^[I]; SorTListClone^[I] := SorTListClone^[J]; SorTListClone^[J] := T; Inc(I); Dec(J); end; until I > J; if L < J then QuickSort(SorTListClone, L, J, SCompare); L := I; until I >= R; end; procedure TListClone.Sort(Compare: TListCloneSortCompare); begin if (FList <> nil) and (Count > 0) then QuickSort(FList, 0, Count - 1, Compare); end; function TListClone.Extract(Item: Pointer): Pointer; var I: Integer; begin Result := nil; I := IndexOf(Item); if I >= 0 then begin Result := Item; FList^[I] := nil; Delete(I); Notify(Result, lnExtracted); end; end; procedure TListClone.Notify(Ptr: Pointer; Action: TListCloneNotification); begin end; procedure TListClone.Assign(ListA: TListClone; AOperator: TListCloneAssignOp; ListB: TListClone); var I: Integer; LTemp, LSource: TListClone; begin // ListB given? if ListB <> nil then begin LSource := ListB; Assign(ListA); end else LSource := ListA; // on with the show case AOperator of // 12345, 346 = 346 : only those in the new list laCopy: begin Clear; Capacity := LSource.Capacity; for I := 0 to LSource.Count - 1 do Add(LSource[I]); end; // 12345, 346 = 34 : intersection of the two lists laAnd: for I := Count - 1 downto 0 do if LSource.IndexOf(Items[I]) = -1 then Delete(I); // 12345, 346 = 123456 : union of the two lists laOr: for I := 0 to LSource.Count - 1 do if IndexOf(LSource[I]) = -1 then Add(LSource[I]); // 12345, 346 = 1256 : only those not in both lists laXor: begin LTemp := TListClone.Create; // Temp holder of 4 byte values try LTemp.Capacity := LSource.Count; for I := 0 to LSource.Count - 1 do if IndexOf(LSource[I]) = -1 then LTemp.Add(LSource[I]); for I := Count - 1 downto 0 do if LSource.IndexOf(Items[I]) <> -1 then Delete(I); I := Count + LTemp.Count; if Capacity < I then Capacity := I; for I := 0 to LTemp.Count - 1 do Add(LTemp[I]); finally LTemp.Free; end; end; // 12345, 346 = 125 : only those unique to source laSrcUnique: for I := Count - 1 downto 0 do if LSource.IndexOf(Items[I]) <> -1 then Delete(I); // 12345, 346 = 6 : only those unique to dest laDestUnique: begin LTemp := TListClone.Create; try LTemp.Capacity := LSource.Count; for I := LSource.Count - 1 downto 0 do if IndexOf(LSource[I]) = -1 then LTemp.Add(LSource[I]); Assign(LTemp); finally LTemp.Free; end; end; end; end; end.