Скачиваний:
28
Добавлен:
01.05.2014
Размер:
44.7 Кб
Скачать

// Классы и коллекции для организации //
// обычных списков, хэш таблиц //

unit uContainers;

interface

uses Registry, uBinary, uConsts, uAlgorithm;

type
{ Класс TArrayList - список элементов типа Pointer или TObject }

TGroupEnumerator = class;

TArrayList = class(TObject)
private
FCapacity: Integer;
FItems: PPointerItemList;
FCount: Integer;
FOwnItems: Boolean;
procedure SetCapacity(NewCapacity: Integer);
public

{ Конструктор Create создает экземпляр класса TArrayList и выделяет память
под InitialCapacity элементов во внутреннем массиве. }

constructor Create(InitialCapacity: Integer = 0);

{ Деструктор Destroy освобождает память, занятую экземпляром класса
TArrayList и его внутренним массивом. Если свойство OwnItems равно True,
каждый элемент списка приводится к типу TObject и для него вызывается
метод Free. }

destructor Destroy; override;

{ Свойства }

{ Свойство Count возвращает или устанавливает текущее число элементов
в списке. При присвоении значения данному свойству никаких проверок
не выполняется. }

property Count: Integer read FCount write FCount;

{ Свойство ItemList возвращает указатель на внутренний массив, используемый
для хранения элементов списка. Этот указатель меняется при изменении
свойства Capacity. Обращение к свойству ItemList является основным методом
доступа к элементам списка. }

property ItemList: PPointerItemList read FItems;

{ Свойство Capacity считывает или изменяет количество элементов, под которое
распределена память во внутреннем массиве. Если при добавлении нового
элемента фактическое число элементов превысит Capacity, произойдет
перераспределение памяти и свойство Capacity увеличится до значения,
рассчитанного вызовом G_EnlargeCapacity(Capacity). }

property Capacity: Integer read FCapacity write SetCapacity;

{ Если свойство OwnItems равно True, при удалении элементов и полной очистке
списка, например из деструктора класса, для каждого элемента вызывается
метод Free, чтобы освободить память, занимаемую соответствующим объектом.
По умолчанию это свойство равно False и метод Free не вызывается для
элементов списка. }

property OwnItems: Boolean read FOwnItems write FOwnItems;

{ Методы }

{ Следующий метод Load загружает элементы из другого экземпляра списка,
переданного параметром ArrayList. Перед загрузкой данных вызывается метод
Clear с параметром SuppressDisposingItems. }

procedure Load(ArrayList: TArrayList;
SuppressDisposingItems: Boolean = False); overload;

{ Следующий метод Load загружает список из массива указателей, адресуемого
параметром Items, состоящего из Count элементов. Перед загрузкой данных
вызывается метод Clear с параметром SuppressDisposingItems. }

procedure Load(Items: Pointer; Count: Integer;
SuppressDisposingItems: Boolean = False); overload;

{ Вызов метода EnsureCapacity гарантирует, что размер внутреннего массива,
адресуемого свойством ItemList, будет достаточен для хранения Capacity
элементов, т.е. свойство Capacity данного экземпляра класса TArrayList
будет больше или равно значению, переданному параметром Capacity. }

procedure EnsureCapacity(Capacity: Integer);

{ Метод Add добавляет значение, переданное параметром P, в конец списка. }

procedure Add(P: Pointer);

{ Следующий метод Insert вставляет значение, переданное параметром P,
в позицию Index списка (индексация с нуля). }

procedure Insert(Index: Integer; P: Pointer); overload;

{ Следующий метод Insert вставляет в позицию Index списка (индексация с нуля)
массив из Count указателей, адресуемый параметром Items. }

procedure Insert(Index: Integer; Items: Pointer; Count: Integer); overload;

{ Следующий метод Insert вставляет Count копий значения, переданного
параметром P, в позицию Index списка (индексация с нуля). }

procedure Insert(Index, Count: Integer; P: Pointer); overload;

{ Функция PopBack возвращает указатель на последний элемент списка и
уменьшает число элементов на единицу. Эта операция подобна извлечению
элемента из стека. Поместить элемент в стек можно методом Add. }

function PopBack: Pointer;

{ Функция PeekBack возвращает указатель на последний элемент списка, т.е.
элемент с индексом (Count - 1). }

function PeekBack: Pointer;

{ Функция ScanPointer выполняет последовательный поиск элемента списка,
указатель на который передан параметром P, и возвращает индекс найденного
элемента. Если в списке нет элемента с таким указателем, возвращает
значение -1. Если список большой и он отсортирован по возрастанию, лучше
использовать функцию IndexOf, реализующую бинарный поиск. }

function ScanPointer(P: Pointer): Integer;

{ Метод Sort сортирует элементы списка в порядке возрастания. Функция для
сравнения элементов задается параметром CompareFunction. }

procedure Sort(CompareFunction: TCompareFunction);

{ Метод StableSort сортирует элементы списка в порядке возрастания.
В отличие от метода Sort, относительное расположение равных элементов
списка не меняется, но функция сортировки использует дополнительную память.
Функция для сравнения элементов задается параметром CompareFunction. }

procedure StableSort(CompareFunction: TCompareFunction);

{ Функция EnumerateGroups сортирует и группирует элементы списка. Они
сравниваются между собой с помощью функции, которая передается параметром
CompareFunction. В группу объединяются элементы, которые равны с точки
зрения функции CompareFunction. Если данный список уже отсортирован
по возрастанию, в параметре Sorted следует передать значение True,
чтобы избежать повторной сортировки. Иначе, в параметре Sorted надо
передавать False. Функция EnumerateGroups возвращает экземпляр класса
TGroupEnumerator, представляющий собой коллекцию групп. }

function EnumerateGroups(CompareFunction: TCompareFunction;
Sorted: Boolean = False): TGroupEnumerator;

{ Функция Search выполняет бинарный или линейный поиск значения Value
в списке. Если список отсортирован по возрастанию значения искомого
признака, в параметре Sorted следует передать значение True. В этом случае
используется метод бинарного поиска. Если в параметр Sorted равен False,
применяется линейный поиск. Искомое значение передается в виде указателя.
Функция для сопоставления значения элементу списка передается параметром
MatchFunction. В случае успеха функция Search возвращает указатель
на искомый элемент списка, иначе возвращает nil. }

function Search(Value: Pointer; MatchFunction: TMatchFunction;
Sorted: Boolean = False): Pointer;

{ Функция IndexOf выполняет бинарный или линейный поиск значения Value
в списке. Если список отсортирован по возрастанию значения искомого
признака, в параметре Sorted следует передать значение True. В этом случае
используется метод бинарного поиска. Если в параметр Sorted равен False,
применяется линейный поиск. Искомое значение передается в виде указателя.
Функция для сопоставления значения элементу списка передается параметром
MatchFunction. В случае успеха функция IndexOf возвращает индекс искомого
элемента списка, иначе возвращает -1. }

function IndexOf(Value: Pointer; MatchFunction: TMatchFunction;
Sorted: Boolean = False): Integer;

{ Следующий метод RemoveAt удаляет из списка элемент, находящийся в позиции
Index (индексация с нуля). Если свойство OwnItems равно True, элемент
приводится к типу TObject и для него вызывается метод Free. }

procedure RemoveAt(Index: Integer); overload;

{ Следующий метод RemoveAt удаляет из списка Count последовательных
элементов, начиная с элемента с индексом Index. Если свойство OwnItems
равно True, каждый удаляемый элемент приводится к типу TObject и для
него вызывается метод Free. }

procedure RemoveAt(Index, Count: Integer); overload;

{ Метод UnorderedRemoveAt удаляет из списка элемент, находящийся в позиции
Index (индексация с нуля). Удаляемый элемент замещается последним элементом
списка, после чего свойство Count уменьшается на 1. Метод выполняется
значительно быстрее, чем RemoveAt, но приводит к изменению относительного
порядка элементов списка. Если свойство OwnItems равно True, удаляемый
элемент приводится к типу TObject и для него вызывается метод Free. }

procedure UnorderedRemoveAt(Index: Integer);

{ Метод Clear очищает список без освобождения памяти, занимаемой внутренним
массивом. Если свойство OwnItems равно True, каждый элемент списка
приводится к типу TObject и для него вызывается метод Free. Однако, если
в параметре SuppressDisposingItems передано значение True, метод Free
для элементов списка не вызывается. }

procedure Clear(SuppressDisposingItems: Boolean = False);

{ Метод TrimToSize изменяет размер внутреннего массива, адресуемого свойством
ItemList, таким образом, чтобы он соответствовал занятому в настоящий
момент количеству элементов. }

procedure TrimToSize;

{ Функция Equals возвращает True, если данный список поэлементно равен списку
ArrayList. Если списки содержат различное число элементов или содержат
неравные соответствующие элементы, функция возвращает False. Равенство
элементов проверяется с помощью функции CompareFunction, а если она
не указана, сравниваются указатели на соответствующие элементы списков. }

function Equals(ArrayList: TArrayList;
CompareFunction: TCompareFunction = nil): Boolean;

{ Функция Clone возвращает экземпляр класса TArrayList, который является
копией данного списка. Свойство OwnItems нового списка равно False. }

function Clone: TArrayList;
end;


{ Класс TArrayReadOnlyList представляет собой список элементов типа Pointer,
аналогичный TArrayList, но не допускающий добавления/удаления элементов. }

TArrayReadOnlyList = class(TObject)
private
FItems: PPointerItemList;
FCount: Integer;
public

{ Конструктор Create создает экземпляр класса TArrayReadOnlyList на основе
массива указателей, адресуемого параметром ItemList, длиной Count элементов. }

constructor Create(ItemList: PPointerItemList; Count: Integer);

{ Свойства }

{ Свойство Count возвращает число элементов в списке. }

property Count: Integer read FCount;

{ Свойство ItemList возвращает указатель на внутренний массив, содержащий
элементы списка. Обращение к свойству ItemList является основным методом
доступа к элементам списка. }

property ItemList: PPointerItemList read FItems;

{ Методы }

{ Функция ScanPointer выполняет последовательный поиск элемента списка,
указатель на который передан параметром P, и возвращает индекс найденного
элемента. Если в списке нет элемента с таким указателем, возвращает
значение -1. Если список большой и он отсортирован по возрастанию, лучше
использовать функцию IndexOf, реализующую бинарный поиск. }

function ScanPointer(P: Pointer): Integer;

{ Метод Sort сортирует элементы списка в порядке возрастания. Функция для
сравнения элементов задается параметром CompareFunction. }

procedure Sort(CompareFunction: TCompareFunction);

{ Метод StableSort сортирует элементы списка в порядке возрастания.
В отличие от метода Sort, относительное расположение равных элементов
списка не меняется, но функция сортировки использует дополнительную память.
Функция для сравнения элементов задается параметром CompareFunction. }

procedure StableSort(CompareFunction: TCompareFunction);

{ Функция EnumerateGroups сортирует и группирует элементы списка. Они
сравниваются между собой с помощью функции, которая передается параметром
CompareFunction. В группу объединяются элементы, которые равны с точки
зрения функции CompareFunction. Если данный список уже отсортирован
по возрастанию, в параметре Sorted следует передать значение True,
чтобы избежать повторной сортировки. Иначе, в параметре Sorted надо
передавать False. Функция EnumerateGroups возвращает экземпляр класса
TGroupEnumerator, представляющий собой коллекцию групп. }

function EnumerateGroups(CompareFunction: TCompareFunction;
Sorted: Boolean = False): TGroupEnumerator;

{ Функция Search выполняет бинарный или линейный поиск значения Value
в списке. Если список отсортирован по возрастанию значения искомого
признака, в параметре Sorted следует передать значение True. В этом случае
используется метод бинарного поиска. Если в параметр Sorted равен False,
применяется линейный поиск. Искомое значение передается в виде указателя.
Функция для сопоставления значения элементу списка передается параметром
MatchFunction. В случае успеха функция Search возвращает указатель
на искомый элемент списка, иначе возвращает nil. }

function Search(Value: Pointer; MatchFunction: TMatchFunction;
Sorted: Boolean = False): Pointer;

{ Функция IndexOf выполняет бинарный или линейный поиск значения Value
в списке. Если список отсортирован по возрастанию значения искомого
признака, в параметре Sorted следует передать значение True. В этом случае
используется метод бинарного поиска. Если в параметр Sorted равен False,
применяется линейный поиск. Искомое значение передается в виде указателя.
Функция для сопоставления значения элементу списка передается параметром
MatchFunction. В случае успеха функция IndexOf возвращает индекс искомого
элемента списка, иначе возвращает -1. }

function IndexOf(Value: Pointer; MatchFunction: TMatchFunction;
Sorted: Boolean = False): Integer;

{ Функция Equals возвращает True, если данный список поэлементно равен списку
ArrayList. Если списки содержат различное число элементов или содержат
неравные соответствующие элементы, функция возвращает False. Равенство
элементов проверяется с помощью функции CompareFunction, а если она
не указана, сравниваются указатели на соответствующие элементы списков. }

function Equals(ArrayList: TArrayReadOnlyList;
CompareFunction: TCompareFunction = nil): Boolean; overload;
function Equals(ArrayList: TArrayList;
CompareFunction: TCompareFunction = nil): Boolean; overload;

{ Функция Clone возвращает экземпляр класса TArrayList, который является
копией данного списка. Свойство OwnItems нового списка равно False. }

function Clone: TArrayList;
end;


{ Класс TGroupEnumerator - сгруппированный набор данных }

{ Тип массива и указателя на массив групп, каждая из которых представляет
собой коллекцию типа TArrayReadOnlyList. }

PGroupList = ^TGroupList;
TGroupList = array[0..DWordListUpperLimit] of TArrayReadOnlyList;

TGroupEnumerator = class(TObject)
private
FGroups: PGroupList;
FGroupCount: Integer;
public

{ Конструктор Create создает экземпляр класса TGroupEnumerator, который
представляет собой коллекцию групп. Сортирует и группирует элементы
массива ссылок, адресуемого параметром ItemList, состоящего из Count
элементов. При сортировке элементы массива попарно сравниваются с помощью
функции CompareFunction. В группу затем объединяются элементы, равные
с точки зрения этой функции. }

constructor Create(ItemList: PPointerItemList; Count: Integer;
CompareFunction: TCompareFunction; Sorted: Boolean);

{ Деструктор Destroy освобождает память, занятую экземпляром класса
TGroupEnumerator и его внутренним массивом, содержащим группы элементов.
При этом для каждой группы (экземпляра класса TArrayReadOnlyList)
вызывается метод Free. }

destructor Destroy; override;

{ Свойства }

{ Свойство GroupCount возвращает число групп в наборе. }

property GroupCount: Integer read FGroupCount;

{ Свойство GroupList возвращает указатель на внутренний массив, содержащий
группы элементов. Обращение к свойству GroupList является основным методом
доступа к группам. }

property GroupList: PGroupList read FGroups;
end;

{ Класс TStringHashtable представляет собой ассоциативный массив, где с каждым
ключом типа String связано значение типа Pointer или TObject. Ключи являются
уникальными и хранятся в виде хэшированного списка. }

TStringHashtable = class(TObject)
private
FCapacity: Integer;
FKeys: PStringItemList;
FValues: PPointerItemList;
FCount: Integer;
FMaxCount: Integer;
FCaseSensitive: Boolean;
FOwnValues: Boolean;
procedure SetMaxCount(NewMaxCount: Integer);
function GetIndex(P: Pointer): Integer;
procedure SetCapacity(NewCapacity: Integer);
function GetItem(const Key: string): Pointer;
procedure SetItem(const Key: string; Value: Pointer);
public

{ Конструктор Create создает экземпляр класса TStringHashtable и выделяет
память, достаточную для хранения InitialCapacity элементов во внутренних
массивах ключей и ассоциированных с ними значений. Параметр CaseSensitive
определяет, нужно ли учитывать регистр символов при сортировке и поиске
ключей типа String. Если этот параметр равен False, большие и маленькие
буквы в значении ключей не различаются, если True, они различаются. }

constructor Create(CaseSensitive: Boolean; InitialCapacity: Integer = 0);

{ Деструктор Destroy освобождает память, занятую внутренними массивами
и самим экземпляром класса TStringHashtable. Если свойство OwnValues
равно True, деструктор приводит каждое значение в ассоциативном списке
к типу TObject и вызывает для него метод Free }

destructor Destroy; override;

{ Свойства }

{ Свойство Count возвращает текущее число элементов в ассоциативном массиве. }

property Count: Integer read FCount;

{ Свойство Items считывает или устанавливает значение, ассоциированное
с ключом Key. При чтении, если такой ключ отсутствует в списке, свойство
возвращает nil. При записи, если такой ключ отсутствует, в список
добавляется пара ключ-значение. Если в списке уже есть значение,
соответствующее такому ключу, оно заменяется новым значением. При этом,
если свойство OwnValues равно True, старое значение приводится к типу
TObject и для него вызывается метод Free. }

property Items[const Key: string]: Pointer read GetItem write SetItem; default;

{ Свойство Capacity считывает или изменяет количество элементов, под которое
распределена память во внутренних массивах. Если при добавлении нового
элемента фактическое число элементов превысит Capacity, произойдет
выделение дополнительной памяти под внутренние массивы, что повлечет за
собой перестройку хэшированного списка ключей. }

property Capacity: Integer read FMaxCount write SetMaxCount;

{ Свойство CaseSensitive возвращает значение одноименного параметра,
переданного в конструктор данного класса. Если это значение равно True,
сравнение ключей типа String выполняется с учетом регистра символов,
если False, регистр символов игнорируется в значении ключей. }

property CaseSensitive: Boolean read FCaseSensitive;

{ Значение свойства OwnValues определяет, нужно ли вызывать метод Free
для значений, помещенных в ассоциативный список, когда они удаляются
из списка, заменяются новыми значениями или когда сам список выгружается
из памяти. По умолчанию свойство OwnValues равно False и метод Free
не вызывается. }

property OwnValues: Boolean read FOwnValues write FOwnValues;

{ Свойство InnerKeyList возвращает указатель на внутренний массив,
используемый для хранения ключей в виде хэшированного списка. Число
элементов в этом массиве определяется свойством InnerCapacity. Кроме
элементов, содержащих значения ключей, в этом списке встречаются пустые
элементы, равные nil, а также удаленные элементы. Числовое значение ключа
удаленного элемента хэша, т.е. PLongWord(@InnerKeyList^[I])^, равно
$FFFFFFFF. }

property InnerKeyList: PStringItemList read FKeys;

{ Свойство InnerValueList возвращает указатель на внутренний массив значений,
ассоциированных с ключами. Этот указатель меняется при изменении свойства
InnerCapacity. }

property InnerValueList: PPointerItemList read FValues;

{ Свойство InnerCapacity возвращает число элементов, под которое распределена
память в массивах InnerKeyList и InnerValueList. Значение этого свойства
составляет приблизительно 7/5 от значения свойства Capacity, т.к. для
эффективного выполнения поиска в хэшированном списке в нем должно быть
заполнено не более 71-73% элементов. }

property InnerCapacity: Integer read FCapacity;

{ Методы }

{ Вызов метода EnsureCapacity гарантирует, что размер внутренних массивов,
достаточен для помещения в список Capacity элементов, т.е. свойство
Capacity данного экземпляра класса TStringHashtable после вызова метода
EnsureCapacity будет больше или равно значению, переданному параметром
Capacity. }

procedure EnsureCapacity(Capacity: Integer);

{ Метод Add добавляет в ассоциативный список ключ Key и соответствующее
ему значение Value. Если в списке уже есть элемент с таким ключом,
возникает исключение. }

procedure Add(const Key: string; Value: Pointer);

{ Функция Contains возвращает True, если в хэшированном списке присутствует
ключ со значением Key. Если ключ с таким значением отсутствует в списке,
функция возвращает False. }

function Contains(const Key: string): Boolean;

{ Метод Remove удаляет из ассоциативного списка элемент с ключом Key, если
такой элемент имеется. Если свойство OwnValues равно True, значение,
ассоциированное с этим ключом, приводится к типу TObject и для него
вызывается метод Free. }

procedure Remove(const Key: string);

{ Метод Clear очищает массив InnerKeyList и устанавливает в ноль свойство
Count. Память, занятая под внутренние массивы, не освобождается и значение
свойства Capacity не меняется. Если свойство OwnValues равно True, каждое
значение в ассоциативном списке приводится к типу TObject и для него
вызывается метод Free. Однако, если в параметре SuppressDisposingValues
передано значение True, метод Free для значений в ассоциативном списке
не вызывается. }

procedure Clear(SuppressDisposingValues: Boolean = False);
end;

{ Специальные константы, используемые хэшами и бинарными деревьями }

{ Значения ключей для удаленных и пустых значений хэшированных списков
TIntegerHashtable и TStringHashtable. }

const
MissingIntegerHashKey = Integer($81000000);
EmptyIntegerHashKey = Integer($81000001);
MissingStringHashKey = Pointer($FFFFFFFF);

implementation

uses Windows, uCommon, uStrings;

{ TArrayList }

constructor TArrayList.Create(InitialCapacity: Integer);
begin
FCapacity := InitialCapacity;
if InitialCapacity > 0 then
GetMem(FItems, InitialCapacity * SizeOf(Pointer));
end;

destructor TArrayList.Destroy;
var
I: Integer;
begin
if FCapacity > 0 then
begin
if FOwnItems then
for I := FCount - 1 downto 0 do
TObject(FItems^[I]).Free;
FreeMem(FItems);
end;
end;

procedure TArrayList.SetCapacity(NewCapacity: Integer);
var
NewItems: PPointerItemList;
begin
if (NewCapacity <> FCapacity) and (NewCapacity >= FCount) then
begin
if NewCapacity > 0 then
begin
GetMem(NewItems, NewCapacity * SizeOf(Pointer));
if FCount > 0 then
G_CopyLongs(FItems, NewItems, FCount);
end else
NewItems := nil;
if FCapacity > 0 then
FreeMem(FItems);
FCapacity := NewCapacity;
FItems := NewItems;
end;
end;

procedure TArrayList.Load(ArrayList: TArrayList; SuppressDisposingItems: Boolean);
var
C: Integer;
begin
Clear(SuppressDisposingItems);
C := ArrayList.FCount;
if C > FCapacity then
SetCapacity(G_NormalizeCapacity(C));
if C > 0 then
G_CopyLongs(ArrayList.FItems, FItems, C);
FCount := C;
end;

procedure TArrayList.Load(Items: Pointer; Count: Integer;
SuppressDisposingItems: Boolean);
begin
Clear(SuppressDisposingItems);
if Count > FCapacity then
SetCapacity(G_NormalizeCapacity(Count));
if Count > 0 then
G_CopyLongs(Items, FItems, Count);
FCount := Count;
end;

procedure TArrayList.EnsureCapacity(Capacity: Integer);
begin
if FCapacity < Capacity then
SetCapacity(G_NormalizeCapacity(Capacity));
end;

procedure TArrayList.Add(P: Pointer);
begin
if FCount >= FCapacity then
SetCapacity(G_EnlargeCapacity(FCapacity));
FItems^[FCount] := P;
Inc(FCount);
end;

procedure TArrayList.Insert(Index: Integer; P: Pointer);
begin
if LongWord(Index) > LongWord(FCount) then
RaiseError(SErrIndexOutOfRange);
if FCount >= FCapacity then
SetCapacity(G_EnlargeCapacity(FCapacity));
if Index < FCount then
G_MoveLongs(@FItems^[Index], @FItems^[Index + 1], FCount - Index);
FItems^[Index] := P;
Inc(FCount);
end;

procedure TArrayList.Insert(Index: Integer; Items: Pointer; Count: Integer);
var
C: Integer;
begin
if LongWord(Index) > LongWord(FCount) then
RaiseError(SErrIndexOutOfRange);
if Count <= 0 then
Exit;
if Count + FCount > FCapacity then
begin
C := G_EnlargeCapacity(FCapacity);
if C < Count + FCount then
C := Count + FCount;
SetCapacity(C);
end;
if Index < FCount then
G_MoveLongs(@FItems^[Index], @FItems^[Index + Count], FCount - Index);
G_CopyLongs(Items, @FItems^[Index], Count);
Inc(FCount, Count);
end;

procedure TArrayList.Insert(Index, Count: Integer; P: Pointer);
var
C: Integer;
begin
if LongWord(Index) > LongWord(FCount) then
RaiseError(SErrIndexOutOfRange);
if Count <= 0 then
Exit;
if Count + FCount > FCapacity then
begin
C := G_EnlargeCapacity(FCapacity);
if C < Count + FCount then
C := Count + FCount;
SetCapacity(C);
end;
if Index < FCount then
G_MoveLongs(@FItems^[Index], @FItems^[Index + Count], FCount - Index);
G_FillLongs(LongWord(P), @FItems^[Index], Count);
Inc(FCount, Count);
end;

function TArrayList.PopBack: Pointer;
begin
if FCount = 0 then
RaiseError(SErrPeekFromEmptyList);
Dec(FCount);
Result := FItems^[FCount];
end;

function TArrayList.PeekBack: Pointer;
begin
if FCount = 0 then
RaiseError(SErrPeekFromEmptyList);
Result := FItems^[FCount - 1];
end;

function TArrayList.ScanPointer(P: Pointer): Integer;
begin
Result := G_Scan_Pointer(P, FItems, FCount);
end;

procedure TArrayList.Sort(CompareFunction: TCompareFunction);
begin
G_Sort(FItems, FCount, CompareFunction);
end;

procedure TArrayList.StableSort(CompareFunction: TCompareFunction);
begin
G_StableSort(FItems, FCount, CompareFunction);
end;

function TArrayList.EnumerateGroups(CompareFunction: TCompareFunction;
Sorted: Boolean): TGroupEnumerator;
begin
Result := TGroupEnumerator.Create(FItems, FCount, CompareFunction, Sorted);
end;

function TArrayList.Search(Value: Pointer; MatchFunction: TMatchFunction;
Sorted: Boolean): Pointer;
var
Index: Integer;
begin
if Sorted then
Index := G_BinarySearch(Value, FItems, FCount, MatchFunction)
else
Index := G_Search(Value, FItems, FCount, MatchFunction);
if Index >= 0 then
Result := FItems^[Index]
else
Result := nil;
end;

function TArrayList.IndexOf(Value: Pointer; MatchFunction: TMatchFunction;
Sorted: Boolean): Integer;
begin
if Sorted then
Result := G_BinarySearch(Value, FItems, FCount, MatchFunction)
else
Result := G_Search(Value, FItems, FCount, MatchFunction);
end;

procedure TArrayList.RemoveAt(Index: Integer);
begin
if LongWord(Index) >= LongWord(FCount) then
RaiseError(SErrIndexOutOfRange);
if FOwnItems then
TObject(FItems^[Index]).Free;
Dec(FCount);
if Index < FCount then
G_MoveLongs(@FItems^[Index + 1], @FItems^[Index], FCount - Index);
end;

procedure TArrayList.RemoveAt(Index, Count: Integer);
var
I: Integer;
begin
if LongWord(Index) > LongWord(FCount) then
RaiseError(SErrIndexOutOfRange);
if LongWord(Index + Count) >= LongWord(FCount) then
begin
if FOwnItems then
for I := FCount - 1 downto Index do
TObject(FItems^[I]).Free;
FCount := Index;
end
else if Count > 0 then
begin
if FOwnItems then
for I := Index + Count - 1 downto Index do
TObject(FItems^[I]).Free;
Dec(FCount, Count);
G_MoveLongs(@FItems^[Index + Count], @FItems^[Index], FCount - Index);
end;
end;

procedure TArrayList.UnorderedRemoveAt(Index: Integer);
begin
if LongWord(Index) >= LongWord(FCount) then
RaiseError(SErrIndexOutOfRange);
if FOwnItems then
TObject(FItems^[Index]).Free;
Dec(FCount);
if Index < FCount then
FItems^[Index] := FItems^[FCount];
end;

procedure TArrayList.Clear(SuppressDisposingItems: Boolean);
var
I: Integer;
begin
if FOwnItems and not SuppressDisposingItems then
for I := FCount - 1 downto 0 do
TObject(FItems^[I]).Free;
FCount := 0;
end;

procedure TArrayList.TrimToSize;
begin
if FCount < FCapacity then
SetCapacity(FCount);
end;

function TArrayList.Equals(ArrayList: TArrayList;
CompareFunction: TCompareFunction): Boolean;
var
P1, P2: Pointer;
I: Integer;
begin
if ArrayList = Self then
Result := True
else if (ArrayList <> nil) and (ArrayList.FCount = FCount) then
begin
if not Assigned(CompareFunction) then
Result := G_SameLongs(FItems, ArrayList.FItems, FCount)
else
begin
Result := False;
for I := FCount - 1 downto 0 do
begin
P1 := FItems^[I];
P2 := ArrayList.FItems^[I];
if (P1 <> P2) and (CompareFunction(P1, P2) <> 0) then
Exit;
end;
Result := True;
end;
end else
Result := False;
end;

function TArrayList.Clone: TArrayList;
begin
Result := TArrayList.Create(FCount);
if FCount > 0 then
G_CopyLongs(FItems, Result.FItems, FCount);
Result.FCount := FCount;
end;

{ TArrayReadOnlyList }

constructor TArrayReadOnlyList.Create(ItemList: PPointerItemList; Count: Integer);
begin
FItems := ItemList;
FCount := Count;
end;

function TArrayReadOnlyList.ScanPointer(P: Pointer): Integer;
begin
Result := G_Scan_Pointer(P, FItems, FCount);
end;

procedure TArrayReadOnlyList.Sort(CompareFunction: TCompareFunction);
begin
G_Sort(FItems, FCount, CompareFunction);
end;

procedure TArrayReadOnlyList.StableSort(CompareFunction: TCompareFunction);
begin
G_StableSort(FItems, FCount, CompareFunction);
end;

function TArrayReadOnlyList.EnumerateGroups(CompareFunction: TCompareFunction;
Sorted: Boolean): TGroupEnumerator;
begin
Result := TGroupEnumerator.Create(FItems, FCount, CompareFunction, Sorted);
end;

function TArrayReadOnlyList.Search(Value: Pointer; MatchFunction: TMatchFunction;
Sorted: Boolean): Pointer;
var
Index: Integer;
begin
if Sorted then
Index := G_BinarySearch(Value, FItems, FCount, MatchFunction)
else
Index := G_Search(Value, FItems, FCount, MatchFunction);
if Index >= 0 then
Result := FItems^[Index]
else
Result := nil;
end;

function TArrayReadOnlyList.IndexOf(Value: Pointer; MatchFunction: TMatchFunction;
Sorted: Boolean): Integer;
begin
if Sorted then
Result := G_BinarySearch(Value, FItems, FCount, MatchFunction)
else
Result := G_Search(Value, FItems, FCount, MatchFunction);
end;

function TArrayReadOnlyList.Equals(ArrayList: TArrayReadOnlyList;
CompareFunction: TCompareFunction): Boolean;
var
P1, P2: Pointer;
I: Integer;
begin
if ArrayList = Self then
Result := True
else if (ArrayList <> nil) and (ArrayList.FCount = FCount) then
begin
if not Assigned(CompareFunction) then
Result := G_SameLongs(FItems, ArrayList.FItems, FCount)
else
begin
Result := False;
for I := FCount - 1 downto 0 do
begin
P1 := FItems^[I];
P2 := ArrayList.FItems^[I];
if (P1 <> P2) and (CompareFunction(P1, P2) <> 0) then
Exit;
end;
Result := True;
end;
end else
Result := False;
end;

function TArrayReadOnlyList.Equals(ArrayList: TArrayList;
CompareFunction: TCompareFunction): Boolean;
var
P1, P2: Pointer;
I: Integer;
begin
if (ArrayList <> nil) and (ArrayList.FCount = FCount) then
begin
if not Assigned(CompareFunction) then
Result := G_SameLongs(FItems, ArrayList.FItems, FCount)
else
begin
Result := False;
for I := FCount - 1 downto 0 do
begin
P1 := FItems^[I];
P2 := ArrayList.FItems^[I];
if (P1 <> P2) and (CompareFunction(P1, P2) <> 0) then
Exit;
end;
Result := True;
end;
end else
Result := False;
end;

function TArrayReadOnlyList.Clone: TArrayList;
begin
Result := TArrayList.Create(FCount);
if FCount > 0 then
G_CopyLongs(FItems, Result.FItems, FCount);
Result.FCount := FCount;
end;

{ TGroupEnumerator }

constructor TGroupEnumerator.Create(ItemList: PPointerItemList; Count: Integer;
CompareFunction: TCompareFunction; Sorted: Boolean);
var
I, N, P: Integer;
begin
if Count > 0 then
begin
if not Sorted then
G_Sort(ItemList, Count, CompareFunction);
N := 1;
for I := 1 to Count - 1 do
if CompareFunction(ItemList^[I - 1], ItemList^[I]) <> 0 then
Inc(N);
FGroupCount := N;
GetMem(FGroups, N * SizeOf(Pointer));
N := 0;
P := 0;
for I := 1 to Count - 1 do
if CompareFunction(ItemList^[I - 1], ItemList^[I]) <> 0 then
begin
FGroups^[N] := TArrayReadOnlyList.Create(@ItemList^[P], I - P);
Inc(N);
P := I;
end;
FGroups^[N] := TArrayReadOnlyList.Create(@ItemList^[P], Count - P);
end;
end;

destructor TGroupEnumerator.Destroy;
var
I: Integer;
begin
if FGroupCount > 0 then
begin
for I := FGroupCount - 1 downto 0 do
FGroups^[I].Free;
FreeMem(FGroups);
end;
end;

{ TStringHashtable }

constructor TStringHashtable.Create(CaseSensitive: Boolean;
InitialCapacity: Integer);
var
N: Integer;
begin
N := G_EnlargePrimeCapacity((InitialCapacity * 7) div 5);
FCapacity := N;
FMaxCount := (N * 5) div 7;
FCaseSensitive := CaseSensitive;
GetMem(FKeys, N shl 2);
GetMem(FValues, N shl 2);
G_FillLongs(0, FKeys, N);
end;

destructor TStringHashtable.Destroy;
var
I: Integer;
P: Pointer;
begin
for I := FCapacity - 1 downto 0 do
begin
P := Pointer(FKeys^[I]);
if (P <> nil) and (P <> MissingStringHashKey) then
begin
if FOwnValues then
TObject(FValues^[I]).Free;
FKeys^[I] := '';
end;
end;
FreeMem(FKeys);
FreeMem(FValues);
end;

procedure TStringHashtable.SetMaxCount(NewMaxCount: Integer);
var
N: Integer;
begin
if NewMaxCount >= FCount then
begin
N := G_EnlargePrimeCapacity((NewMaxCount * 7) div 5);
if N <> FCapacity then
SetCapacity(N);
end;
end;

function TStringHashtable.GetIndex(P: Pointer): Integer;
var
X, HashSize, HashStep: LongWord;
begin
if not FCaseSensitive then
X := G_CRC32_Text(P)
else
X := G_CRC32_Str(P);
HashSize := LongWord(FCapacity);
HashStep := (((X shr 5) + 1) mod (HashSize - 1)) + 1;
while True do
begin
X := X mod HashSize;
if Pointer(FKeys^[X]) = nil then
begin
Result := X;
Exit;
end;
Inc(X, HashStep);
end;
end;

procedure TStringHashtable.SetCapacity(NewCapacity: Integer);
var
PrevKeys: PStringItemList;
PrevValues: PPointerItemList;
P: Pointer;
N, I: Integer;
begin
N := NewCapacity;
I := FCapacity - 1;
PrevKeys := FKeys;
PrevValues := FValues;
FCapacity := N;
FMaxCount := (N * 5) div 7;
GetMem(FKeys, N shl 2);
GetMem(FValues, N shl 2);
G_FillLongs(0, FKeys, N);
while I >= 0 do
begin
P := Pointer(PrevKeys^[I]);
if (P <> nil) and (P <> MissingStringHashKey) then
begin
N := GetIndex(P);
Pointer(FKeys^[N]) := P;
FValues^[N] := PrevValues^[I];
end;
Dec(I);
end;
FreeMem(PrevKeys);
FreeMem(PrevValues);
end;

function TStringHashtable.GetItem(const Key: string): Pointer;
var
X, HashSize, HashStep: LongWord;
P: Pointer;
begin
HashSize := LongWord(FCapacity);
if not FCaseSensitive then
begin
X := G_CRC32_Text(Key);
HashStep := (((X shr 5) + 1) mod (HashSize - 1)) + 1;
while True do
begin
X := X mod HashSize;
P := Pointer(FKeys^[X]);
if P = nil then
Break;
if (P <> MissingStringHashKey) and G_SameText(P, Pointer(Key)) then
begin
Result := FValues^[X];
Exit;
end;
Inc(X, HashStep);
end;
end else
begin
X := G_CRC32_Str(Key);
HashStep := (((X shr 5) + 1) mod (HashSize - 1)) + 1;
while True do
begin
X := X mod HashSize;
P := Pointer(FKeys^[X]);
if P = nil then
Break;
if (P <> MissingStringHashKey) and G_SameStr(P, Pointer(Key)) then
begin
Result := FValues^[X];
Exit;
end;
Inc(X, HashStep);
end;
end;
Result := nil;
end;

procedure TStringHashtable.SetItem(const Key: string; Value: Pointer);
var
X, HashSize, HashStep: LongWord;
L: Integer;
P: Pointer;
O: Pointer;
begin
HashSize := LongWord(FCapacity);
L := -1;
if not FCaseSensitive then
begin
X := G_CRC32_Text(Key);
HashStep := (((X shr 5) + 1) mod (HashSize - 1)) + 1;
while True do
begin
X := X mod HashSize;
P := Pointer(FKeys^[X]);
if (P <> nil) and (P <> MissingStringHashKey) then
begin
if G_SameText(P, Pointer(Key)) then
begin
if not FOwnValues then
FValues^[X] := Value
else
begin
O := FValues^[X];
if O <> Value then
begin
FValues^[X] := Value;
TObject(O).Free;
end;
end;
Exit;
end;
end else
begin
if L < 0 then
L := X;
if P = nil then
Break;
end;
Inc(X, HashStep);
end;
end else
begin
X := G_CRC32_Str(Key);
HashStep := (((X shr 5) + 1) mod (HashSize - 1)) + 1;
while True do
begin
X := X mod HashSize;
P := Pointer(FKeys^[X]);
if (P <> nil) and (P <> MissingStringHashKey) then
begin
if G_SameStr(P, Pointer(Key)) then
begin
if not FOwnValues then
FValues^[X] := Value
else
begin
O := FValues^[X];
if O <> Value then
begin
FValues^[X] := Value;
TObject(O).Free;
end;
end;
Exit;
end;
end else
begin
if L < 0 then
L := X;
if P = nil then
Break;
end;
Inc(X, HashStep);
end;
end;
if FCount >= FMaxCount then
begin
SetCapacity(G_EnlargePrimeCapacity(FCapacity));
L := GetIndex(Pointer(Key));
end;
Pointer(FKeys^[L]) := nil;
FKeys^[L] := Key;
FValues^[L] := Value;
Inc(FCount);
end;

procedure TStringHashtable.EnsureCapacity(Capacity: Integer);
begin
if FMaxCount < Capacity then
SetCapacity(G_EnlargePrimeCapacity((Capacity * 7) div 5));
end;

procedure TStringHashtable.Add(const Key: string; Value: Pointer);
var
X, HashSize, HashStep: LongWord;
L: Integer;
P: Pointer;
begin
HashSize := LongWord(FCapacity);
L := -1;
if not FCaseSensitive then
begin
X := G_CRC32_Text(Key);
HashStep := (((X shr 5) + 1) mod (HashSize - 1)) + 1;
while True do
begin
X := X mod HashSize;
P := Pointer(FKeys^[X]);
if (P <> nil) and (P <> MissingStringHashKey) then
begin
if G_SameText(P, Pointer(Key)) then
RaiseErrorFmt(SErrKeyDuplicatesInAssociationList, 'TStringHashtable');
end else
begin
if L < 0 then
L := X;
if P = nil then
Break;
end;
Inc(X, HashStep);
end;
end else
begin
X := G_CRC32_Str(Key);
HashStep := (((X shr 5) + 1) mod (HashSize - 1)) + 1;
while True do
begin
X := X mod HashSize;
P := Pointer(FKeys^[X]);
if (P <> nil) and (P <> MissingStringHashKey) then
begin
if G_SameStr(P, Pointer(Key)) then
RaiseErrorFmt(SErrKeyDuplicatesInAssociationList, 'TStringHashtable');
end else
begin
if L < 0 then
L := X;
if P = nil then
Break;
end;
Inc(X, HashStep);
end;
end;
if FCount >= FMaxCount then
begin
SetCapacity(G_EnlargePrimeCapacity(FCapacity));
L := GetIndex(Pointer(Key));
end;
Pointer(FKeys^[L]) := nil;
FKeys^[L] := Key;
FValues^[L] := Value;
Inc(FCount);
end;

function TStringHashtable.Contains(const Key: string): Boolean;
var
X, HashSize, HashStep: LongWord;
P: Pointer;
begin
HashSize := LongWord(FCapacity);
if not FCaseSensitive then
begin
X := G_CRC32_Text(Key);
HashStep := (((X shr 5) + 1) mod (HashSize - 1)) + 1;
while True do
begin
X := X mod HashSize;
P := Pointer(FKeys^[X]);
if P = nil then
Break;
if (P <> MissingStringHashKey) and G_SameText(P, Pointer(Key)) then
begin
Result := True;
Exit;
end;
Inc(X, HashStep);
end;
end else
begin
X := G_CRC32_Str(Key);
HashStep := (((X shr 5) + 1) mod (HashSize - 1)) + 1;
while True do
begin
X := X mod HashSize;
P := Pointer(FKeys^[X]);
if P = nil then
Break;
if (P <> MissingStringHashKey) and G_SameStr(P, Pointer(Key)) then
begin
Result := True;
Exit;
end;
Inc(X, HashStep);
end;
end;
Result := False;
end;

procedure TStringHashtable.Remove(const Key: string);
var
X, HashSize, HashStep: LongWord;
P: Pointer;
begin
HashSize := LongWord(FCapacity);
if not FCaseSensitive then
begin
X := G_CRC32_Text(Key);
HashStep := (((X shr 5) + 1) mod (HashSize - 1)) + 1;
while True do
begin
X := X mod HashSize;
P := Pointer(FKeys^[X]);
if P = nil then
Break;
if (P <> MissingStringHashKey) and G_SameText(P, Pointer(Key)) then
begin
FKeys^[X] := '';
Pointer(FKeys^[X]) := MissingStringHashKey;
if FOwnValues then
TObject(FValues^[X]).Free;
Dec(FCount);
Exit;
end;
Inc(X, HashStep);
end;
end else
begin
X := G_CRC32_Str(Key);
HashStep := (((X shr 5) + 1) mod (HashSize - 1)) + 1;
while True do
begin
X := X mod HashSize;
P := Pointer(FKeys^[X]);
if P = nil then
Break;
if (P <> MissingStringHashKey) and G_SameStr(P, Pointer(Key)) then
begin
FKeys^[X] := '';
Pointer(FKeys^[X]) := MissingStringHashKey;
if FOwnValues then
TObject(FValues^[X]).Free;
Dec(FCount);
Exit;
end;
Inc(X, HashStep);
end;
end;
end;

procedure TStringHashtable.Clear(SuppressDisposingValues: Boolean);
var
I: Integer;
P: Pointer;
begin
for I := FCapacity - 1 downto 0 do
begin
P := Pointer(FKeys^[I]);
if (P <> nil) and (P <> MissingStringHashKey) then
begin
if FOwnValues and not SuppressDisposingValues then
TObject(FValues^[I]).Free;
FKeys^[I] := '';
end;
end;
G_FillLongs(0, FKeys, FCapacity);
FCount := 0;
end;

end.

Соседние файлы в папке utils