Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

TSharedStream — класс упрощающий работу с файлом подкачки

Delphi , Файловая система , Файлы

TSharedStream — класс упрощающий работу с файлом подкачки

Открывает программер как-то холодильник после недельной попойки, глядит, а там нечто аж позеленело от плесени:
- ShareWare, trial version... - подумал программер.


unit SharedStream;

interface 

uses 
 SysUtils, Windows, Classes, Consts; 

type 

{ TSharedStream }

 TSharedStream = class(TStream) { Для совместимости с TStream }
 private 
   FMemory  : Pointer;          { Указатель на данные }
   FSize    : Longint;          { Реальный размер записанных данных }
   FPageSize : Longint;         { Размер выделенной "страницы" под данные }
   FPosition : Longint;         { Текущая позиция "курсора" на "странице" }
 protected 
 public 
   constructor Create; 
   destructor Destroy; override; 
   function Read(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); 
 public 
   property Memory: Pointer read FMemory; 
 end; 

const 
 SwapHandle = $FFFFFFFF; { Handle файла подкачки }

implementation 

resourcestring 
 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 then 
    raise Exception.Create(CouldNotMapViewOfFile); { ошибка -
    неудалось создать объект отображения[т.е. "страница" не создана и указатель на нее = 0].
    Это может быть:
       Если Вы что-либо изменяли в конструкторе -
           a. Из-за ошибки в параметрах, передоваемых функции CreateFileMapping
           б. Если Sz <= 0
       Если Вы ничего не изменяли -
           а. То такое бывает случается после исключительных ситуаций в OS или
              некорректной работы с FileMapping'ом в Вашей или чужой программе.
              Помогает перезагрузка виндуса }

 FMemory := MapViewOfFile(SHandle, FILE_MAP_WRITE, 0, 0, Sz); { Получаем
            указатель на данные }
 if FMemory = nil then 
    raise 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 then 
 begin 
   Result := FSize - FPosition; 
   if Result > 0 then 
   begin 
     if Result > Count then Result := Count; 
     Move((PChar(FMemory) + FPosition)^, Buffer, Result); 
     Inc(FPosition, Result); 
   end 
 end else 
   Result := 0 
end; 

function TSharedStream.Write(const Buffer; Count: Integer): Longint; 
var 
 I : Integer; 
begin { Функция аналогичная TStream.Write().
       Все пояснения по работе с ней см. в help'e. }
 if Count > 0 then 
 begin 
   I := FPosition + Count; 
   if FSize < I then Size := I; 
   System.Move(Buffer, (PChar(FMemory) + FPosition)^, Count); 
   FPosition := I; 
   Result := Count; 
 end else 
   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 
 else if 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 then 
      raise Exception.Create(CouldNotMapViewOfFile); 

   SMemory := MapViewOfFile(SHandle, FILE_MAP_WRITE, 0, 0, NewSz); 
   if SMemory = nil then 
      raise 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 
 end 
end; 

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 
 end 
end; 

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




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.


:: Главная :: Файлы ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-19 13:29:56
2024-10-24 19:57:59/0.0045139789581299/0