вопрос по TMemoryStream
От: Pavel Dvorkin Россия  
Дата: 25.03.12 10:12
Оценка:
Пришлось мне тут с одной програмой на Delphi разбираться.


var Stream:TMemoryStream;
begin
  Stream := TMemoryStream.Create(  );
  (Stream as TMemoryStream).LoadFromFile( FileName);


То, что этот код вызывает ошибку для 3Гб файла в 32-битном режиме , понятно, но почему та же ошибка возникает в x64 ? Можно ли с этим бороться ?

Саму идею загружать 3Гб файл в ОП целиком прошу не обсуждать — не я такое писал.
With best regards
Pavel Dvorkin
Re: вопрос по TMemoryStream
От: hattab  
Дата: 25.03.12 10:53
Оценка: 14 (1) +1
Здравствуйте, Pavel Dvorkin, Вы писали:

TMemoryStream, судя по коду, не предназначен для работы с кусками размером более 2Gb (для указания размеров везде используется тип Longint). Можно сделать своего потомка от TCustomMemoryStream, он поддерживает максимальные размеры целевой платформы.
avalon 1.0rc3 build 428, zlib 1.2.3
Re[2]: вопрос по TMemoryStream
От: Pavel Dvorkin Россия  
Дата: 25.03.12 13:53
Оценка:
Здравствуйте, hattab, Вы писали:

H>Здравствуйте, Pavel Dvorkin, Вы писали:


H>TMemoryStream, судя по коду, не предназначен для работы с кусками размером более 2Gb (для указания размеров везде используется тип Longint). Можно сделать своего потомка от TCustomMemoryStream, он поддерживает максимальные размеры целевой платформы.


Спасибо. Посмотрел исходники, и вот что не понимаю

1.

  TCustomMemoryStream = class(TStream)
  private
    FMemory: Pointer;
    FSize, FPosition: NativeInt;
//...
procedure TMemoryStream.SetSize(NewSize: Longint);
var
  OldPosition: Longint;
begin
  OldPosition := FPosition;
  SetCapacity(NewSize);
  FSize := NewSize;
  if OldPosition > NewSize then Seek(0, soFromEnd);
end;


Вроде бы FSize и FPosition объявлены в TCustomMemoryStream как private, а между тем к ним идет обращение из метода наследника TMemoryStream.SetSize. Как такое возможно ? Я попробовал вставить код класса TMemoryStream в свой TLargeMemoryStream, естественно, не компилируется. А как тогда System.Classes компилировалось ?

2.

Судя по

  TCustomMemoryStream = class(TStream)
    function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
    function Read(var Buffer; Count: Longint): Longint; override;


даже он не поддерживает чтения больше 2GB. То есть, если я правильно понимаю, смещения в файле могут быть 64-битные, а вот читать можно только не более 2GB.

Более того, это даже TStream не поддерживает


 TStream = class(TObject)
    function Read(var Buffer; Count: Longint): Longint; virtual; abstract;


Что же теперь, писать с нуля ?
With best regards
Pavel Dvorkin
Re[3]: вопрос по TMemoryStream
От: BlackEric http://black-eric.lj.ru
Дата: 25.03.12 14:27
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Здравствуйте, hattab, Вы писали:


H>>Здравствуйте, Pavel Dvorkin, Вы писали:


Читайте блоками и обрабатывайте по частям.
https://github.com/BlackEric001
Re[4]: вопрос по TMemoryStream
От: Pavel Dvorkin Россия  
Дата: 25.03.12 14:32
Оценка:
Здравствуйте, BlackEric, Вы писали:

BE>Читайте блоками и обрабатывайте по частям.


До этого я и сам мог бы догадаться, если бы это проходило. Увы, придется переделывать всю программу.
With best regards
Pavel Dvorkin
Re[3]: вопрос по TMemoryStream
От: Jolly Roger  
Дата: 25.03.12 14:43
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

H>>Здравствуйте, Pavel Dvorkin, Вы писали:


PD>Вроде бы FSize и FPosition объявлены в TCustomMemoryStream как private, а между тем к ним идет обращение из метода наследника TMemoryStream.SetSize. Как такое возможно ? Я попробовал вставить код класса TMemoryStream в свой TLargeMemoryStream, естественно, не компилируется. А как тогда System.Classes компилировалось ?


А в дельфях хитрющая инкапсуляция. у-у-у! Все классы, объявленные в одном модуле (unit) автоматически как-бы дружественные, и могут свободно друг другу в приват лазить

PD>Что же теперь, писать с нуля ?


Я дельфей после 7-ки не видел, может в новых версиях, поддерживающих 64 бита, какой-нить новый стрим появился. Или переделайте на IStream.
"Нормальные герои всегда идут в обход!"
Re[4]: вопрос по TMemoryStream
От: Pavel Dvorkin Россия  
Дата: 25.03.12 14:51
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

JR>А в дельфях хитрющая инкапсуляция. у-у-у! Все классы, объявленные в одном модуле (unit) автоматически как-бы дружественные, и могут свободно друг другу в приват лазить


Спасибо!!!

PD>>Что же теперь, писать с нуля ?


JR> Я дельфей после 7-ки не видел, может в новых версиях, поддерживающих 64 бита, какой-нить новый стрим появился. Или переделайте на IStream.


Тогда уж проще memory mapped files напрямую через uses Windows.
With best regards
Pavel Dvorkin
Re[5]: вопрос по TMemoryStream
От: Jolly Roger  
Дата: 25.03.12 15:02
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Тогда уж проще memory mapped files напрямую через uses Windows.


Ну ежель мест, где используется, много, то может проще написать стрим, базирующийся на MMF? ИМХО, и править проще будет, и вероятность ошибок меньше.
"Нормальные герои всегда идут в обход!"
Re[6]: вопрос по TMemoryStream
От: Pavel Dvorkin Россия  
Дата: 25.03.12 15:49
Оценка:
Здравствуйте, Jolly Roger, Вы писали:


JR>Ну ежель мест, где используется, много, то может проще написать стрим, базирующийся на MMF? ИМХО, и править проще будет, и вероятность ошибок меньше.


Да нет, там просто от стрима берут пойнтер на его буфер, а дальше уже с ним вся работа. В общем, банальность.
With best regards
Pavel Dvorkin
Re[5]: вопрос по TMemoryStream
От: hattab  
Дата: 25.03.12 17:14
Оценка: 14 (1)
Здравствуйте, Pavel Dvorkin, Вы писали:

PD> BE>Читайте блоками и обрабатывайте по частям.


PD> До этого я и сам мог бы догадаться, если бы это проходило. Увы, придется переделывать всю программу.


Зачем переделывать? Ты же сам сказал, что от стрима там только указатель требуется. Делаешь простенькую функцию:
 Function GetFileData(Const AFileName : String) : TBytes;
 Var

  Index : NativeInt;
  Count : Integer;

 Begin

  With TFileStream.Create(AFileName, fmOpenRead Or fmShareDenyWrite) Do
   Try

    SetLength(Result, Size);

    Index := 0;

    Repeat

     Count := Read(Result[Index], Min(High(Longint), Length(Result) - Index - 1));
     Inc(Index, Count);

    Until Count < High(Longint);

   Finally

    Free;

   End;

 End;


...и задача решена. TBytes приводится к Pointer и всех делов
avalon 1.0rc3 build 428, zlib 1.2.3
Re: вопрос по TMemoryStream
От: david_yusupov Россия  
Дата: 26.03.12 04:04
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Пришлось мне тут с одной програмой на Delphi разбираться.



PD>
PD>var Stream:TMemoryStream;
PD>begin
PD>  Stream := TMemoryStream.Create(  );
PD>  (Stream as TMemoryStream).LoadFromFile( FileName);

PD>


PD>То, что этот код вызывает ошибку для 3Гб файла в 32-битном режиме , понятно, но почему та же ошибка возникает в x64 ? Можно ли с этим бороться ?


PD>Саму идею загружать 3Гб файл в ОП целиком прошу не обсуждать — не я такое писал.



Не знаю сколько у вас оперативной памяти
Так TMemoryStream – записывает в память
Рекомендую использовать TFileStream – записывает прямо в файлов
Re[6]: вопрос по TMemoryStream
От: Pavel Dvorkin Россия  
Дата: 26.03.12 16:01
Оценка:
Здравствуйте, hattab, Вы писали:

Увы...

Даже вот такое не проходит


   var Buffer : TBytes;


    SetLength(Buffer, 3000000000);

    Index := 3000000000 -1;

    Buffer[Index] := 0;


ERangeCheckError

А вот на такое


   var B : array [1..3000000000] of byte;



E2100 Data type too large exceeds 2 GB (Delphi)

Это уже форменное безобразие. В Win64!

В общем, впечатление такое, что средствами Delphi работать с массивами длиной больше 2 GB нельзя даже в x64. Хмм...
With best regards
Pavel Dvorkin
Re[7]: все же обманул
От: Pavel Dvorkin Россия  
Дата: 26.03.12 17:08
Оценка:

 Index : NativeInt;
  Count : Integer;
  Stream : TFileStream;
  Portion: Integer;
  Current : TBytes;

  var Rest : NativeInt;
 Begin
  Portion := 1000000000;
  Stream:=TFileStream.Create(FileName, fmOpenRead Or fmShareDenyWrite);
   Try

    SetLength(Buffer, Stream.Size);
    Current := Buffer;

    Repeat
    Rest := Length(Buffer) - Index - 1;
    Count := Stream.Read(Current[0], Min(Portion, Rest));
    Inc(NativeUint(Current),Count);
    Inc(Index, Count);

    Until Count < Portion;

   Finally

    Stream.Free;

   End;


И тем не менее — безобразие
With best regards
Pavel Dvorkin
Re[7]: вопрос по TMemoryStream
От: hattab  
Дата: 26.03.12 19:11
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD> Даже вот такое не проходит


PD>
PD>    var Buffer : TBytes;

PD>     SetLength(Buffer, 3000000000);

PD>     Index := 3000000000 -1;

PD>     Buffer[Index] := 0;

PD>


PD> ERangeCheckError


А Index, часом, не Integer?

Вот этот код 100% работающий:
program Project1;

{$APPTYPE CONSOLE}

Uses SysUtils;

Const

 Size3Gb = Int64(1024) * Int64(1024) * Int64(1024) * Int64(3);

Var

 Bytes : TBytes;

Begin

 SetLength(Bytes, Size3Gb);

 WriteLn(Length(Bytes));
 Bytes[Length(Bytes) - 1] := 255;
 WriteLn('Last byte has been modified!');
 ReadLn;

End.
avalon 1.0rc3 build 428, zlib 1.2.3
Re[8]: все же обманул
От: hattab  
Дата: 26.03.12 19:11
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>

PD>  Index : NativeInt;
PD>   Count : Integer;
PD>   Stream : TFileStream;
PD>   Portion: Integer;
PD>   Current : TBytes;

PD>   var Rest : NativeInt;
PD>  Begin
PD>   Portion := 1000000000;
PD>   Stream:=TFileStream.Create(FileName, fmOpenRead Or fmShareDenyWrite);
PD>    Try

PD>     SetLength(Buffer, Stream.Size);
PD>     Current := Buffer;

PD>     Repeat
PD>     Rest := Length(Buffer) - Index - 1;
PD>     Count := Stream.Read(Current[0], Min(Portion, Rest));
PD>     Inc(NativeUint(Current),Count);
PD>     Inc(Index, Count);

PD>     Until Count < Portion;

PD>    Finally

PD>     Stream.Free;

PD>    End;

PD>


PD> И тем не менее — безобразие


Inc(NativeUint(Current),Count);

Зачем инкаешь TBytes? Поломаешь поинтер будет зяка-зяка
avalon 1.0rc3 build 428, zlib 1.2.3
Re[9]: все же обманул
От: Pavel Dvorkin Россия  
Дата: 27.03.12 02:35
Оценка:
Здравствуйте, hattab, Вы писали:



H>Зачем инкаешь TBytes? Поломаешь поинтер будет зяка-зяка


А с чего это я его поломаю ? Прибавление числа к указателю его сдвигает, вот и все.
With best regards
Pavel Dvorkin
Re[8]: вопрос по TMemoryStream
От: Pavel Dvorkin Россия  
Дата: 27.03.12 04:21
Оценка:
Здравствуйте, hattab, Вы писали:

H>Вот этот код 100% работающий:


Сделал отдельный тест — работает. Вставляю код твой без изменения в программу — ERangeCheckError. Мистика.
Что-то с опциями ? Или влияет то, что у меня проект не консольный ? Хотя это уж совсем странно.
With best regards
Pavel Dvorkin
Re[9]: вопрос по TMemoryStream
От: Jolly Roger  
Дата: 27.03.12 06:22
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Сделал отдельный тест — работает. Вставляю код твой без изменения в программу — ERangeCheckError. Мистика.

PD>Что-то с опциями ? Или влияет то, что у меня проект не консольный ? Хотя это уж совсем странно.

ERangeCheckError, ЕМНИП, опциями отключается, в том числе и в свойствах проекта.
"Нормальные герои всегда идут в обход!"
Re[9]: вопрос по TMemoryStream
От: Jolly Roger  
Дата: 27.03.12 06:24
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

Я, признаться, недопонял, чем Вас MMF не устроил. Кроссплатформенность?
"Нормальные герои всегда идут в обход!"
Re[9]: вопрос по TMemoryStream
От: hattab  
Дата: 27.03.12 06:34
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD> H>Вот этот код 100% работающий:


PD> Сделал отдельный тест — работает. Вставляю код твой без изменения в программу — ERangeCheckError. Мистика.

PD> Что-то с опциями ? Или влияет то, что у меня проект не консольный ? Хотя это уж совсем странно.

Подебажь, что там за значения у индекса. Я проверил свой код (GetFileData) на реальном файле в 3GB с включенными опциями проверки индекса, не на консольном приложении (хотя это значения не имеет) — код рабочий.
avalon 1.0rc3 build 428, zlib 1.2.3
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.