unit dstreammem;

interface

uses
  dtypes,
  dmath,
  dstream;

type
PStreamMem = ^TStreamMem;
TStreamMem = object(TStream)
protected
  function GetMemEnd: PByte; inline;
  function GetMemPos: PByte; inline;
public
  constructor Init(Buffer: Pointer; Size: Int64; AutoFree: Boolean = False); overload;
  constructor Init(Size: Int64); overload;
  // If it possible, it does not copy data
  constructor Init(Data: TData); overload;
  function Create(Buffer: Pointer; Size: Int64; AutoFree: Boolean = False): PStreamMem; static; overload;
  function Create(Size: Int64): PStreamMem; static; overload;
  destructor Done; virtual;
  procedure InitProc(Buffer: Pointer; Size: Int64; AutoFree: Boolean = False);
  property MemStart: PByte read FBuf;
  property MemEnd: PByte read GetMemEnd;
  property MemPos: PByte read GetMemPos;
protected
  function DoRead(P: Pointer; Size: LongInt): LongInt; virtual;
  function DoWrite(P: Pointer; Size: LongInt): LongInt; virtual;
  function DoSeek(Offset: TInt64; Origin: TSeekOrigin): TInt64; virtual;
  function DoGetPosition: Int64; virtual;
  function DoEof: Boolean; virtual;
  function DoGetSize: Int64; virtual;
end;

function Data(P: Pointer; Size: Int64): TData; inline; overload;
function DataFromString(const S: AnsiString): TData; inline; overload;

implementation

function TStreamMem.GetMemEnd: PByte;
begin
  Result := FBuf + FBufSize;
end;

function TStreamMem.GetMemPos: PByte;
begin
  Result := FBuf + FBufPos;
end;

constructor TStreamMem.Init(Buffer: Pointer; Size: Int64; AutoFree: Boolean = False);
begin
  inherited Init;
  InitProc(Buffer, Size, AutoFree);
  SetReadable;
  SetWritable;
end;

constructor TStreamMem.Init(Size: Int64);
begin
  inherited Init(Size);
  SetReadable;
  SetWritable;
end;

constructor TStreamMem.Init(Data: TData);
var
  P: Pointer;
  S: SizeUInt;
begin
  Data.Stream.ReadToMemory(P, S);
  FBufAllocedSize := S;
  FBufRequiredSize := S;
  FBufSize := S;
  FBufPos := 0;
  FBuf := P;
  FBufOwner := True;
  Data.Done;
end;

function TStreamMem.Create(Buffer: Pointer; Size: Int64; AutoFree: Boolean = False): PStreamMem;
begin
  Result := New(PStreamMem, Init(Buffer, Size, AutoFree));
end;

function TStreamMem.Create(Size: Int64): PStreamMem;
begin
  Result := New(PStreamMem, Init(Size));
end;

destructor TStreamMem.Done;
begin
  inherited Done;
end;

procedure TStreamMem.InitProc(Buffer: Pointer; Size: Int64; AutoFree: Boolean = False);
begin
  FBuf := Buffer;
  FBufSize := Size;
  FBufAllocedSize := Size;
  FBufRequiredSize := Size;
  FBufOwner := AutoFree;
  FBufPos := 0;
end;

function TStreamMem.DoRead(P: Pointer; Size: LongInt): LongInt;
begin
  Result := 0;
end;

function TStreamMem.DoWrite(P: Pointer; Size: LongInt): LongInt;
begin
  Result := 0;
end;

function TStreamMem.DoSeek(Offset: TInt64; Origin: TSeekOrigin): TInt64;
begin
  //case Origin of
  //  SO_BEGINNING: FPosition := Offset;
  //  SO_CURRENT:   FPosition := FPosition + Offset;
  //  SO_END:       FPosition := FSize - Offset;
  //end;
  //Result := FPosition; // FIXME is it correct behaviour?
  // TODO how to signalize DoSeek is not implemented?
  Result := 0;
end;

function TStreamMem.DoGetPosition: Int64;
begin
  Result := 0;
end;

function TStreamMem.DoEof: Boolean;
begin
  Result := True;
end;

function TStreamMem.DoGetSize: Int64;
begin
  Result := 0;
end;

function Data(P: Pointer; Size: Int64): TData;
begin
  Result.Init(TStreamMem.Create(P, Size, False), True);
end;

function DataFromString(const S: AnsiString): TData;
begin
  Result := Data(@S[1], Length(S));
end;

end.
