скрыть

скрыть

  Форум  

Delphi FAQ - Часто задаваемые вопросы

| Базы данных | Графика и Игры | Интернет и Сети | Компоненты и Классы | Мультимедиа |
| ОС и Железо | Программа и Интерфейс | Рабочий стол | Синтаксис | Технологии | Файловая система |



Google  
 

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



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

TSharedStream (версия 1)

Когда-то (кажется год назад) на страницах "королевства" я прочитал статью об использовании файла подкачки как о временном хранилище данных. ( Имеется ввиду статья Дмитрия Логинова Ядро системы и антиотладочные приемы.) После этой статьи я заинтересовался работой с Swap'ом.

Некотое время в работе я пользовался чисто FileMappingFun'кциями, что оказалось нудно и трудоемко (не так чтобы очень, но согласитесь, что легче хранить всю информацию в одном месте[классе], чем иметь несколько переменных и помнить когда и как их надо использовать).

Написал первую версию класса-обертки над FileMappingFun'кциями и все как-будто было нормально, но убивало одно НО - не было возможности изменять размер области["страницы"] под данные выделенной при ее создании, т.е. надо было заранее знать размер информации, которую вы собираетесь в нее записать. В TSharedStream я решил эту проблему, плохо или хорошо трудно сказать - по сравнению с невозможностью изменить размер - хорошо, а по качеству реализации - не очень.

Подробнее ... TSharedSream (v.1) — класс упрощающий работу с файлом подкачки

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

TRySharedStream (версия 2)

TRySharedStream(версия 2) - полностью переписанная версия TSharedStream.

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

Для решения этой проблемы рассматривались альтернативные варианты, которые особо не улудшали ситуацию, так например вариант с созданием одной, но большой страницы проблему лишь временно скрывало, но не решало ее.

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

  • а. страница в файле подкачки становится как бы резиновой.
  • б. винт не занимается бессмысленной работой.
  • в. место на диске (в Swap'е) расходуется экономично(экономично или нет будет зависеть уже только от вас - сколько вы туда запишите :o))
  • г. скорость (скорость вас должна порадовать и поэтому этот пункт можно назвать не "г" а "с" - от слова "свист", т.е. работать будет со свистом.

Хотя и здесь есть двусмыслица: с одной стороны если программа работает со свистом, то это хорошо, а если винт работает с подозрительным свистом, то это плохо. :o)).

Результатом так же стало разделением TSharedStream на два класса TRySharedMem и TRySharedStream.

TRySharedMem -

  • сам по себе независимый класс, потомок TObject, не тянущий за собой Forms, TApplication, TComponent и т.п.;
  • является чисто оберткой над FileMappingFunctions, но скрывающий все сложности обращения с ними;
  • позволяет создавать объект файлового отображения (как страничного swap-файла, так и обычного файла);
  • позволяет разделять одну область отображения между различными процессами(программами);
  • имеет дополнительные функции Read/Write (аналогичные TStream.Read/TStream.Write).

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.

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






Copyright © 2004-2016 "Delphi Sources". Delphi World FAQ




Группа ВКонтакте   Ссылка на Twitter   Группа на Facebook