вопрос по 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
Re[10]: все же обманул
От: hattab  
Дата: 27.03.12 06:34
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

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


PD> А с чего это я его поломаю ? Прибавление числа к указателю его сдвигает, вот и все.


TBytes это не просто указатель. Это указатель на динамический массив, а динамические массивы это типы с управляемым временем жизни и поэтому имеют еще и заголовок (подробности в справке по словам Internal Data Formats) находящийся в отрицательных смещениях от указателя. При присваивании, изменении размера и удалении динамических массивов компилятор модифицирует поля заголовка. Из приведенного кода не понятно, что там за Buffer который присваивается Current, но понятно, что когда Current выйдет за пределы видимости, компилятор будет уменьшать счетчик ссылок на динамический массив (т.е. на Current) и в зависимости от мусора расположенного перед Current (ты ведь изменил указатель) может попытаться его удалить (будет эксепшен), а иначе просто испортит данные декрементом счетчика ссылок.
avalon 1.0rc3 build 428, zlib 1.2.3
Re[10]: вопрос по TMemoryStream
От: hattab  
Дата: 27.03.12 06:38
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

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

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

JR> ERangeCheckError, ЕМНИП, опциями отключается, в том числе и в свойствах проекта.


Заметать сор под ковер негодная практика
avalon 1.0rc3 build 428, zlib 1.2.3
Re[10]: вопрос по TMemoryStream
От: Pavel Dvorkin Россия  
Дата: 27.03.12 06:58
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

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


JR>Я, признаться, недопонял, чем Вас MMF не устроил. Кроссплатформенность?


Вполне устроит, но я вроде и штатными средствами Delphi обошелся.
With best regards
Pavel Dvorkin
Re[11]: все же обманул
От: Pavel Dvorkin Россия  
Дата: 27.03.12 07:40
Оценка:
Здравствуйте, hattab, Вы писали:

H>TBytes это не просто указатель. Это указатель на динамический массив, а динамические массивы это типы с управляемым временем жизни и поэтому имеют еще и заголовок (подробности в справке по словам Internal Data Formats) находящийся в отрицательных смещениях от указателя. При присваивании, изменении размера и удалении динамических массивов компилятор модифицирует поля заголовка. Из приведенного кода не понятно, что там за Buffer который присваивается Current, но понятно, что когда Current выйдет за пределы видимости, компилятор будет уменьшать счетчик ссылок на динамический массив (т.е. на Current) и в зависимости от мусора расположенного перед Current (ты ведь изменил указатель) может попытаться его удалить (будет эксепшен), а иначе просто испортит данные декрементом счетчика ссылок.


Наверное, ты прав, хотя все это работало. Но лучше, действительно, не рисковать, тем более, что последний раз с Delphi я работал в прошлом тысячелетии , и все позабыл. А жаль. Хорошая, в общем, среда, к тому же нативная, на фоне всех этих управляемых сред.

Спасибо за помощб еще раз. В общем, вернул твой код , запретил ERangeCheckError — вроде читает.
With best regards
Pavel Dvorkin
Re: вопрос по TMemoryStream
От: Dimonka Верблюд  
Дата: 27.03.12 10:55
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

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


А что, если не секрет, приходится подгружать?
Re[2]: вопрос по TMemoryStream
От: Pavel Dvorkin Россия  
Дата: 27.03.12 11:25
Оценка:
Здравствуйте, Dimonka, Вы писали:

D>А что, если не секрет, приходится подгружать?


Файл
With best regards
Pavel Dvorkin
Re[10]: вопрос по TMemoryStream
От: hattab  
Дата: 27.03.12 12:35
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

JR> ERangeCheckError, ЕМНИП, опциями отключается, в том числе и в свойствах проекта.


Несмотря на минус смею настаивать на своем мнении. Этот эксепшн не просто так возникает, видимо есть тому причина. Лучше причину таки найти, а не "лечить" симптомы.
avalon 1.0rc3 build 428, zlib 1.2.3
Re[11]: вопрос по TMemoryStream
От: Jolly Roger  
Дата: 27.03.12 13:52
Оценка: +1
Здравствуйте, hattab, Вы писали:

H>Несмотря на минус смею настаивать на своем мнении. Этот эксепшн не просто так возникает, видимо есть тому причина. Лучше причину таки найти, а не "лечить" симптомы.


"Минус" не за само мнение, а за отсутствие его связи с моим постом. Павел спросил, как может быть, что поведение отличается в разных проектах. На этот вопрос я и отвечал, правильность или неправильность использования данной опции не обсуждалось вообще. Ваше же мнение сформулировано так, будто я предлагал сделать что-то нехорошее.
"Нормальные герои всегда идут в обход!"
Re[3]: вопрос по TMemoryStream
От: Dimonka Верблюд  
Дата: 27.03.12 15:31
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

D>>А что, если не секрет, приходится подгружать?


PD>Файл


Т.е. секрет? Обычно такие большие файлы за раз не грузят.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.