Changeset 6


Ignore:
Timestamp:
Mar 30, 2016, 10:50:22 PM (8 years ago)
Author:
chronos
Message:
  • Modified: TBlockMap reworked to sparse map using list of value changes. Now much less memory is required to keep sector map of high capacity drives. Also drawing of map is much faster.
Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/UBlockMap.pas

    r3 r6  
    66
    77uses
    8   Classes, SysUtils, Graphics, Math;
     8  Classes, SysUtils, Graphics, Math, Contnrs, Dialogs;
    99
    1010type
    1111  TSectorState = (bsNone, bsOk, bsDamaged);
     12
     13  TChange = class
     14    Index: Integer;
     15    Value: TSectorState;
     16  end;
    1217
    1318  { TBlockMap }
     
    1823    FDrawSize: TPoint;
    1924    FSectorPerBlock: Integer;
    20     FSectors: array of TSectorState;
    21     FBlocks: array of TSectorState;
     25    FSectorCount: Integer;
     26    FBlockCount: Integer;
     27    FChanges: TObjectList; // TObjectList<TChange>
    2228    function GetBlockCount: Integer;
    2329    function GetSector(Index: Integer): TSectorState;
    24     function GetSectorCount: Integer;
    2530    procedure SetBlockCount(AValue: Integer);
    2631    procedure SetBlockSize(AValue: TPoint);
     
    3035    procedure SetSectorPerBlock(AValue: Integer);
    3136    procedure UpdateBlockSize;
    32     procedure UpdateBlocks;
     37    function FindChange(Index: Integer): TChange;
     38    function GetCombinedSectors(StartIndex, EndIndex: Integer): TSectorState;
    3339  public
    3440    ItemsCount: TPoint;
    35     function CombineSectors(From, Count: Integer): TSectorState;
    3641    procedure Draw(Canvas: TCanvas);
    3742    procedure Clear;
    3843    constructor Create;
     44    destructor Destroy; override;
    3945    property BlockSize: TPoint read FBlockSize write SetBlockSize;
    4046    property DrawSize: TPoint read FDrawSize write SetDrawSize;
    4147    property SectorPerBlock: Integer read FSectorPerBlock write SetSectorPerBlock;
    42     property SectorCount: Integer read GetSectorCount write SetSectorCount;
     48    property SectorCount: Integer read FSectorCount write SetSectorCount;
    4349    property BlockCount: Integer read GetBlockCount;
    4450    property Sectors[Index: Integer]: TSectorState read GetSector write SetSector;
     
    4854implementation
    4955
     56resourcestring
     57  SBlockSizeZeroNotAllowed = 'BlockSize can''t be set to 0';
     58  SIndexOutOfRange = 'Index %d out of range';
     59  SEmptyChangesList = 'Empty changes list';
     60  SUnexpectedCombination = 'Unexpected combination';
     61
    5062{ TBlockMap }
    5163
    52 function TBlockMap.GetSectorCount: Integer;
    53 begin
    54   Result := Length(FSectors);
    55 end;
    56 
    5764procedure TBlockMap.SetBlockCount(AValue: Integer);
    5865begin
    59   SetLength(FBlocks, AValue);
     66  if FBlockCount = AValue then Exit;
     67  FBlockCount := AValue;
    6068end;
    6169
     
    6472  if (FBlockSize.X = AValue.X) and (FBlockSize.Y = AValue.Y) then Exit;
    6573  if (AValue.X = 0) or (AValue.Y = 0) then
    66     raise Exception.Create('BlockSize can''t be set to 0');
     74    raise Exception.Create(SBlockSizeZeroNotAllowed);
    6775  FBlockSize := AValue;
    6876end;
     
    7684
    7785function TBlockMap.GetSector(Index: Integer): TSectorState;
    78 begin
    79   Result := FSectors[Index];
     86var
     87  Change: TChange;
     88begin
     89  if Index < FSectorCount then begin
     90    Change := FindChange(Index);
     91    if Assigned(Change) then Result := Change.Value
     92      else raise Exception.Create(SIndexOutOfRange);
     93  end else raise Exception.Create(SIndexOutOfRange);
    8094end;
    8195
    8296function TBlockMap.GetBlockCount: Integer;
    8397begin
    84   Result := Length(FBlocks);
     98  Result := FBlockCount;
    8599end;
    86100
    87101procedure TBlockMap.SetSector(Index: Integer; AValue: TSectorState);
    88102var
    89   BlockIndex: Integer;
    90 begin
    91   FSectors[Index] := AValue;
    92 
    93   // Update block
    94   BlockIndex := Index div FSectorPerBlock;
    95   if (AValue = bsDamaged) then FBlocks[BlockIndex] := bsDamaged
    96   else if (AValue = bsOk) and (FBlocks[BlockIndex] = bsNone) then
    97     FBlocks[BlockIndex] := bsOk;
     103  LeftBeforeSame, RightBeforeSame: Boolean;
     104  LeftAfterSame, RightAfterSame: Boolean;
     105  Change: TChange;
     106  ChangeIndex: Integer;
     107begin
     108  Change := FindChange(Index);
     109  if Assigned(Change) then begin
     110    if Change.Value = AValue then Exit;
     111
     112    ChangeIndex := FChanges.IndexOf(Change);
     113    // Before
     114    if ChangeIndex > 0 then begin
     115      if (Index - TChange(FChanges[ChangeIndex]).Index) > 0 then LeftBeforeSame := True
     116        else LeftBeforeSame := TChange(FChanges[ChangeIndex - 1]).Value = Change.Value;
     117    end else begin
     118      LeftBeforeSame := Change.Value = bsNone;
     119    end;
     120    if ChangeIndex < (FChanges.Count - 1) then begin
     121      if (TChange(FChanges[ChangeIndex]).Index - Index) > 0 then RightBeforeSame := True
     122        else RightBeforeSame := TChange(FChanges[ChangeIndex + 1]).Value = Change.Value;
     123    end else begin
     124      RightBeforeSame := Change.Value = bsNone;
     125    end;
     126    // After
     127    if ChangeIndex > 0 then begin
     128      if (Index - TChange(FChanges[ChangeIndex]).Index) > 0 then LeftAfterSame := True
     129        else LeftAfterSame := TChange(FChanges[ChangeIndex - 1]).Value = AValue;
     130    end else begin
     131      LeftAfterSame := AValue = bsNone;
     132    end;
     133    if ChangeIndex < (FChanges.Count - 1) then begin
     134      if (TChange(FChanges[ChangeIndex]).Index - Index) > 0 then RightAfterSame := True
     135        else RightAfterSame := TChange(FChanges[ChangeIndex + 1]).Value = AValue;
     136    end else begin
     137      RightAfterSame := AValue = bsNone;
     138    end;
     139
     140    // Update items
     141    if not LeftBeforeSame and not RightBeforeSame and not LeftAfterSame and not RightAfterSame then begin
     142      Change.Value := AValue
     143    end else
     144    if not LeftBeforeSame and not RightBeforeSame and not LeftAfterSame and RightAfterSame then begin
     145      FChanges.Delete(ChangeIndex + 1);
     146      Change.Value := AValue;
     147    end else
     148    if not LeftBeforeSame and not RightBeforeSame and LeftAfterSame and not RightAfterSame then begin
     149      FChanges.Delete(ChangeIndex);
     150    end else
     151    if not LeftBeforeSame and not RightBeforeSame and LeftAfterSame and RightAfterSame then begin
     152      FChanges.Delete(ChangeIndex + 1);
     153      FChanges.Delete(ChangeIndex);
     154    end else
     155    if not LeftBeforeSame and RightBeforeSame and not LeftAfterSame and not RightAfterSame then begin
     156      FChanges.Insert(ChangeIndex + 1, TChange.Create);
     157      TChange(FChanges[ChangeIndex + 1]).Index := Index + 1;
     158      TChange(FChanges[ChangeIndex + 1]).Value := Change.Value;
     159      Change.Value := AValue;
     160    end else
     161    if not LeftBeforeSame and RightBeforeSame and LeftAfterSame and not RightAfterSame then begin
     162      Change.Index := Index + 1;
     163    end else
     164    if not LeftBeforeSame and RightBeforeSame and not LeftAfterSame and not RightAfterSame then begin
     165      FChanges.Insert(ChangeIndex, TChange.Create);
     166      TChange(FChanges[ChangeIndex]).Index := Index;
     167      TChange(FChanges[ChangeIndex]).Value := AValue;
     168    end else
     169    if LeftBeforeSame and not RightBeforeSame and not LeftAfterSame and RightAfterSame then begin
     170      TChange(FChanges[ChangeIndex + 1]).Index := Index;
     171    end else
     172    if LeftBeforeSame and RightBeforeSame and not LeftAfterSame and not RightAfterSame then begin
     173      if ChangeIndex > 0 then begin
     174        FChanges.Insert(ChangeIndex, TChange.Create);
     175        TChange(FChanges[ChangeIndex]).Index := Index;
     176        TChange(FChanges[ChangeIndex]).Value := AValue;
     177      end;
     178      FChanges.Insert(ChangeIndex + 1, TChange.Create);
     179      TChange(FChanges[ChangeIndex + 1]).Index := Index + 1;
     180      TChange(FChanges[ChangeIndex + 1]).Value := Change.Value;
     181      if ChangeIndex = 0 then
     182        TChange(FChanges[ChangeIndex]).Value := AValue;
     183    end else raise Exception.Create(SUnexpectedCombination);
     184  end else raise Exception.Create(Format(SIndexOutOfRange, [Index]));
    98185end;
    99186
    100187procedure TBlockMap.SetSectorCount(AValue: Integer);
    101 begin
    102   if Length(FSectors) = AValue then Exit;
    103   SetLength(FSectors, AValue);
     188var
     189  I: Integer;
     190begin
     191  if FSectorCount = AValue then Exit;
     192  if (FChanges.Count = 0) and (AValue > 0) then begin
     193    FChanges.Add(TChange.Create);
     194    TChange(FChanges[0]).Index := 0;
     195    TChange(FChanges[0]).Value := bsNone;
     196  end;
     197  FSectorCount := AValue;
     198
     199  // Cut changes outside of max sector count
     200  I := FChanges.Count - 1;
     201  while TChange(FChanges[I]).Index >= FSectorCount do Dec(I);
     202  if TChange(FChanges[I]).Index >= FSectorCount then FChanges.Count := I;
     203
    104204  UpdateBlockSize;
    105205end;
     
    117217  FSectorPerBlock := Ceil(SectorCount / (ItemsCount.X * ItemsCount.Y));
    118218  SetBlockCount(Ceil(SectorCount / FSectorPerBlock));
    119   UpdateBlocks;
    120 end;
    121 
    122 procedure TBlockMap.UpdateBlocks;
    123 var
    124   I: Integer;
    125 begin
    126   for I := 0 to BlockCount - 1 do
    127     FBlocks[I] := CombineSectors(I * FSectorPerBlock, FSectorPerBlock);
    128 end;
    129 
    130 function TBlockMap.CombineSectors(From, Count: Integer): TSectorState;
    131 var
    132   I: Integer;
    133   UpTo: Integer;
     219end;
     220
     221function TBlockMap.FindChange(Index: Integer): TChange;
     222var
     223  LeftIndex, MiddleIndex, RightIndex: Integer;
     224begin
     225  if Fchanges.Count = 0 then raise Exception.Create(SEmptyChangesList);
     226  if FChanges.Count = 1 then begin
     227    Result := TChange(FChanges[0]);
     228    Exit;
     229  end;
     230  Result := nil;
     231  LeftIndex := 0;
     232  RightIndex := FChanges.Count - 1;
     233  while (LeftIndex <= RightIndex) do begin
     234    MiddleIndex := LeftIndex + (RightIndex - LeftIndex) div 2;
     235    if (TChange(FChanges[MiddleIndex]).Index <= Index) and
     236    ((FChanges.Count = (MiddleIndex + 1)) or
     237    (TChange(FChanges[MiddleIndex + 1]).Index > Index)) then begin
     238      Result := TChange(FChanges[MiddleIndex]);
     239      Break;
     240    end;
     241    // Cut interval in half and use nearest section
     242    if TChange(FChanges[MiddleIndex]).Index < Index then begin
     243      if MiddleIndex = LeftIndex then Inc(LeftIndex)
     244        else LeftIndex := MiddleIndex
     245      end else begin
     246        if MiddleIndex = RightIndex then Dec(RightIndex)
     247          else RightIndex := MiddleIndex
     248      end;
     249  end;
     250end;
     251
     252function TBlockMap.GetCombinedSectors(StartIndex, EndIndex: Integer
     253  ): TSectorState;
     254var
     255  Change: TChange;
     256  ChangeIndex: Integer;
    134257begin
    135258  Result := bsNone;
    136   UpTo := From + Count - 1;
    137   if UpTo >= SectorCount then Upto := SectorCount - 1;
    138   for I := From to UpTo do begin
    139 
    140     if (Result = bsNone) and (Sectors[I] = bsOk) then Result := bsOk
    141     else if (Result = bsOk) and (Sectors[I] = bsDamaged) then Result := bsDamaged;
     259  Change := FindChange(StartIndex);
     260  if Assigned(Change) then begin
     261    ChangeIndex := FChanges.IndexOf(Change);
     262    while (ChangeIndex < FChanges.Count) and
     263     (TChange(FChanges[ChangeIndex]).Index <= EndIndex) do begin
     264      if (Result = bsNone) and (TChange(FChanges[ChangeIndex]).Value = bsOk) then Result := bsOk
     265        else if TChange(FChanges[ChangeIndex]).Value = bsDamaged then Result := bsDamaged;
     266      Inc(ChangeIndex);
     267    end;
    142268  end;
    143269end;
     
    161287      Rect := Bounds((I mod ItemsCount.X) * BlockSize.X,
    162288        (I div ItemsCount.X) * BlockSize.Y, BlockSize.X - 1, BlockSize.Y - 1);
    163       BlockState := FBlocks[I];
     289      BlockState := GetCombinedSectors(I * SectorPerBlock, (I + 1) * SectorPerBlock);
    164290      if BlockState = bsOk then Canvas.Brush.Color := clGreen
    165291        else if BlockState = bsDamaged then Canvas.Brush.Color := clRed
     
    172298
    173299procedure TBlockMap.Clear;
    174 var
    175   I: Integer;
    176 begin
    177   for I := 0 to SectorCount - 1 do
    178     FSectors[I] := bsNone;
     300begin
     301  FChanges.Clear;
     302  if FSectorCount > 0 then begin
     303    FChanges.Add(TChange.Create);
     304    TChange(FChanges[0]).Index := 0;
     305    TChange(FChanges[0]).Value := bsNone;
     306  end;
    179307end;
    180308
    181309constructor TBlockMap.Create;
    182310begin
     311  FChanges := TObjectList.Create;
    183312  FBlockSize := Point(12, 12);
    184313  SectorPerBlock := 1;
     314  FSectorCount := 0;
     315end;
     316
     317destructor TBlockMap.Destroy;
     318begin
     319  FreeAndNil(FChanges);
     320  inherited Destroy;
    185321end;
    186322
  • trunk/UDriveScan.pas

    r5 r6  
    5454implementation
    5555
     56resourcestring
     57  SUnknownRunMode = 'Unknown run mode';
     58
    5659
    5760{ TScanThread }
     
    8285procedure TDriveScan.Start;
    8386begin
    84   ScanThread := TScanThread.Create(True);
    85   ScanThread.Scan := Self;
    86   ScanThread.Start;
     87  if Terminated then begin
     88    Terminated := False;
     89    ScanThread := TScanThread.Create(True);
     90    ScanThread.Scan := Self;
     91    ScanThread.Start;
     92  end;
    8793end;
    8894
     
    120126        if Mode = rmRead then RealSize := F.Read(Buffer[0], SectorSize)
    121127          else if Mode = rmWrite then RealSize := F.Write(Buffer[0], SectorSize)
    122           else raise Exception.Create('Unknwon run mode');
     128          else raise Exception.Create(SUnknownRunMode);
    123129      end;
    124130    finally
     
    144150procedure TDriveScan.Stop;
    145151begin
    146   Terminated := True;
    147   ScanThread.Terminate;
    148   ScanThread.WaitFor;
    149   FreeAndNil(ScanThread);
     152  if not Terminated then begin
     153    Terminated := True;
     154    ScanThread.Terminate;
     155    ScanThread.WaitFor;
     156    FreeAndNil(ScanThread);
     157  end;
    150158end;
    151159
     
    160168destructor TDriveScan.Destroy;
    161169begin
     170  Stop;
    162171  FreeAndNil(BlockMap);
    163172  FreeAndNil(Lock);
  • trunk/UFormMain.pas

    r5 r6  
    120120  DriveScan.Drive := EditDrive.Text;
    121121  DriveScan.SectorSize := Project.SectorSize;
    122   DriveScan.Terminated := False;
    123122  DriveScan.Mode := TRunMode(ComboBoxRunMode.ItemIndex);
    124123  DriveScan.WritePattern := StrToInt(EditPattern.Text);
     
    145144procedure TFormMain.FormClose(Sender: TObject; var CloseAction: TCloseAction);
    146145begin
    147   DriveScan.Terminated := True;
    148146end;
    149147
    150148procedure TFormMain.FormDestroy(Sender: TObject);
    151149begin
     150  Timer1.Enabled := False;
    152151  FreeAndNil(Project);
    153152  FreeAndNil(DriveScan);
     
    208207procedure TFormMain.UpdateInterface;
    209208begin
    210   AScanStart.Enabled := DriveScan.Terminated = True;
    211   AScanStop.Enabled := DriveScan.Terminated = False;
     209  if Assigned(DriveScan) then begin
     210    AScanStart.Enabled := DriveScan.Terminated = True;
     211    AScanStop.Enabled := DriveScan.Terminated = False;
     212  end;
    212213end;
    213214
Note: See TracChangeset for help on using the changeset viewer.