52704.fb2
{вернуть первый элемент свободного списка}
Result := FFreeList;
FFreeList := PGenericNode(FFreeList)^.gnNext;
end;
Тип PGenericNode представляет собой запись с одним полем - полем для ссылки gnNext. Этот тип и преобразование типов в коде позволяет работать с узлами в списке свободных узлов на основании общего алгоритма - нечто похожее на тип TSimpleNode, который мы рассматривали ранее. Обратите внимание, конструктор гарантирует, что размер узлов, отслеживаемых диспетчером узлов, составляет, по крайней мере, 4 байта, т.е. размер указателя.
Следующий метод - FreeNode - ничуть не сложнее предыдущего. Он просто вставляет новый узел в начало свободного списка (используется код вставки перед первым узлом).
Листинг 3.4. Освобождение узла в классе TtdNodeManager
procedure TtdNodeManager.FreeNode(aNode : pointer);
begin
{вставить узел (если он не nil) в начало свободного списка}
if (aNode <> nil) then begin
PGenericNode(aNode)^.gnNext := FFreeList;
FFreeList := aNode;
end;
end;
Следующий метод, заслуживающий внимания, - nmAllocNewPage. Этот метод предназначен для распределения памяти объемом FpageSize, вычисленным в конструкторе Create, для новой страницы. Каждая страница содержит указатель и узлы FNodesPerPage. Указатель используется для создания связного списка страниц (именно поэтому конструктор учитывает в своих вычислениях значение sizeof(pointer)). Находящиеся на странице узлы вставляются в свободный список с помощью простого вызова FreeNode. Поскольку переменная NewPage объявлена как PAnsiChar, при выполнении простых операций с указателем для идентификации отдельных узлов на странице преобразование указателя в тип integer не требуется.
Листинг 3.5. Распределение новой страницы в классе TtdNodeManager
procedure TtdNodeManager.nmAllocNewPage;
var
NewPage : PAnsiChar;
i : integer;
begin
{распределить новую страницу и вставить ее в начало списка страниц}
GetMem(NewPage, FPageSize);
PGenericNode(NewPage)^.gnNext := FPageHead;
FPageHead := NewPage;
{разделить новую страницу на узлы и вставить их в начале свободного списка; обратите внимание, что первые 4 байта страницы представляют собой указатель на следующую страницу, поэтому не забудьте их пропустить}
inc(NewPage, sizeof(pointer));
for i := pred(FNodesPerPage) downto 0 do
begin
FreeNode(NewPage);
inc(NewPage, FNodeSize);
end;
end;
И, наконец, деструктор Destroy удаляет все страницы в списке страниц. Он ничего не делает со свободным списком, поскольку все узлы в нем находятся на освобождаемых страницах и будут освобождены в любом случае.
Листинг 3.6. Удаление экземпляра класса TtdNodeManager
destructor TtdNodeManager.Destroy;
var
Temp : pointer;
begin
{освободить все имеющиеся страницы}
while (FPageHead <> nil) do
begin
Temp := PGenericNode (FPageHead)^.gnNext;
FreeMem(FPageHead, FPageSize);
FPageHead := Temp;
end;
inherited Destroy;
end;