Article

From:
To:
Samuel Aeschbacher
Subject:
Re: Out of memory Exception when using large StringStream.DataString inD2009
Newsgroup:
embarcadero.public.delphi.rtl

Re: Out of memory Exception when using large StringStream.DataString inD2009

<Samuel Aeschbacher> wrote in message news:✉forums.embarcadero.com...

> I use TFileStream to read the data, then copying the filestream to a 
> stringstream
> for better string handling. If I seek a substring in 
> Stringstream.DataString, I get an
> "Out-of-memory-Exception" if the size of the Stream exceeds 200 MB (it 
> worked
> well with a size <= 136 MB and crashed with a size >= 280 MB).

In pre-D2009 versions, TStringStream derived directly from TStream, used an 
AnsiString as its data storage (hense its name), and the DataString property 
provided direct access to that data.

In D2009 and later, TStringStream now derives from TByteStream, thus uses a TBytes for its data storage, and the DataString property no longer provides direct access to the data. It returns a temporary UnicodeString that is converted from the raw TBytes data using the new Encoding property.
The code you are using is not taking into account TStringStream's re-designed functionality for Unicode in D2009 and later. If you want to maintain the original Ansi-based logic, then something like this:
{code:delphi} function TDLHI_Main.ImportKompendium(var MsgList: TListBox; var PrgrssBar: TProgressBar; FileName : string): string; var   pmem, p: PAnsiChar;   i : integer; begin   Result := '';   mstrm := TMemoryStream.Create;   try     mstrm.LoadFromFile(FileName);     pmem := PAnsiChar(mstrm.Memory);     p := StrPos(pmem, '<OK_ERROR>');     if p <> nil then       i := Integer(p - pmem) + 1     else       i := 0;     ...   finally     mstrm.Free;   end; end; {code}
Alternatively, implement an AnsiString-based TStream class so you can preserve your original code, eg:
{code} type   TAnsiStringStream = class(TStream)   private     FDataString: AnsiString;     FPosition: Integer;   protected     procedure SetSize(NewSize: Longint); override;   public     constructor Create(const AString: AnsiString);     function Read(var Buffer; Count: Longint): Longint; override;     function ReadString(Count: Longint): AnsiString;     function Seek(Offset: Longint; Origin: Word): Longint; override;     function Write(const Buffer; Count: Longint): Longint; override;     procedure WriteString(const AString: AnsiString);     property DataString: AnsiString read FDataString;   end;
constructor TAnsiStringStream.Create(const AString: AnsiString); begin   inherited Create;   FDataString := AString; end;
function TAnsiStringStream.Read(var Buffer; Count: Longint): Longint; begin   Result := Length(FDataString) - FPosition;   if Result > Count then Result := Count;   Move(PAnsiChar(@FDataString[FPosition + SizeOf(AnsiChar)])^, Buffer, Result * SizeOf(AnsiChar));   Inc(FPosition, Result); end;
function TAnsiStringStream.Write(const Buffer; Count: Longint): Longint; begin   Result := Count;   SetLength(FDataString, (FPosition + Result));   Move(Buffer, PAnsiChar(@FDataString[FPosition + SizeOf(AnsiChar)])^, Result * SizeOf(AnsiChar));   Inc(FPosition, Result); end;
function TAnsiStringStream.Seek(Offset: Longint; Origin: Word): Longint; begin   case Origin of     soFromBeginning: FPosition := Offset;     soFromCurrent: FPosition := FPosition + Offset;     soFromEnd: FPosition := Length(FDataString) - Offset;   end;   if FPosition > Length(FDataString) then     FPosition := Length(FDataString)   else if FPosition < 0 then FPosition := 0;   Result := FPosition; end;
function TAnsiStringStream.ReadString(Count: Longint): string; var   Len: Integer; begin   Len := Length(FDataString) - FPosition;   if Len > Count then Len := Count;   SetString(Result, PChar(@FDataString[FPosition + SizeOf(Char)]), Len);   Inc(FPosition, Len); end;
procedure TAnsiStringStream.WriteString(const AString: AnsiString); begin   Write(PAnsiChar(AString)^, Length(AString) * SizeOf(AnsiChar)); end;
procedure TAnsiStringStream.SetSize(NewSize: Longint); begin   SetLength(FDataString, NewSize);   if FPosition > NewSize then FPosition := NewSize; end;
function TDLHI_Main.ImportKompendium(var MsgList: TListBox; var PrgrssBar: TProgressBar; FileName : string): string; var   i : integer; begin   Result := '';   fs := TFileStream.Create(FileName, fmOpenRead + fmShareDenyWrite);   try     strstrm := TAnsiStringStream.Create('');     strstrm.CopyFrom(fs, fs.Size);   finally     fs.Free;   end;   i := AnsiStrings.AnsiPos('<OK_ERROR>', strstrm.DataString);   ... end; {code}
> It's interesting, to get the exception on the "pos"-Statement and not as I
> expected on the copying of the stream.

If the stream contains 200MB of raw bytes, then the DataString property will 
try to allocate a UnicodeString that uses (slightly more than) 400B of new 
memory, on top of the 200MB that the stream is already holding on to.  It is 
unlikely that the memory manager will be able to find an available 
contigious block of memory that large.

-- Remy Lebeau (TeamB)
FYI: Phrase searches are enclosed in either single or double quotes
 
 
Originally created by
Tamarack Associates
Thu, 21 Nov 2024 20:19:05 UTC
Copyright © 2009-2024
HREF Tools Corp.