difilecache.pas

来自「DELPHI 访问SQLITE3 数据库的VCL控件」· PAS 代码 · 共 162 行

PAS
162
字号
unit DIFileCache;

interface

uses
  Classes, DISQLite3Cache;

type
  TDIFileBlock = record
    Len: Integer; // Count of data bytes in block.
    Data: record end;
  end;
  PDIFileBlock = ^TDIFileBlock;

  //------------------------------------------------------------------------------
  // TDIFileCache class
  //------------------------------------------------------------------------------

  { Provides buffered access to blocks of a file. }
  TDIFileCache = class
  private
    FBlockSize: Integer;
    FCache: TDISQLite3Cache;
    FFileName: AnsiString;
    FFileStream: TFileStream;
    FUpdateCount: Integer;
  public
    constructor Create(const AFileName: AnsiString; const ABlockSize: Integer);
    { Reads blocks from file and adds them to the internal cache. Returns a
      pointer to the last block added to the cache. }
    function AddBlocksToCache(ABlockIdx, ABlockCount: Integer): PDIFileBlock;
    procedure BeginUpdate;
    procedure EndUpdate;
    { Reads a block from disk into the cache and returns a pointer to
      the cached block. }
    function GetBlock(const ABlockIdx: Integer): PDIFileBlock;
    procedure Invalidate;

    property BlockSize: Integer read FBlockSize;
  end;

function BlockToHex(const ABlock: PDIFileBlock): AnsiString;

function BlockToText(const ABlock: PDIFileBlock): AnsiString;

implementation

uses
  SysUtils;

//------------------------------------------------------------------------------
// TDIFileCache class
//------------------------------------------------------------------------------

constructor TDIFileCache.Create(const AFileName: AnsiString; const ABlockSize: Integer);
begin
  FFileName := AFileName;
  FBlockSize := ABlockSize;
  FCache := TDISQLite3Cache.Create(SizeOf(TDIFileBlock) + ABlockSize);
  FCache.MaxCount := 396;
end;

//------------------------------------------------------------------------------

function TDIFileCache.AddBlocksToCache(ABlockIdx, ABlockCount: Integer): PDIFileBlock;
var
  Pos: Int64;
begin
  Result := nil;
  BeginUpdate;
  try
    Pos := ABlockIdx * FBlockSize;
    if FFileStream.Seek(Pos, soFromBeginning) = Pos then
      repeat
        Result := FCache.GetItem(ABlockIdx);
        if not Assigned(Result) then
          Result := FCache.AddItem(ABlockIdx);
        Result.Len := FFileStream.Read(Result^.Data, FBlockSize);
        Inc(ABlockIdx);
        Dec(ABlockCount);
      until (Result.Len < FBlockSize) or (ABlockCount = 0);
  finally
    EndUpdate;
  end;
end;

//------------------------------------------------------------------------------

procedure TDIFileCache.BeginUpdate;
begin
  if FUpdateCount = 0 then
    FFileStream := TFileStream.Create(FFileName, fmOpenRead or fmShareDenyNone);
  Inc(FUpdateCount);
end;

//------------------------------------------------------------------------------

procedure TDIFileCache.EndUpdate;
begin
  if FUpdateCount > 0 then
    begin
      Dec(FUpdateCount);
      if FUpdateCount = 0 then
        FFileStream.Free;
    end;
end;

//------------------------------------------------------------------------------

function TDIFileCache.GetBlock(const ABlockIdx: Integer): PDIFileBlock;
begin
  Result := FCache.GetItem(ABlockIdx);
  if not Assigned(Result) then
    Result := AddBlocksToCache(ABlockIdx, 1);
end;

//------------------------------------------------------------------------------

procedure TDIFileCache.Invalidate;
begin
  FCache.Invalidate;
end;

//------------------------------------------------------------------------------
// Functions
//------------------------------------------------------------------------------

function BlockToHex(const ABlock: PDIFileBlock): AnsiString;
var
  l: Integer;
  p: ^Byte;
begin
  Result := '';
  p := @ABlock^.Data;
  l := ABlock^.Len;
  while l > 0 do
    begin
      Result := Result + IntToHex(p^, 2) + ' ';
      Inc(p); Dec(l);
    end;
end;

//------------------------------------------------------------------------------

function BlockToText(const ABlock: PDIFileBlock): AnsiString;
var
  l: Integer;
  p: PAnsiChar;
begin
  l := ABlock.Len;
  SetString(Result, PAnsiChar(@ABlock^.Data), l);
  p := Pointer(Result);
  while l > 0 do
    begin
      if p^ < #32 then p^ := '.';
      Inc(p); Dec(l);
    end;
end;

end.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?