---------- TNkBufferedStream のソースコード ----------
{$IFDEF CONDITIONALEXPRESSIONS}
{$DEFINE DELPHI6ORLATER}
{$ENDIF}
// 2002/7/11 追加
{$RANGECHECKS OFF} // 2003/4/17 Seek の Offset の符号が soFromEnd時 逆に処理されていたので
// 修正しました。
unit NkStream;
interface
uses
SysUtils, Classes;
type
ENkSTreamError = class(Exception);
TNkStreamMode = (nkSmNoBuffering, nkSmRead, nkSmWrite);
TNkBufferedStream = class(TStream)
private
FMode: TNkStreamMode;
FBuffer: array of Byte; // バッファ
FBufferSize: Integer; // バッファサイズ
FDataSize: Integer; // バッファ内のデータサイズ
FDataIndex: Integer; // 次に読むデータのインデックス
FStream: TStream; // バッファリング対象のストリーム
procedure FillBuffer; // バッファに先読みする
// バッファ状態を考慮した正しい位置を返す。
function GetCorrectPosition: Int64;
procedure EmptyBuffer; // バッファをクリアし非バッファリング状態にする
protected
// ストリームのリサイズ 32bit版
procedure SetSize(NewSize: Longint); override;
{$IFDEF DELPHI6ORLATER}
// ストリームのリサイズ 64bit版
procedure SetSize(const NewSize: Int64); override;
{$ENDIF}
public
constructor Create(Stream: TStream; BufferSize: Integer);
destructor Destroy; override;
function Read(var Buffer; Count: Longint): Longint; override;
function Write(const Buffer; Count: Longint): Longint; override;
function FlushBuffer: Integer;
function Seek(Offset: Longint; Origin: Word): Longint;
overload; override;
{$IFDEF DELPHI6ORLATER}
function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
overload; override;
{$ENDIF}
end;
implementation
procedure RaiseError(s: string);
begin raise ENkStreamError.Create(s); end;
{ TNkBufferedStream }
// コンストラクタ
constructor TNkBufferedStream.Create(Stream: TStream; BufferSize: Integer);
begin
inherited Create;
if (BufferSize < 1) then
RaiseError('TNkBufferedStream.Create: バッファサイズが不正です');
FStream := Stream; // ストリームを格納
// バッファを初期化
FBufferSize := BufferSize;
SetLength(FBuffer, FBufferSize);
FDataSize := 0;
FDataIndex := 0;
FMode := nkSmNoBuffering; //バッファリングなし状態
end;
// デストラクタ
destructor TNkBufferedStream.Destroy;
begin
FlushBuffer; // 書き込みキャッシュにデータがあれば書き込む
inherited;
end;
// バッファを空にし nkSmNoBuffering モードにする
procedure TNkBufferedStream.EmptyBuffer;
begin
case FMode of
nkSmRead: // 先読みを取り消す
begin
FMode := nkSmNoBuffering;
FStream.Position := FStream.Position - (FDataSize - FDataIndex);
FDataSize := 0;
FDataIndex := 0;
end;
nkSmWrite:
begin
FlushBuffer; // データバッファを吐く
FDataSize := 0;
FDataIndex := 0;
end;
end;
FMode := nkSmNoBuffering; // バッファリングが無い状態へ
end;
// バッファに先読みを行う
procedure TNkBufferedStream.FillBuffer;
begin
Assert(FMode = nkSmRead);
FDataSize := FStream.Read(FBuffer[0], FBufferSize);
FDataIndex := 0;
end;
// バッファにたまったデータを吐き出す。書き込んだバイト数を返す。
function TNkBufferedStream.FlushBuffer: Integer;
begin
Result := 0;
if FMode = nkSmWrite then // 書き込みモード時のみフラッシュを行う
begin
Result := FStream.Write(FBuffer[0], FDataIndex);
FDataIndex := 0;
end;
end;
// 先読み、ライトバッファの格納データ長を考慮した正しい Position を得る
function TNkBufferedStream.GetCorrectPosition: Int64;
begin
Result := 0; // ダミー
case FMode of
nkSmNoBuffering: Result := FStream.Position;
nkSmRead: Result := FStream.Position - (FDataSize - FDataIndex);
nkSmWrite: Result := FStream.Position + FDataIndex;
end;
end;
// 読み込み
function TNkBufferedStream.Read(var Buffer; Count: Integer): Longint;
var
Dest: PChar; // 書き込み先
DataSizeInBuffer: Integer; // 先読みバッファ中のデータ量
ReadLength: Integer; // 読み込む長さ
begin
if Count < 0 then
RaiseError('TNkBufferedStream.Read: Count が負です');
// 読み込みモードで無いならば、
// バッファを空にしてモードを切り替えて先読み
if FMode <> nkSmRead then
begin
EmptyBuffer;
FMode := nkSmRead;
FillBuffer;
end;
Result := 0; // 累積読み込み量 クリア
Dest := @Buffer; // Dest を書き込み先バッファの先頭へ
while True do // 読み込みループ
begin
if FDataSize = 0 then Exit; // 読むデータがなくなれば終了
// 読み込みサイズを決める
DataSizeInBuffer := FDataSize - FDataIndex;
if DataSizeInBuffer < Count then ReadLength := DataSizeInBuffer
else ReadLength := Count;
// 読み込む
System.Move(FBuffer[FDataIndex], Dest^, ReadLength);
Inc(Result, ReadLength);
Dec(Count, ReadLength);
Inc(Dest, ReadLength);
Inc(FDataIndex, ReadLength);
if Count = 0 then Exit; // 全て読んだら終了
FillBuffer; // バッファが空なので先読みをする
end;
end;
// 32bit版 Seek
function TNkBufferedStream.Seek(Offset: Integer; Origin: Word): Longint;
var
RequestedPosition: Int64;
begin
RequestedPosition := 0; // ダミー
// 要求された位置を計算する
case Origin of
soFromBeginning: RequestedPosition := Offset;
soFromCurrent: RequestedPosition := GetCorrectPosition + Offset;
soFromEnd:
begin
EmptyBuffer;
RequestedPosition := FStream.Size + Offset;
end;
end;
if GetCorrectPosition <> RequestedPosition then // 位置に変化がある
begin
EmptyBuffer; // バッファを空にする
FStream.Position := RequestedPosition; // 格納しているストリームに
// 位置を設定
end;
Result := GetCorrectPosition;
end;
{$IFDEF DELPHI6ORLATER}
// 64bit版 Seek
function TNkBufferedStream.Seek(const Offset: Int64;
Origin: TSeekOrigin): Int64;
var
RequestedPosition: Int64;
begin
RequestedPosition := 0; // ダミー
// 要求された位置を計算する
case Origin of
soBeginning: RequestedPosition := Offset;
soCurrent: RequestedPosition := GetCorrectPosition + Offset;
soEnd:
begin
EmptyBuffer;
RequestedPosition := FStream.Size - Offset;
end;
end;
if GetCorrectPosition <> RequestedPosition then // 位置に変化がある
begin
EmptyBuffer; // バッファを空にする
FStream.Position := RequestedPosition; // 格納しているストリームに
// 位置を設定
end;
Result := GetCorrectPosition;
end;
{$ENDIF}
// サイズへ変更
procedure TNkBufferedStream.SetSize(NewSize: Longint);
begin
EmptyBuffer; // バッファを空にする
FStream.Size := NewSize; // バッファリング対象のStream をリサイズ
end;
{$IFDEF DELPHI6ORLATER}
procedure TNkBufferedStream.SetSize(const NewSize: Int64);
begin
EmptyBuffer; // バッファを空にする
FStream.Size := NewSize; // バッファリング対象のStream をリサイズ
end;
{$ENDIF}
// 書き込み
// 制限事項: Write が返す書き込みバイト数はバッファに書き込まれたものを
// 含むので、実際にストリームに書き込まれたものでは無い。
// フラッシュに失敗した場合不一致が生じる。
function TNkBufferedStream.Write(const Buffer; Count: Integer): Longint;
var
RestOfBuffer: Integer; // ライトバッファの残量
Source: PChar; // 読み込み元
WriteLength: Integer; // 書き込み量
begin
if Count < 0 then
RaiseError('TNkBufferedStream.Write: Count が負です');
// 書き込みモードで無いならバッファをクリアして書き込みモードに
if FMode <> nkSmWrite then
begin
EmptyBuffer;
FMode := nkSmWrite;
end;
Result := 0;
Source := @Buffer;
while True do
begin
// 書き込み量を決める
RestOfBuffer := FBufferSize - FDataIndex;
if RestOfBuffer < Count then WriteLength := RestOfBuffer
else WriteLength := Count;
// 書き込む
System.Move(Source^, FBuffer[FDataIndex], WriteLength);
Inc(FDataIndex, WriteLength);
Inc(Result, WriteLength);
Dec(Count, WriteLength);
Inc(Source, WriteLength);
if Count = 0 then Exit; // 書き終わったら終了
// 全部かけない場合も終了(Disk Full?)
if FlushBuffer < FBufferSize then Exit;
end;
end;
end.
---------- TNkBufferedStream のソースコード おわり
----------
|