In Delphi, including the latest versions (I can only check up to 10.1 Berlin, although there are negative reviews about 10.2 Tokyo), the TMemoryStream class cannot allocate more than 2 GB for data even when compiled in 64-bit mode. Attempts to write large volumes to a stream result in an error "Out of memory while expanding memory stream" .

Is it possible to solve this problem without creating your own class that implements the stream in memory?

    2 answers 2

    Let's take a look at the definition of the TMemoryStream class in the System.Classes.pas file:

      TMemoryStream = class(TCustomMemoryStream) private FCapacity: Longint; procedure SetCapacity(NewCapacity: Longint); protected function Realloc(var NewCapacity: Longint): Pointer; virtual; property Capacity: Longint read FCapacity write SetCapacity; public destructor Destroy; override; procedure Clear; procedure LoadFromStream(Stream: TStream); procedure LoadFromFile(const FileName: string); procedure SetSize(const NewSize: Int64); override; procedure SetSize(NewSize: Longint); override; function Write(const Buffer; Count: Longint): Longint; override; function Write(const Buffer: TBytes; Offset, Count: Longint): Longint; override; end; 

    We note right away that variables of the longint type are used for memory addressing, which naturally explains the impossibility of using more than 2 GB. Moreover, if you look at the Read methods (there are two of them) of the immediate ancestor of TMemoryStream - TCustomMemoryStream :

     function TCustomMemoryStream.Read(var Buffer; Count: Longint): Longint; begin if (FPosition >= 0) and (Count >= 0) then begin Result := FSize - FPosition; if Result > 0 then begin if Result > Count then Result := Count; Move((PByte(FMemory) + FPosition)^, Buffer, Result); Inc(FPosition, Result); Exit; end; end; Result := 0; end; function TCustomMemoryStream.Read(Buffer: TBytes; Offset, Count: Longint): Longint; begin if (FPosition >= 0) and (Count >= 0) then begin Result := FSize - FPosition; if Result > 0 then begin if Result > Count then Result := Count; Move((PByte(FMemory) + FPosition)^, Buffer[Offset], Result); Inc(FPosition, Result); Exit; end; end; Result := 0; end; 

    , then we note that the result may be counted incorrectly.

    Therefore, the solution was: replace the type of variables inside TMemoryStream with NativeInt and Int64, including local variables, and rewrite the TCustomMemoryStream .Read methods, for example, to:

     function TCustomMemoryStream.Read(var Buffer; Count: Longint): Longint; var diff: Int64; begin if (FPosition >= 0) and (Count >= 0) then begin diff := FSize - FPosition; if diff > 0 then begin if diff > Count then Result := Count else Result := diff; Move((PByte(FMemory) + FPosition)^, Buffer, Result); Inc(FPosition, Result); Exit; end; end; Result := 0; end; function TCustomMemoryStream.Read(Buffer: TBytes; Offset, Count: Longint): Longint; var diff: Int64; begin if (FPosition >= 0) and (Count >= 0) then begin diff := FSize - FPosition; if diff > 0 then begin if diff > Count then Result := Count else Result := diff; Move((PByte(FMemory) + FPosition)^, Buffer[Offset], Result); Inc(FPosition, Result); Exit; end; end; Result := 0; end; 

    This allows you to work with memory sizes of more than 2 GB (the test example quietly processed a 5-gigabyte file, completely copying it into the stream.

    For those who want to make changes in their System.Classes, I remind you that you must first save a backup copy of this file - just in case :)

    Update 1

    For those who for some reason do not want to follow the options suggested above, I can offer the TSegmentedMemoryStream class - it is available for download and works (tested!).

    • Yes, it turns out they have not updated this class since Delphi 6: Bug in TMemoryStream? - zed
    • 2
      Wouldn't it be better to write your successor from TCustomMemoryStream instead of TCustomMemoryStream system libraries? Such code will not compile correctly on the machine of another developer (without preliminary dances with a tambourine). Such code will not compile correctly on another version of Delphi. - kot-da-vinci
    • @ kot-da-vinci 1. TCustomMemoryStream still had to be edited, except that in the successor to replace its methods. 2. I have installed components that use TMemoryStream, but alas, they have no source code. 3. Regarding another version of Delphi - incorrectly, I have already received "thanks" from the owners of Seattle, XE8, XE6 4. I am not a developer, but an application worker, but for us such trifles as a transfer, in general, are of little value - Viktor Tomilov
    • one
      @ kot-da-vinci Oh, yes, and even 5. The reasons still had to be shown. But now anyone can realize what you offer, based on the knowledge of errors. After all, everyone does as he likes. - Viktor Tomilov
    • one
      @ViktorTomilov Fixed (Delphi Tokyo), data recorded !!!! - Alexey Kozlov

    In the current version of Delphi (10.2.2), the class is declared as follows:

     TMemoryStream = class(TCustomMemoryStream) private FCapacity: NativeInt; protected procedure SetCapacity(NewCapacity: NativeInt); virtual; function Realloc(var NewCapacity: Longint): Pointer; virtual; property Capacity: NativeInt read FCapacity write SetCapacity; public destructor Destroy; override; procedure Clear; procedure LoadFromStream(Stream: TStream); procedure LoadFromFile(const FileName: string); procedure SetSize(const NewSize: Int64); override; procedure SetSize(NewSize: Longint); override; function Write(const Buffer; Count: Longint): Longint; override; function Write(const Buffer: TBytes; Offset, Count: Longint): Longint; override; end; 

    It can be seen that they somehow tried to correct the problem, but it has not yet been completely fixed (for example, the Realloc function is still tied to LongInt ).

    In the qualification center there are several tickets regarding TMemoryStream:

    So there is a hope that they will finally make 64-bit support in TMemoryStream .

    • What about TCustomMemoryStream's Read methods in 10.2.2? I, alas, have no opportunity to work with 10.2.X - Viktor Tomilov
    • @ViktorTomilov There is still LongInt . - zed
    • Did they just notice that their class was defective? Apparently, because bioinformatics to Delphi pulled, and we have huge data :) By the way, how to get access to the qualification center? - Alexey Kozlov
    • one
      @AlexeyKozlov You need to register here: members.embarcadero.com - zed