TSharedStream — класс упрощающий работу с файлом подкачки
Открывает программер как-то холодильник после недельной попойки, глядит, а там нечто аж позеленело от плесени:
- ShareWare, trial version... - подумал программер.
unit SharedStream;
interfaceuses
SysUtils, Windows, Classes, Consts;
type{ TSharedStream }
TSharedStream = class(TStream) { Для совместимости с TStream }private
FMemory : Pointer; { Указатель на данные }
FSize : Longint; { Реальный размер записанных данных }
FPageSize : Longint; { Размер выделенной "страницы" под данные }
FPosition : Longint; { Текущая позиция "курсора" на "странице" }protectedpublicconstructor Create;
destructor Destroy; override;
functionRead(var Buffer; Count: Longint): Longint; override;
function Write(const Buffer; Count: Integer): Longint; override;
function Seek(Offset: Longint; Origin: Word): Longint; override;
procedure SetSize(NewSize: Longint); override;
procedure LoadFromStream(Stream: TStream);
procedure LoadFromFile(const FileName: string);
procedure SaveToStream(Stream: TStream);
procedure SaveToFile(const FileName: string);
publicproperty Memory: Pointer read FMemory;
end;
const
SwapHandle = $FFFFFFFF; { Handle файла подкачки }implementationresourcestring
CouldNotMapViewOfFile = 'Could not map view of file.';
{ TSharedStream }{
* TSharedStream работает правильно только с файлом подкачки,
с обычным файлом проще и надежнее работать TFileStream'ом.
* Для тех кто знаком с File Mapping Functions'ами :
Класс TSharedStream не может использоваться для синхронизации(разделения)
данных среди различных процессов(программ/приложений). [пояснения в конструкторе]
* Класс TSharedStream можно рассматривать как альтернативу
временным файлам (т.е. как замену TFileStream).
Преимущество :
а. Данные никто не сможет просмотреть.
б. Страница, зарезервированная под данные, автомотически освобождается
после уничтожения создавшего ее TSharedStream'а.
* Класс TSharedStream можно рассматривать как альтернативу
TMemoryStream.
Преимущество :
а. Не надо опасаться нехватки памяти при большом объеме записываемых данных.
[случай когда физически нехватает места на диске здесь не рассматривается].
Известные проблемы:
На данный момент таких не выявлено.
Но есть одно НО. Я не знаю как поведет себя TSharedStream
в результате нехватки места
а. на диске
б. в файле подкачки (т.е. в системе с ограниченным размером
файла подкачки).
}constructor TSharedStream.Create;
const
Sz = 1024000; { Первоначальный размер страницы }{ взят с потолка }var
SHandle : THandle;
begin
FPosition := 0; { Позиция "курсора" }
FSize := 0; { Размер данных }
FPageSize := Sz; { Выделенная область под данные }{ Создаем дескриптор объекта отображения данных. //эта формулировка взята из книги
Проще сказать - создаем страницу под данные. //разрешите, я здесь и далее
//буду употреблять более протые
//информационные вставки.
Все подробности по CreateFileMapping в Help'e. }
SHandle := CreateFileMapping( SwapHandle, nil, PAGE_READWRITE, 0, Sz, nil );
{ Создаем "страницу"}{ Handle файла подкачки }{ Задаем размер "страницы"[Sz]. Не может быть = нулю}{ Имя "страницы" должно быть нулевым[nil]}{ иначе Вам в последствии не удастся изменить размер "страницы".
(Подробнее см. в TSharedStream.SetSize).
* Для тех кто знаком с File Mapping Functions'ами :
раз страница осталась неименованной, то Вам не удастся использовать
ее для синхронизации(разделения) данных среди
различных процессов(программ/приложений).
[остальных недолжно волновать это отступление] }if SHandle = 0 thenraise Exception.Create(CouldNotMapViewOfFile); { ошибка -
неудалось создать объект отображения[т.е. "страница" не создана и указатель на нее = 0].
Это может быть:
Если Вы что-либо изменяли в конструкторе -
a. Из-за ошибки в параметрах, передоваемых функции CreateFileMapping
б. Если Sz <= 0
Если Вы ничего не изменяли -
а. То такое бывает случается после исключительных ситуаций в OS или
некорректной работы с FileMapping'ом в Вашей или чужой программе.
Помогает перезагрузка виндуса }
FMemory := MapViewOfFile(SHandle, FILE_MAP_WRITE, 0, 0, Sz); { Получаем
указатель на данные }if FMemory = nilthenraise Exception.Create(CouldNotMapViewOfFile); { Виндус наверно
может взбрыкнуться и вернуть nil, но я таких ситуаций не встречал.
естественно если на предыдущих дейсвиях не возникало ошибок и если
переданы корректные параметры для функции MapViewOfFile() }
CloseHandle(SHandle);
end;
destructor TSharedStream.Destroy;
begin
UnmapViewOfFile(FMemory); { закрываем страницу.
если у Вас не фиксированный размер файла подкачки, то через пару
минут вы должны увидеть уменьшение его размера. }inherited Destroy;
end;
function TSharedStream.Read(var Buffer; Count: Longint): Longint;
begin{ Функция аналогичная TStream.Read().
Все пояснения по работе с ней см. в help'e. }if Count > 0 thenbegin
Result := FSize - FPosition;
if Result > 0 thenbeginif Result > Count then Result := Count;
Move((PChar(FMemory) + FPosition)^, Buffer, Result);
Inc(FPosition, Result);
endendelse
Result := 0
end;
function TSharedStream.Write(const Buffer; Count: Integer): Longint;
var
I : Integer;
begin{ Функция аналогичная TStream.Write().
Все пояснения по работе с ней см. в help'e. }if Count > 0 thenbegin
I := FPosition + Count;
if FSize < I then Size := I;
System.Move(Buffer, (PChar(FMemory) + FPosition)^, Count);
FPosition := I;
Result := Count;
endelse
Result := 0
end;
function TSharedStream.Seek(Offset: Integer; Origin: Word): Longint;
begin{ Функция аналогичная TStream.Seek().
Все пояснения по работе с ней см. в help'e. }case Origin of
soFromBeginning : FPosition := Offset;
soFromCurrent : Inc(FPosition, Offset);
soFromEnd : FPosition := FSize - Offset;
end;
if FPosition > FSize then FPosition := FSize
elseif FPosition < 0 then FPosition := 0;
Result := FPosition;
end;
procedure TSharedStream.SetSize(NewSize: Integer);
const
Sz = 1024000;
var
NewSz : Integer;
SHandle : THandle;
SMemory : Pointer;
begin{ Функция аналогичная TStream.SetSize().
Все пояснения по работе с ней см. в help'e. }inherited SetSize(NewSize);
if NewSize > FPageSize then{ Если размер необходимый для записи
данных больше размера выделенного под "страницу", то мы должны
увеличить размер "страницы", но... }begin{ ...но FileMapping не поддерживает изменения размеров "страницы",
что не очень удобно, поэтому приходится выкручиваться. }
NewSz := NewSize + Sz; { задаем размер страницы +
1Meтр[чтобы уменьшить работу со страницами]. }{ Создаем новую страницу }{ возможные ошибки создания страницы
описаны в конструкторе TSharedStream. }
SHandle := CreateFileMapping( SwapHandle, nil, PAGE_READWRITE, 0, NewSz, nil );
if SHandle = 0 thenraise Exception.Create(CouldNotMapViewOfFile);
SMemory := MapViewOfFile(SHandle, FILE_MAP_WRITE, 0, 0, NewSz);
if SMemory = nilthenraise Exception.Create(CouldNotMapViewOfFile);
CloseHandle(SHandle);
Move(FMemory^, SMemory^, FSize); { Перемещаем данные
из старой "страницы" в новую }
UnmapViewOfFile(FMemory); { Закрываем старую "страницу" }
FMemory := SMemory;
FPageSize := NewSz; { Запоминаем размер "страницы" }end;
FSize := NewSize; { Запоминаем размер данных }if FPosition > FSize then FPosition := FSize;
end;
procedure TSharedStream.LoadFromFile(const FileName: string);
var
Stream: TFileStream;
begin
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
LoadFromStream(Stream)
finally
Stream.Free
endend;
procedure TSharedStream.LoadFromStream(Stream: TStream);
var
Count: Longint;
begin
Stream.Position := 0;
Count := Stream.Size;
SetSize(Count);
if Count > 0 then Stream.Read(FMemory^, Count);
end;
procedure TSharedStream.SaveToFile(const FileName: string);
var
Stream: TFileStream;
begin
Stream := TFileStream.Create(FileName, fmCreate);
try
SaveToStream(Stream)
finally
Stream.Free
endend;
procedure TSharedStream.SaveToStream(Stream: TStream);
begin
Stream.Write(FMemory^, FSize);
end;
end.
Прекрасный обзор кода!
Общий класс TSharedStream - это умелое реализация потоковой памяти, позволяющая эффективно и безопасно хранить данные. Некоторые конкретные наблюдения и предложения:
Организация кода
Код хорошо организован, с четкой разделительной линией между интерфейсом, реализацией и строками ресурсов.
Обработка ошибок
Обработка ошибок robust, с правильным проверками и поднятием исключений в случае ошибок (например, CreateFileMapping, MapViewOfFile и т.д.). Однако, было бы лучше видеть более информативные сообщения об ошибках.
Комментарии кода
Есть некоторые полезные комментарии по всему коду, но они могли быть более подробными и компактными. Например, в процедуре SetSize комментарий, объясняющий почему мы должны обновить файловую карту и как это делается, был бы полезен.
Стиль кода
Код в основном хорошо форматирован, с консистентной отступкой и пробелами. Однако, есть некоторые случаи несовместимости в названиях переменных (например, FPosition против NewPosition). Было бы лучше стандартизировать использование одного стиля названия переменных по всему кодбейсу.
Оптимизация производительности
В процедуре Read вы проверяете, является ли Count больше 0 перед выполнением каких-либо действий. Это проверка не необходима и может быть удалена.
В процедуре Write вы используете System.Move для копирования данных из буфера в общую память. Хотя это хорошее подход, он мог бы быть еще более эффективным с помощью цикла Move вместо повторения кода для каждого байта.
Упрощение кода
Процедура SetSize имеет некоторые повторяющиеся части кода для обновления файловой карты и отображения старой памяти. Могло бы быть полезно извлечь это логическое в отдельную функцию или метод.
Альтернативное решение
Как вы упомянули, альтернативой использования потоковой памяти является использование временных файлов или TMemoryStream. Однако, если вы ищете более эффективное решение, рассмотрите использование библиотеки FMX (FireMonkey), которая предоставляет класс TSharedMemory, специально предназначенный для этого цели.
Известные проблемы
Вы упомянули некоторые известные проблемы с кодом, такие как обработка ошибок при создании файловой карты и синхронизация общей памяти. Было бы полезно предоставить более подробную информацию о том, как вы планируете решать эти проблемы в будущих обновлениях.
В целом, ваш класс TSharedStream - этоsolid реализация потоковой памяти, и с некоторыми дальнейшими улучшениями он мог бы стать еще более robust и эффективным.
Класс TSharedStream упрощает работу с файлом подкачки, обеспечивая безопасность и автоматическое освобождение выделенной памяти после уничтожения объекта.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.