TMemoryStream, судя по коду, не предназначен для работы с кусками размером более 2Gb (для указания размеров везде используется тип Longint). Можно сделать своего потомка от TCustomMemoryStream, он поддерживает максимальные размеры целевой платформы.
Здравствуйте, 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 и всех делов
Здравствуйте, hattab, Вы писали:
H>Несмотря на минус смею настаивать на своем мнении. Этот эксепшн не просто так возникает, видимо есть тому причина. Лучше причину таки найти, а не "лечить" симптомы.
"Минус" не за само мнение, а за отсутствие его связи с моим постом. Павел спросил, как может быть, что поведение отличается в разных проектах. На этот вопрос я и отвечал, правильность или неправильность использования данной опции не обсуждалось вообще. Ваше же мнение сформулировано так, будто я предлагал сделать что-то нехорошее.
Здравствуйте, 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.
Здравствуйте, Pavel Dvorkin, Вы писали:
H>>Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Вроде бы FSize и FPosition объявлены в TCustomMemoryStream как private, а между тем к ним идет обращение из метода наследника TMemoryStream.SetSize. Как такое возможно ? Я попробовал вставить код класса TMemoryStream в свой TLargeMemoryStream, естественно, не компилируется. А как тогда System.Classes компилировалось ?
А в дельфях хитрющая инкапсуляция. у-у-у! Все классы, объявленные в одном модуле (unit) автоматически как-бы дружественные, и могут свободно друг другу в приват лазить
PD>Что же теперь, писать с нуля ?
Я дельфей после 7-ки не видел, может в новых версиях, поддерживающих 64 бита, какой-нить новый стрим появился. Или переделайте на IStream.
Здравствуйте, Jolly Roger, Вы писали:
JR>А в дельфях хитрющая инкапсуляция. у-у-у! Все классы, объявленные в одном модуле (unit) автоматически как-бы дружественные, и могут свободно друг другу в приват лазить
Спасибо!!!
PD>>Что же теперь, писать с нуля ?
JR> Я дельфей после 7-ки не видел, может в новых версиях, поддерживающих 64 бита, какой-нить новый стрим появился. Или переделайте на IStream.
Тогда уж проще memory mapped files напрямую через uses Windows.
JR>Ну ежель мест, где используется, много, то может проще написать стрим, базирующийся на MMF? ИМХО, и править проще будет, и вероятность ошибок меньше.
Да нет, там просто от стрима берут пойнтер на его буфер, а дальше уже с ним вся работа. В общем, банальность.
PD>То, что этот код вызывает ошибку для 3Гб файла в 32-битном режиме , понятно, но почему та же ошибка возникает в x64 ? Можно ли с этим бороться ?
PD>Саму идею загружать 3Гб файл в ОП целиком прошу не обсуждать — не я такое писал.
Не знаю сколько у вас оперативной памяти
Так TMemoryStream – записывает в память
Рекомендую использовать TFileStream – записывает прямо в файлов
Здравствуйте, hattab, Вы писали:
H>Вот этот код 100% работающий:
Сделал отдельный тест — работает. Вставляю код твой без изменения в программу — ERangeCheckError. Мистика.
Что-то с опциями ? Или влияет то, что у меня проект не консольный ? Хотя это уж совсем странно.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Сделал отдельный тест — работает. Вставляю код твой без изменения в программу — ERangeCheckError. Мистика. PD>Что-то с опциями ? Или влияет то, что у меня проект не консольный ? Хотя это уж совсем странно.
ERangeCheckError, ЕМНИП, опциями отключается, в том числе и в свойствах проекта.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD> H>Вот этот код 100% работающий:
PD> Сделал отдельный тест — работает. Вставляю код твой без изменения в программу — ERangeCheckError. Мистика. PD> Что-то с опциями ? Или влияет то, что у меня проект не консольный ? Хотя это уж совсем странно.
Подебажь, что там за значения у индекса. Я проверил свой код (GetFileData) на реальном файле в 3GB с включенными опциями проверки индекса, не на консольном приложении (хотя это значения не имеет) — код рабочий.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD> H>Зачем инкаешь TBytes? Поломаешь поинтер будет зяка-зяка
PD> А с чего это я его поломаю ? Прибавление числа к указателю его сдвигает, вот и все.
TBytes это не просто указатель. Это указатель на динамический массив, а динамические массивы это типы с управляемым временем жизни и поэтому имеют еще и заголовок (подробности в справке по словам Internal Data Formats) находящийся в отрицательных смещениях от указателя. При присваивании, изменении размера и удалении динамических массивов компилятор модифицирует поля заголовка. Из приведенного кода не понятно, что там за Buffer который присваивается Current, но понятно, что когда Current выйдет за пределы видимости, компилятор будет уменьшать счетчик ссылок на динамический массив (т.е. на Current) и в зависимости от мусора расположенного перед Current (ты ведь изменил указатель) может попытаться его удалить (будет эксепшен), а иначе просто испортит данные декрементом счетчика ссылок.
Здравствуйте, Jolly Roger, Вы писали:
JR> PD>Сделал отдельный тест — работает. Вставляю код твой без изменения в программу — ERangeCheckError. Мистика. JR> PD>Что-то с опциями ? Или влияет то, что у меня проект не консольный ? Хотя это уж совсем странно.
JR> ERangeCheckError, ЕМНИП, опциями отключается, в том числе и в свойствах проекта.
Здравствуйте, Jolly Roger, Вы писали:
JR>Здравствуйте, Pavel Dvorkin, Вы писали:
JR>Я, признаться, недопонял, чем Вас MMF не устроил. Кроссплатформенность?
Вполне устроит, но я вроде и штатными средствами Delphi обошелся.
Здравствуйте, hattab, Вы писали:
H>TBytes это не просто указатель. Это указатель на динамический массив, а динамические массивы это типы с управляемым временем жизни и поэтому имеют еще и заголовок (подробности в справке по словам Internal Data Formats) находящийся в отрицательных смещениях от указателя. При присваивании, изменении размера и удалении динамических массивов компилятор модифицирует поля заголовка. Из приведенного кода не понятно, что там за Buffer который присваивается Current, но понятно, что когда Current выйдет за пределы видимости, компилятор будет уменьшать счетчик ссылок на динамический массив (т.е. на Current) и в зависимости от мусора расположенного перед Current (ты ведь изменил указатель) может попытаться его удалить (будет эксепшен), а иначе просто испортит данные декрементом счетчика ссылок.
Наверное, ты прав, хотя все это работало. Но лучше, действительно, не рисковать, тем более, что последний раз с Delphi я работал в прошлом тысячелетии , и все позабыл. А жаль. Хорошая, в общем, среда, к тому же нативная, на фоне всех этих управляемых сред.
Спасибо за помощб еще раз. В общем, вернул твой код , запретил ERangeCheckError — вроде читает.
Здравствуйте, Jolly Roger, Вы писали:
JR> ERangeCheckError, ЕМНИП, опциями отключается, в том числе и в свойствах проекта.
Несмотря на минус смею настаивать на своем мнении. Этот эксепшн не просто так возникает, видимо есть тому причина. Лучше причину таки найти, а не "лечить" симптомы.