Версия для печати


TSharedSream — класс упрощающий работу с файлом подкачки
http://www.delphikingdom.com/asp/viewitem.asp?catalogID=451

Алексей Румянцев
дата публикации 05-09-2001 12:06

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

TSharedStream (версия 1).

Когда-то (кажется год назад) на страницах "королевства" я прочитал статью об использовании файла подкачки как о временном хранилище данных. ( Имеется ввиду статья Дмитрия Логинова Ядро системы и антиотладочные приемы.) После этой статьи я заинтересовался работой с Swap'ом.
Некотое время в работе я пользовался чисто FileMappingFun'кциями, что оказалось нудно и трудоемко (не так чтобы очень, но согласитесь, что легче хранить всю информацию в одном месте[классе], чем иметь несколько переменных и помнить когда и как их надо использовать).
Написал первую версию класса-обертки над FileMappingFun'кциями и все как-будто было нормально, но убивало одно НО - не было возможности изменять размер области["страницы"] под данные выделенной при ее создании, т.е. надо было заранее знать размер информации, которую вы собираетесь в нее записать. В TSharedStream я решил эту проблему, плохо или хорошо трудно сказать - по сравнению с невозможностью изменить размер - хорошо, а по качеству реализации - не очень.
Подробнее ... TSharedSream (v.1) — класс упрощающий работу с файлом подкачки

Прошло н-ное кол-во времени, появилось желание сделать работу класса правильней, действенней, качественней (нужного слова не подобрать).

TRySharedStream(версия 2)

TRySharedStream(версия 2) - полностью переписанная версия TSharedStream.
Пользовательская сторона работы с классом осталась неизменной (единственное был переименован сам класс и его юнит), а внутреннее содержание претерпело изменения. Не бойтесь, работа файла подкачки не изменилась :o), а вот работа TSharedStream меня устраивать перестала - пересоздание бОльших по объему страниц и перемещение данных из одной в другую по несколько раз хоть и работает быстро, но выглядело по скобарски.

Для решения этой проблемы рассматривались альтернативные варианты, которые особо не улудшали ситуацию, так например вариант с созданием одной, но большой страницы проблему лишь временно скрывало, но не решало ее.
Результатом же раздумий стал многостраничный вариант, т.е. группа маленьких страниц, хранящих информацию, при необходимости добавляются новыми страницами в которые и дозаписываются данные, в результате Хотя и здесь есть двусмыслица: с одной стороны если программа работает со свистом, то это хорошо, а если винт работает с подозрительным свистом, то это плохо. :o)).

Результатом так же стало разделением TSharedStream на два класса TRySharedMem и TRySharedStream.
TRySharedMem - TRySharedStream - Потомок TStream, не тянущий за собой Forms, TApplication, TComponent и т.п. базируется на работе TRySharedMem, аналог временным файлам и постоянным страхам нехватки памяти - т.е. аналог TFileStream и TMemoryStream; расширяет возможности работы с файлом подкачки - размер записываемых данных ограничивается толь местом на диске.

Единственное сейчас TRySharedStream не поддерживает разделения области отображения между различными процессами(программами) как в TRySharedMem, но в следующей версии, скорей всего, эта возможность будет доступна (мысль как это сделать уже есть).

 

Лицензионное соглашение.

Лицензионное соглашение написано в каждом сооветствующем юните, здесь же написано некоторое пояснение :
TRySharedMem и TRySharedStream - это, по большому счету, базируются на результате(ах) работы FileMappingFunctions, но немалое значение здесь имеет и человеческий фактор: как вы распорядитесь объектами отображения, какой файл вы отобразите и что, как и сколько вы туда запишите никто не может знать, а файловая область, как вы знаете, это не шутка. Поэтому программный код дается вам бесплатно, по принципу "as is". Автор и "Королевство Дельфи" снимают с себя всю ответственность за результаты работы классов. Весь риск по работе с этими классами ложится на вас и только вас. Если вы не согласны с лицензионным соглашением или с некоторыми пунктами - вы не должны использовать данный програмный код в ваших проектах.

Класс TRySharedSream.

unit RySharedStream; 
 
interface 
 
uses 
  SysUtils, Windows, Classes, RySharedMem; 
 
{$IFDEF VER120} 
  {$DEFINE D5} 
{$ENDIF} 
{$IFDEF VER130} 
  {$DEFINE D5} 
{$ENDIF} 
{$IFDEF VER140} 
  {$DEFINE D6} 
{$ENDIF} 
 
type 
 
{ TRyPageList } 
 
  TRyPageList = class(TList) 
  protected 
    function  Get(Index: Integer): TRySharedMem; 
    procedure Put(Index: Integer; Item: TRySharedMem); 
  public 
    property  Items[Index: Integer]: TRySharedMem read Get write Put; default; 
  end; 
 
{ TRySharedStream } 
 
  TRySharedStream = class(TStream)  { Для совместимости с TStream } 
  private 
    FSize     : Longint;          { Реальный размер записанных данных } 
    FPosition : Longint; 
    FPages    : TRyPageList; 
  protected 
    function  NewPage: TRySharedMem; 
  public 
    constructor Create; 
    destructor  Destroy; override; 
    function  Read(var Buffer; Count: Longint): Longint; override; 
    function  Write(const Buffer; Count: Longint): 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 
  end; 
 
implementation 
 
uses RyActiveX; 
 
{resourcestring 
  CouldNotMapViewOfFile = 'Could not map view of file.';} 
 
{ TRySharedStream } 
 
{ 
  * Класс TRySharedStream можно рассматривать как альтернативу 
    временным файлам (т.е. как замену TFileStream). 
    Преимущество : 
      а. Данные никто не сможет просмотреть. 
      б. Страницы, зарезервированные под данные, автомотически освобождаются 
         после уничтожения создавшего ее TRySharedStream'а. 
 
  * Класс TRySharedStream можно рассматривать как альтернативу 
    TMemoryStream. 
    Преимущество : 
      а. Не надо опасаться нехватки памяти при большом объеме записываемых данных. 
         [случай когда физически нехватает места на диске здесь не рассматривается]. 
 
  Известные проблемы: 
    На данный момент таких не выявлено. 
    Но есть одно НО. Я не знаю как поведет себя TRySharedStream 
    в результате нехватки места 
      а. на диске 
      б. в файле подкачки (т.е. в системе с ограниченным размером 
                           файла подкачки). 
} 
const 
  PageSize = 1024000; { размер страницы } 
 
constructor TRySharedStream.Create; 
begin 
  FPosition := 0;   { Позиция "курсора" } 
  FSize     := 0;   { Размер данных } 
  FPages    := TRyPageList.Create; 
  FPages.Add(NewPage); 
end; 
 
destructor TRySharedStream.Destroy; 
begin 
  with FPages do 
    while Count > 0 do 
    begin 
      Items[Count - 1].Free; 
      Delete(Count-1); 
    end; 
  FPages.Free; 
  inherited; 
end; 
 
function TRySharedStream.NewPage: TRySharedMem; 
begin 
  Result := TRySharedMem.Create(RyActiveX.GUIDToString(RyActiveX.GetGUID), 0, PageSize) 
  {                                         |} 
  {Я знаю что можно не именовать страницу __|} 
  {но оказалось не всегда Win98 правильно создает новую} 
  {неименнованную страницу. а другого способа получения} 
  {уникальной строки я не знаю.                        } 
  {если у кого-то будут идеи по этому поводу - милости просим.} 
end; 
 
function TRySharedStream.Read(var Buffer; Count: Longint): Longint; 
var 
  FPos, BPos, FPageNo: Integer; 
  ACount, FCount : Longint; 
  Buf: PChar; 
begin 
  Buf := @Buffer; 
  ACount := 0; 
  if Count > 0 then 
  begin 
    FCount := FSize - FPosition; {максимальное кол-во, которое можно прочитать} 
    if FCount > 0 then 
    begin 
      if FCount > Count then FCount := Count; {если нам нужно прочитать меньше чем можем} 
      ACount := FCount; {запоминаем сколько надо} 
      FPageNo := FPosition div PageSize; {т.к. у нас многостраничный stream, то 
      находим с какой страницы начать читать} 
      BPos := 0; 
      FPos := FPosition - (PageSize * FPageNo); {с какой позиции на странице читаем} 
      while FCount > 0 do 
      begin 
        if FCount > (PageSize - FPos) then 
           Count := PageSize - FPos else Count := FCount; {определяем 
           сколько можно прочитать со страницы} 
        Move(PChar(FPages.Items[FPageNo].Memory)[FPos], Buf[BPos], Count); 
        {считаваем инфо. в буффер} 
        Inc(FPageNo); {переходим на следующую страницу} 
        Dec(FCount, Count); 
        Inc(BPos, Count); 
        FPos := 0; 
      end; 
      Inc(FPosition, ACount); 
    end 
  end; 
 
  Result := ACount; 
end; 
 
function TRySharedStream.Write(const Buffer; Count: Longint): Longint; 
var 
  FPos, BPos, FPageNo: Integer; 
  ASize, ACount, FCount : Longint; 
  Buf: PChar; 
begin { Функция аналогичная TStream.Write(). 
        Все пояснения по работе с ней см. в help'e. } 
  Buf := @Buffer; 
  if Count > 0 then 
  begin 
    ASize := FPosition + Count; {определяем сколько места нужно для данных} 
    if FSize < ASize then Size := ASize; {если больше чем было, то увеличиваем размер стрима} 
 
    FCount := Count; {запоминаем сколько надо записать} 
    FPageNo := FPosition div PageSize; {определяем с какой страницы начинаем писать} 
    BPos := 0; 
    FPos := FPosition - (PageSize * FPageNo); {вычисляем позицию на странице} 
    while FCount > 0 do {пока все не напишем ни куда не уходим} 
    begin 
      if FCount > (PageSize - FPos) then 
         ACount := PageSize - FPos else ACount := FCount; 
      Move(Buf[BPos], PChar(FPages.Items[FPageNo].Memory)[FPos], ACount); 
      {пишем сколько влезает до конца страницы} 
      Inc(FPageNo); {переходим на следующую страницу} 
      Dec(FCount, ACount); {уменьшаем кол-во незаписанных на кол-во записанных} 
      Inc(BPos, ACount); 
      FPos := 0; 
    end; 
    FPosition := ASize; 
  end; 
 
  Result := Count; 
end; 
 
function TRySharedStream.Seek(Offset: Longint; 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 TRySharedStream.SetSize(NewSize: Longint); 
var 
  Sz: Longint; 
begin { Функция аналогичная TStream.SetSize(). 
        Все пояснения по работе с ней см. в help'e. } 
  inherited SetSize(NewSize); 
 
  if NewSize > (PageSize * FPages.Count) then { Если размер необходимый для записи 
  данных больше размера выделенного под наш stream, то мы должны 
  увеличить размер stream'a} 
  begin { ...но FileMapping не поддерживает изменения размеров "страницы", 
    что не очень удобно, поэтому приходится выкручиваться. } 
 
    Sz := NewSize div (PageSize * FPages.Count); 
    { думаем сколько нужно досоздать страниц под данные } 
    while Sz > 0 do {создаем страницы} 
    begin 
      FPages.Add(NewPage); 
      Dec(Sz); 
    end; 
  end; 
 
  FSize := NewSize;   { Запоминаем размер данных } 
 
  if FPosition > FSize then FPosition := FSize; 
end; 
 
procedure TRySharedStream.LoadFromFile(const FileName: string); 
var 
  Stream: TFileStream; 
begin 
  Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); 
  try 
    LoadFromStream(Stream) 
  finally 
    Stream.Free 
  end 
end; 
 
procedure TRySharedStream.LoadFromStream(Stream: TStream); 
begin 
  CopyFrom(Stream, 0); 
end; 
 
procedure TRySharedStream.SaveToFile(const FileName: string); 
var 
  Stream: TFileStream; 
begin 
  Stream := TFileStream.Create(FileName, fmCreate); 
  try 
    SaveToStream(Stream) 
  finally 
    Stream.Free 
  end 
end; 
 
procedure TRySharedStream.SaveToStream(Stream: TStream); 
begin 
  Stream.CopyFrom(Self, 0); 
end; 
 
{ TRyPageList } 
 
function TRyPageList.Get(Index: Integer): TRySharedMem; 
begin 
  Result := TRySharedMem(inherited Get(Index)) 
end; 
 
procedure TRyPageList.Put(Index: Integer; Item: TRySharedMem); 
begin 
  inherited Put(Index, Item) 
end; 
 
end. 

Алексей Румянцев
Специально для Королевства Delphi

Прилагается демонстрационный пример использования TRySharedStream : SwapStream.zip(32 K)
Обновление от 17.04.03