Asuro/src/include/lists.pas

1227 lines
35 KiB
ObjectPascal

//
// lists.pas
//
// Copyright 2021 Kieron Morris
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// @author (Kieron Morris <kjm@kieronmorris.me>)
// @author (Aaron Hance <ah@aaronhance.me>)
unit lists;
interface
uses
console,
lmemorymanager,
tracer,
util;
type
{**
@abstract Pointer to a managed linked list node.
**}
PLinkedList = ^TLinkedList;
{**
@abstract Managed linked list node record.
@field Previous Pointer to the previous node.
@field Data Pointer to the stored data.
@field Next Pointer to the next node.
**}
TLinkedList = record
Previous : PLinkedList;
Data : void;
Next : PLinkedList;
end;
{**
@abstract Pointer to the base of a managed linked list.
**}
PLinkedListBase = ^TLinkedListBase;
{**
@abstract Base record for a managed linked list.
@field Count Number of elements in the list.
@field Head Pointer to the first node.
@field ElementSize Size (in bytes) of each element.
**}
TLinkedListBase = record
Count : uint32;
Head : PLinkedList;
ElementSize : uint32;
end;
{**
@abstract Pointer to a dynamic list.
**}
PDList = ^TDList;
{**
@abstract Record for a dynamic list.
@field Count Number of elements in use.
@field Data Pointer to the underlying buffer.
@field ElementSize Size (in bytes) of each element.
@field DataSize Total allocated bytes for Data.
**}
TDList = record
Count : uint32; // number of elements in use
Data : void; // pointer to the underlying buffer
ElementSize : uint32; // size of each element in bytes
DataSize : uint32; // total allocated bytes for Data
end;
{ Function/Procedure definitions }
// ******************* String Linked List Routines *******************
{**
@abstract Adds a string to a string linked list.
@param LinkedList Pointer to the linked list.
@param str Pointer to the null-terminated string to add.
**}
procedure STRLL_Add(LinkedList : PLinkedListBase; str : pchar);
{**
@abstract Retrieves a string at the given index from a string linked list.
@param LinkedList Pointer to the linked list.
@param idx Zero-based index of the string.
@returns Pointer to the string, or nil if not found.
**}
function STRLL_Get(LinkedList : PLinkedListBase; idx : uint32) : pchar;
{**
@abstract Creates a new string linked list.
@returns Pointer to the newly created linked list.
**}
function STRLL_New : PLinkedListBase;
{**
@abstract Returns the number of elements in the string linked list.
@param LinkedList Pointer to the linked list.
@returns The count of elements.
**}
function STRLL_Size(LinkedList : PLinkedListBase) : uint32;
{**
@abstract Deletes the string at the specified index from the string linked list.
@param LinkedList Pointer to the linked list.
@param idx Index of the string to delete.
**}
procedure STRLL_Delete(LinkedList : PLinkedListBase; idx : uint32);
{**
@abstract Frees the entire string linked list.
@param LinkedList Pointer to the linked list.
**}
procedure STRLL_Free(LinkedList : PLinkedListBase);
{**
@abstract Clears all elements from the string linked list.
@param LinkedList Pointer to the linked list.
**}
procedure STRLL_Clear(LinkedList : PLinkedListBase);
{**
@abstract Creates a string linked list by splitting a string.
@param str The source string.
@param delimter The delimiter character.
@returns A pointer to the resulting linked list.
**}
function STRLL_FromString(str : pchar; delimter : char) : PLinkedListBase;
// ******************* Managed Linked List Routines *******************
{**
@abstract Creates a new managed linked list.
@param ElementSize The size (in bytes) of each element.
@returns A pointer to the newly created linked list base.
**}
function LL_New(ElementSize : uint32) : PLinkedListBase;
{**
@abstract Adds a new element to the managed linked list.
@param LinkedList Pointer to the linked list.
@returns Pointer to the newly allocated data space.
**}
function LL_Add(LinkedList : PLinkedListBase) : Void;
{**
@abstract Deletes the element at the specified index from the linked list.
@param LinkedList Pointer to the linked list.
@param idx Zero-based index of the element to delete.
@returns True if deletion was successful, false otherwise.
**}
function LL_Delete(LinkedList : PLinkedListBase; idx : uint32) : boolean;
{**
@abstract Retrieves the count of elements in the linked list.
@param LinkedList Pointer to the linked list.
@returns The number of elements.
**}
function LL_Size(LinkedList : PLinkedListBase) : uint32;
{**
@abstract Inserts a new element at the specified index in the linked list.
@param LinkedList Pointer to the linked list.
@param idx Zero-based index where the element should be inserted.
@returns Pointer to the newly allocated data space, or nil if insertion failed.
**}
function LL_Insert(LinkedList : PLinkedListBase; idx : uint32) : Void;
{**
@abstract Retrieves the element data at the specified index.
@param LinkedList Pointer to the linked list.
@param idx Zero-based index of the element.
@returns Pointer to the element data, or nil if index is out of range.
**}
function LL_Get(LinkedList : PLinkedListBase; idx : uint32) : Void;
{**
@abstract Frees the entire managed linked list.
@param LinkedList Pointer to the linked list.
**}
procedure LL_Free(LinkedList : PLinkedListBase);
{**
@abstract Creates a managed linked list by splitting a string using a delimiter.
@param str The source string.
@param delimter The delimiter character.
@returns A pointer to the resulting linked list.
**}
function LL_FromString(str : pchar; delimter : char) : PLinkedListBase;
// ******************* Dynamic List Routines *******************
{**
@abstract Creates a new dynamic list.
@param ElementSize The size (in bytes) of each element.
@returns Pointer to the newly created dynamic list.
**}
function DL_New(ElementSize : uint32) : PDList;
{**
@abstract Adds a new element to the dynamic list.
@param DList Pointer to the dynamic list.
@returns Pointer to the newly allocated slot.
@discussion The routine automatically resizes the underlying buffer if necessary.
**}
function DL_Add(DList : PDList) : Void;
{**
@abstract Deletes the element at the specified index from the dynamic list.
@param DList Pointer to the dynamic list.
@param idx Zero-based index of the element to delete.
@returns True if deletion was successful, false otherwise.
@discussion Subsequent elements are shifted left and the buffer may be shrunk.
**}
function DL_Delete(DList : PDList; idx : uint32) : boolean;
{**
@abstract Retrieves the element data at the specified index from the dynamic list.
@param DList Pointer to the dynamic list.
@param idx Zero-based index of the element.
@returns Pointer to the element data, or nil if index is out of range.
**}
function DL_Get(DList : PDList; idx : uint32) : Void;
{**
@abstract Sets the element at the specified index by copying from the provided data.
@param DList Pointer to the dynamic list.
@param idx Zero-based index where the data should be copied.
@param elm Pointer to the source data.
@returns True if the set operation was successful, false otherwise.
**}
function DL_Set(DList : PDList; idx : uint32; elm : puint32) : boolean;
{**
@abstract Retrieves the count of elements currently in the dynamic list.
@param DList Pointer to the dynamic list.
@returns The number of elements.
**}
function DL_Size(DList : PDList) : uint32;
{**
@abstract Frees the entire dynamic list and its underlying buffer.
@param DList Pointer to the dynamic list.
**}
procedure DL_Free(DList : PDList);
{**
@abstract Searches for the given element in the dynamic list.
@param DList Pointer to the dynamic list.
@param elm Pointer to the element to search for.
@returns The index of the element if found; otherwise, returns -1.
@discussion Comparison is performed on pointer addresses.
**}
function DL_IndexOf(DList : PDList; elm : puint32) : uint32;
{**
@abstract Determines whether the dynamic list contains the specified element.
@param DList Pointer to the dynamic list.
@param elm Pointer to the element to search for.
@returns True if the element is found, false otherwise.
**}
function DL_Contains(DList : PDList; elm : puint32) : boolean;
{**
@abstract Concatenates two dynamic lists.
@param DList1 Pointer to the destination dynamic list.
@param DList2 Pointer to the source dynamic list.
@returns Pointer to the concatenated dynamic list (same as DList1), or nil if element sizes differ.
**}
function DL_Concat(DList1 : PDList; DList2 : PDList) : PDList;
{**
@abstract Clears the dynamic list by setting the count to zero.
@param DList Pointer to the dynamic list.
@discussion The underlying buffer is not shrunk to allow fast reuse.
**}
procedure DL_Clear(DList : PDList);
{**
@abstract Inserts a new element at the specified index in the dynamic list.
@param DList Pointer to the dynamic list.
@param idx Zero-based index where the new element should be inserted.
@returns Pointer to the newly allocated slot, or nil if index is out of range.
@discussion Subsequent elements are shifted right.
**}
function DL_Insert(DList : PDList; idx : uint32) : Void;
{**
@abstract Returns the current capacity of the dynamic list.
@param DList Pointer to the dynamic list.
@returns The maximum number of elements that can be stored without reallocation.
**}
function DL_Capacity(DList : PDList) : uint32;
{**
@abstract Ensures the dynamic list has enough allocated space for at least NewCapacity elements.
@param DList Pointer to the dynamic list.
@param NewCapacity The desired capacity (number of elements).
**}
procedure DL_Reserve(DList : PDList; NewCapacity : uint32);
{**
@abstract Shrinks the allocated memory to exactly fit the current number of elements.
@param DList Pointer to the dynamic list.
**}
procedure DL_ShrinkToFit(DList : PDList);
procedure TestAllLists();
implementation
uses
strings;
{ ---------------------------------------------------------------------------- }
{ Managed Linked List }
{ ---------------------------------------------------------------------------- }
{**
@abstract Creates a new managed linked list.
@param ElementSize The size (in bytes) of each element.
@returns A pointer to the newly created linked list base.
**}
function LL_New(ElementSize : uint32) : PLinkedListBase;
begin
LL_New := PLinkedListBase(kalloc(sizeof(TLinkedListBase)));
LL_New^.ElementSize := ElementSize;
LL_New^.Count := 0;
LL_New^.Head := nil;
end;
{**
@abstract Adds a new element to the managed linked list.
@param LinkedList Pointer to the linked list.
@returns Pointer to the newly allocated data space.
**}
function LL_Add(LinkedList : PLinkedListBase) : Void;
var
Element : PLinkedList;
Base : PLinkedList;
begin
if LinkedList^.Head = nil then
begin
Element := PLinkedList(kalloc(sizeof(TLinkedList)));
Element^.Previous := nil;
Element^.Next := nil;
Element^.Data := kalloc(LinkedList^.ElementSize);
memset(uint32(Element^.Data), 0, LinkedList^.ElementSize);
LinkedList^.Head := Element;
LinkedList^.Count := 1;
LL_Add := Element^.Data;
end
else
begin
Base := LinkedList^.Head;
while Base^.Next <> nil do
Base := Base^.Next;
Element := PLinkedList(kalloc(sizeof(TLinkedList)));
Element^.Previous := Base;
Element^.Next := nil;
Element^.Data := kalloc(LinkedList^.ElementSize);
memset(uint32(Element^.Data), 0, LinkedList^.ElementSize);
Base^.Next := Element;
LinkedList^.Count := LinkedList^.Count + 1;
LL_Add := Element^.Data;
end;
end;
{**
@abstract Deletes the element at the specified index from the linked list.
@param LinkedList Pointer to the linked list.
@param idx Zero-based index of the element to delete.
@returns True if deletion was successful, false otherwise.
**}
function LL_Delete(LinkedList : PLinkedListBase; idx : uint32) : boolean;
var
Base : PLinkedList;
i : uint32;
Prev, Next : PLinkedList;
begin
Base := LinkedList^.Head;
i := 0;
while (i < idx) and (Base <> nil) do
begin
Base := Base^.Next;
Inc(i);
end;
if Base = nil then
begin
LL_Delete := false;
exit;
end;
Prev := Base^.Previous;
Next := Base^.Next;
if Prev = nil then
LinkedList^.Head := Next
else
Prev^.Next := Next;
if Next <> nil then
Next^.Previous := Prev;
LinkedList^.Count := LinkedList^.Count - 1;
kfree(void(Base^.Data));
kfree(void(Base));
LL_Delete := true;
end;
{**
@abstract Retrieves the number of elements in the linked list.
@param LinkedList Pointer to the linked list.
@returns The element count.
**}
function LL_Size(LinkedList : PLinkedListBase) : uint32;
begin
LL_Size := LinkedList^.Count;
end;
{**
@abstract Inserts a new element at the specified index in the linked list.
@param LinkedList Pointer to the linked list.
@param idx Zero-based index where the element is to be inserted.
@returns Pointer to the newly allocated data space, or nil if insertion failed.
**}
function LL_Insert(LinkedList : PLinkedListBase; idx : uint32) : void;
var
i : uint32;
Base : PLinkedList;
Prev, Next : PLinkedList;
Element : PLinkedList;
begin
LL_Insert := nil;
if idx > LinkedList^.Count then
Exit;
Base := LinkedList^.Head;
i := 0;
while (i < idx) and (Base <> nil) do
begin
Base := Base^.Next;
Inc(i);
end;
// Insert at head
if i = 0 then
begin
Element := PLinkedList(kalloc(sizeof(TLinkedList)));
Element^.Data := kalloc(LinkedList^.ElementSize);
memset(uint32(Element^.Data), 0, LinkedList^.ElementSize);
Element^.Next := LinkedList^.Head;
Element^.Previous := nil;
if LinkedList^.Head <> nil then
LinkedList^.Head^.Previous := Element;
LinkedList^.Head := Element;
LinkedList^.Count := LinkedList^.Count + 1;
LL_Insert := Element^.Data;
end
else
begin
// Insert in the middle or end
if Base = nil then
Exit;
Prev := Base^.Previous;
Next := Base;
Element := PLinkedList(kalloc(sizeof(TLinkedList)));
Element^.Data := kalloc(LinkedList^.ElementSize);
memset(uint32(Element^.Data), 0, LinkedList^.ElementSize);
Element^.Previous := Prev;
Element^.Next := Next;
if Prev = nil then
LinkedList^.Head := Element
else
Prev^.Next := Element;
if Next <> nil then
Next^.Previous := Element;
LinkedList^.Count := LinkedList^.Count + 1;
LL_Insert := Element^.Data;
end;
end;
{**
@abstract Retrieves the element data at the specified index.
@param LinkedList Pointer to the linked list.
@param idx Zero-based index of the desired element.
@returns Pointer to the element data, or nil if index is out of range.
**}
function LL_Get(LinkedList : PLinkedListBase; idx : uint32) : void;
var
i : uint32;
Base : PLinkedList;
begin
LL_Get := nil;
if idx >= LinkedList^.Count then
exit;
Base := LinkedList^.Head;
i := 0;
while (i < idx) and (Base <> nil) do
begin
Base := Base^.Next;
Inc(i);
end;
if Base <> nil then
LL_Get := Base^.Data;
end;
{**
@abstract Frees the entire managed linked list.
@param LinkedList Pointer to the linked list.
@discussion All nodes are deleted and their memory freed.
**}
procedure LL_Free(LinkedList : PLinkedListBase);
begin
while LL_Size(LinkedList) > 0 do
LL_Delete(LinkedList, 0);
kfree(void(LinkedList));
end;
{**
@abstract Creates a managed linked list by splitting a string using a delimiter.
@param str The source string.
@param delimter The delimiter character.
@returns Pointer to the resulting linked list.
**}
function LL_FromString(str : pchar; delimter : char) : PLinkedListBase;
var
list : PLinkedListBase;
head, tail : pchar;
size : uint32;
null_delim : boolean;
elm : puint32;
out_str : pchar;
begin
list := LL_New(sizeof(uint32));
LL_FromString := list;
head := str;
tail := head;
null_delim := false;
while not null_delim do
begin
if (head^ = delimter) or (head^ = char(0)) then
begin
if head^ = char(0) then
null_delim := true;
size := uint32(head) - uint32(tail);
if size > 0 then
begin
elm := puint32(LL_Add(list));
out_str := stringNew(size + 1);
// Copy from tail -> out_str
memcpy(uint32(tail), uint32(out_str), size);
elm^ := uint32(out_str);
end;
tail := head + 1;
end;
Inc(head);
end;
end;
{ ---------------------------------------------------------------------------- }
{ String Linked List }
{ ---------------------------------------------------------------------------- }
{**
@abstract Adds a string to the string linked list.
@param LinkedList Pointer to the linked list.
@param str Pointer to the null-terminated string.
**}
procedure STRLL_Add(LinkedList : PLinkedListBase; str : pchar);
var
ptr : puint32;
begin
ptr := puint32(LL_Add(LinkedList));
ptr^ := uint32(str);
end;
{**
@abstract Retrieves a string from the string linked list at the specified index.
@param LinkedList Pointer to the linked list.
@param idx Zero-based index.
@returns Pointer to the string, or nil if index is out of range.
**}
function STRLL_Get(LinkedList : PLinkedListBase; idx : uint32) : pchar;
var
ptr : puint32;
begin
ptr := puint32(LL_Get(LinkedList, idx));
STRLL_Get := nil;
if ptr <> nil then
STRLL_Get := pchar(ptr^);
end;
{**
@abstract Creates a new string linked list.
@returns Pointer to the newly created list.
**}
function STRLL_New : PLinkedListBase;
begin
STRLL_New := LL_New(sizeof(uint32));
end;
{**
@abstract Returns the count of strings in the string linked list.
@param LinkedList Pointer to the linked list.
@returns The number of elements.
**}
function STRLL_Size(LinkedList : PLinkedListBase) : uint32;
begin
STRLL_Size := LL_Size(LinkedList);
end;
{**
@abstract Deletes a string from the string linked list at the specified index.
@param LinkedList Pointer to the linked list.
@param idx Zero-based index of the string to delete.
@discussion Frees the string memory before deleting the node.
**}
procedure STRLL_Delete(LinkedList : PLinkedListBase; idx : uint32);
var
ptr : pchar;
begin
tracer.push_trace('lists.STRLL_Delete (Don''t add static strings)');
ptr := STRLL_Get(LinkedList, idx);
if ptr <> nil then
kfree(void(ptr));
LL_Delete(LinkedList, idx);
end;
{**
@abstract Frees the entire string linked list.
@param LinkedList Pointer to the linked list.
@discussion Clears all elements before freeing the list structure.
**}
procedure STRLL_Free(LinkedList : PLinkedListBase);
begin
STRLL_Clear(LinkedList);
LL_Free(LinkedList);
end;
{**
@abstract Clears all elements from the string linked list.
@param LinkedList Pointer to the linked list.
@discussion Each string is deleted in turn.
**}
procedure STRLL_Clear(LinkedList : PLinkedListBase);
begin
while STRLL_Size(LinkedList) > 0 do
STRLL_Delete(LinkedList, 0);
end;
{**
@abstract Creates a string linked list by splitting a string.
@param str The source string.
@param delimter The delimiter character.
@returns Pointer to the newly created list.
**}
function STRLL_FromString(str : pchar; delimter : char) : PLinkedListBase;
var
list : PLinkedListBase;
head, tail : pchar;
size : uint32;
null_delim : boolean;
elm : puint32;
out_str : pchar;
begin
list := LL_New(sizeof(uint32));
STRLL_FromString := list;
head := str;
tail := head;
null_delim := false;
while not null_delim do
begin
if (head^ = delimter) or (head^ = char(0)) then
begin
if head^ = char(0) then
null_delim := true;
size := uint32(head) - uint32(tail);
if size > 0 then
begin
elm := puint32(LL_Add(list));
out_str := stringNew(size + 1);
memcpy(uint32(tail), uint32(out_str), size);
elm^ := uint32(out_str);
end;
tail := head + 1;
end;
Inc(head);
end;
end;
{ ---------------------------------------------------------------------------- }
{ Dynamic List }
{ ---------------------------------------------------------------------------- }
{**
@abstract Creates a new dynamic list.
@param ElementSize The size (in bytes) of each element.
@returns Pointer to the newly allocated dynamic list.
**}
function DL_New(ElementSize : uint32) : PDList;
var
DL : PDList;
initialBytes : uint32;
begin
DL := PDList(kalloc(sizeof(TDList)));
DL^.ElementSize := ElementSize;
DL^.Count := 0;
// Pick an initial capacity based on the element size
if ElementSize > 128 then
initialBytes := ElementSize * 4
else if ElementSize > 64 then
initialBytes := ElementSize * 8
else if ElementSize > 32 then
initialBytes := ElementSize * 16
else if ElementSize > 16 then
initialBytes := ElementSize * 32
else
initialBytes := ElementSize * 64;
DL^.Data := kalloc(initialBytes);
DL^.DataSize := initialBytes;
DL_New := DL;
end;
{**
@abstract Adds a new element to the dynamic list.
@param DList Pointer to the dynamic list.
@returns Pointer to the newly allocated slot.
@discussion Resizes the underlying buffer (doubling its size) if necessary.
**}
function DL_Add(DList : PDList) : Void;
var
newElem : pointer;
oldData : pointer;
oldSize : uint32;
begin
// Check if we need to resize
if ((DList^.Count + 1) * DList^.ElementSize) > DList^.DataSize then
begin
push_trace('lists.DL_Add: Resizing');
oldData := DList^.Data;
oldSize := DList^.DataSize;
DList^.DataSize := DList^.DataSize * 2;
DList^.Data := kalloc(DList^.DataSize);
memset(uint32(DList^.Data), 0, DList^.DataSize);
// Copy old data into the new buffer
memcpy(uint32(oldData), uint32(DList^.Data), oldSize);
kfree(oldData);
end;
// Address of the new element
newElem := pointer(uint32(DList^.Data) + (DList^.Count * DList^.ElementSize));
// Zero out the newly added slot
memset(uint32(newElem), 0, DList^.ElementSize);
DList^.Count := DList^.Count + 1;
DL_Add := newElem;
end;
{**
@abstract Deletes the element at the specified index from the dynamic list.
@param DList Pointer to the dynamic list.
@param idx Zero-based index of the element to delete.
@returns True if deletion was successful, false otherwise.
@discussion Shifts subsequent elements left and shrinks the buffer if underutilized.
**}
function DL_Delete(DList : PDList; idx : uint32) : boolean;
var
sourcePtr, destPtr : pointer;
shiftSize : uint32;
oldData : pointer;
oldSize : uint32;
begin
if (idx >= DList^.Count) then
begin
DL_Delete := false;
exit;
end;
// If not removing the last element, shift the tail left by one element.
if idx < (DList^.Count - 1) then
begin
sourcePtr := pointer(uint32(DList^.Data) + ((idx + 1) * DList^.ElementSize));
destPtr := pointer(uint32(DList^.Data) + (idx * DList^.ElementSize));
shiftSize := (DList^.Count - idx - 1) * DList^.ElementSize;
memcpy(uint32(sourcePtr), uint32(destPtr), shiftSize);
end;
// Decrement element count
DList^.Count := DList^.Count - 1;
// Possibly shrink the buffer if usage is very low
if (DList^.Count > 0) and ((DList^.Count * DList^.ElementSize) < (DList^.DataSize div 2)) then
begin
oldData := DList^.Data;
oldSize := DList^.DataSize;
DList^.DataSize := DList^.DataSize div 2;
if DList^.DataSize < (DList^.Count * DList^.ElementSize) then
DList^.DataSize := (DList^.Count * DList^.ElementSize);
DList^.Data := kalloc(DList^.DataSize);
memset(uint32(DList^.Data), 0, DList^.DataSize);
memcpy(uint32(oldData), uint32(DList^.Data), (DList^.Count * DList^.ElementSize));
kfree(oldData);
end;
DL_Delete := true;
end;
{**
@abstract Retrieves the element data at the specified index in the dynamic list.
@param DList Pointer to the dynamic list.
@param idx Zero-based index of the desired element.
@returns Pointer to the element data, or nil if index is out of range.
**}
function DL_Get(DList : PDList; idx : uint32) : Void;
begin
if (idx >= DList^.Count) then
begin
DL_Get := nil;
exit;
end;
DL_Get := pointer(uint32(DList^.Data) + (idx * DList^.ElementSize));
end;
{**
@abstract Sets the element at the specified index by copying data from the provided pointer.
@param DList Pointer to the dynamic list.
@param idx Zero-based index of the element to set.
@param elm Pointer to the source data.
@returns True if the operation was successful, false otherwise.
**}
function DL_Set(DList : PDList; idx : uint32; elm : puint32) : boolean;
var
targetPtr : pointer;
begin
if (idx >= DList^.Count) then
begin
DL_Set := false;
exit;
end;
// Copy the data from elm into the dynamic list
targetPtr := pointer(uint32(DList^.Data) + (idx * DList^.ElementSize));
memcpy(uint32(elm), uint32(targetPtr), DList^.ElementSize);
DL_Set := true;
end;
{**
@abstract Returns the number of elements currently in the dynamic list.
@param DList Pointer to the dynamic list.
@returns The element count.
**}
function DL_Size(DList : PDList) : uint32;
begin
DL_Size := DList^.Count;
end;
{**
@abstract Frees the entire dynamic list and its underlying buffer.
@param DList Pointer to the dynamic list.
**}
procedure DL_Free(DList : PDList);
begin
if DList = nil then exit;
if DList^.Data <> nil then
kfree(DList^.Data);
kfree(void(DList));
end;
{**
@abstract Searches for an element in the dynamic list.
@param DList Pointer to the dynamic list.
@param elm Pointer to the element to find.
@returns The zero-based index of the element if found; otherwise, returns -1.
@discussion The search is based on pointer equality.
**}
function DL_IndexOf(DList : PDList; elm : puint32) : uint32;
var
i : uint32;
temp : puint32;
begin
for i := 0 to DList^.Count - 1 do
begin
temp := puint32(uint32(DList^.Data) + (i * DList^.ElementSize));
if (temp = elm) then
begin
DL_IndexOf := i;
exit;
end;
end;
DL_IndexOf := uint32(-1);
end;
{**
@abstract Determines whether the dynamic list contains the specified element.
@param DList Pointer to the dynamic list.
@param elm Pointer to the element to search for.
@returns True if the element is present, false otherwise.
**}
function DL_Contains(DList : PDList; elm : puint32) : boolean;
begin
DL_Contains := (DL_IndexOf(DList, elm) <> uint32(-1));
end;
{**
@abstract Concatenates two dynamic lists.
@param DList1 Pointer to the destination dynamic list.
@param DList2 Pointer to the source dynamic list.
@returns Pointer to the concatenated dynamic list (DList1), or nil if element sizes differ.
**}
function DL_Concat(DList1 : PDList; DList2 : PDList) : PDList;
var
i : uint32;
ptr : pointer;
begin
if DList1^.ElementSize <> DList2^.ElementSize then
begin
DL_Concat := nil;
exit;
end;
for i := 0 to DList2^.Count - 1 do
begin
ptr := DL_Add(DList1);
memcpy(uint32(DL_Get(DList2, i)), uint32(ptr), DList1^.ElementSize);
end;
DL_Concat := DList1;
end;
{**
@abstract Clears the dynamic list by resetting the count to zero.
@param DList Pointer to the dynamic list.
@discussion The underlying buffer is retained for fast reuse.
**}
procedure DL_Clear(DList : PDList);
begin
if (DList = nil) then exit;
DList^.Count := 0;
// To clear memory, uncomment the following line:
// memset(uint32(DList^.Data), 0, DList^.DataSize);
end;
{**
@abstract Inserts a new element at the specified index in the dynamic list.
@param DList Pointer to the dynamic list.
@param idx Zero-based index where the element should be inserted.
@returns Pointer to the newly allocated slot, or nil if idx is out of range.
@discussion Subsequent elements are shifted right. The buffer is resized if needed.
**}
function DL_Insert(DList : PDList; idx : uint32) : Void;
var
newElem : pointer;
oldData : pointer;
oldSize : uint32;
shiftSrc : pointer;
shiftDest : pointer;
shiftSize : uint32;
begin
DL_Insert := nil;
if (DList = nil) then exit;
if (idx > DList^.Count) then
exit; // cannot insert beyond the current count
// Ensure there's space for one more element
if ((DList^.Count + 1) * DList^.ElementSize) > DList^.DataSize then
begin
oldData := DList^.Data;
oldSize := DList^.DataSize;
DList^.DataSize := DList^.DataSize * 2;
DList^.Data := kalloc(DList^.DataSize);
memset(uint32(DList^.Data), 0, DList^.DataSize);
memcpy(uint32(oldData), uint32(DList^.Data), oldSize);
kfree(oldData);
end;
// Shift elements to the right if inserting in the middle
if (idx < DList^.Count) then
begin
shiftSrc := pointer(uint32(DList^.Data) + (idx * DList^.ElementSize));
shiftDest := pointer(uint32(DList^.Data) + ((idx + 1) * DList^.ElementSize));
shiftSize := (DList^.Count - idx) * DList^.ElementSize;
// Shift overlapping regions; if memcpy doesn't handle overlap, use memmove.
memcpy(uint32(shiftSrc), uint32(shiftDest), shiftSize);
end;
// Allocate the new element slot
newElem := pointer(uint32(DList^.Data) + (idx * DList^.ElementSize));
memset(uint32(newElem), 0, DList^.ElementSize);
DList^.Count := DList^.Count + 1;
DL_Insert := newElem;
end;
{**
@abstract Returns the current capacity (number of elements) that the dynamic list can hold.
@param DList Pointer to the dynamic list.
@returns The capacity based on the allocated buffer size.
**}
function DL_Capacity(DList : PDList) : uint32;
begin
if DList = nil then
DL_Capacity := 0
else
DL_Capacity := (DList^.DataSize div DList^.ElementSize);
end;
{**
@abstract Ensures the dynamic list has enough space for at least NewCapacity elements.
@param DList Pointer to the dynamic list.
@param NewCapacity Desired capacity (in number of elements).
@discussion Reallocates the buffer if necessary.
**}
procedure DL_Reserve(DList : PDList; NewCapacity : uint32);
var
neededBytes : uint32;
oldData : pointer;
oldSize : uint32;
begin
if (DList = nil) then exit;
neededBytes := NewCapacity * DList^.ElementSize;
if (neededBytes <= DList^.DataSize) then
exit; // Already sufficient
oldData := DList^.Data;
oldSize := DList^.DataSize;
DList^.DataSize := neededBytes;
DList^.Data := kalloc(DList^.DataSize);
memset(uint32(DList^.Data), 0, DList^.DataSize);
memcpy(uint32(oldData), uint32(DList^.Data), oldSize);
kfree(oldData);
end;
{**
@abstract Shrinks the dynamic list's allocated memory to fit its current count.
@param DList Pointer to the dynamic list.
@discussion If the list is empty, the buffer is freed.
**}
procedure DL_ShrinkToFit(DList : PDList);
var
exactBytes : uint32;
oldData : pointer;
begin
if (DList = nil) then exit;
exactBytes := DList^.Count * DList^.ElementSize;
if (exactBytes = 0) then
begin
// For an empty list, free the buffer and reset values.
kfree(DList^.Data);
DList^.Data := nil;
DList^.DataSize := 0;
exit;
end;
if exactBytes >= DList^.DataSize then exit; // Already optimal
oldData := DList^.Data;
DList^.Data := kalloc(exactBytes);
memcpy(uint32(oldData), uint32(DList^.Data), exactBytes);
kfree(oldData);
DList^.DataSize := exactBytes;
end;
procedure TestAllLists;
var
i : uint32;
p : pointer;
ll : PLinkedListBase; // Managed linked list
strList: PLinkedListBase; // String linked list
dlist : PDList; // Dynamic list
intPtr : puint32; // For dynamic list integer values
testStr: pchar;
testStr2: pchar;
begin
{ --- Test Managed Linked List --- }
console.writeString('--- Testing Managed Linked List ---');
ll := LL_New(sizeof(uint32));
for i := 1 to 3 do
begin
p := LL_Add(ll);
puint32(p)^ := i; // Store the value (1, 2, 3)
end;
console.writeString('Managed Linked List Size: ');
console.writeintln(LL_Size(ll));
for i := 0 to LL_Size(ll) - 1 do
begin
p := LL_Get(ll, i);
console.writeString('Element ');
console.writeint(i);
console.writeString(': ');
console.writeintln(puint32(p)^);
end;
LL_Free(ll);
{ --- Test String Linked List --- }
console.writeString('');
console.writeString('--- Testing String Linked List ---');
testStr := stringNew(5);
PuInt8(testStr)^ := ord('H');
PuInt8(testStr + 1)^ := ord('e');
PuInt8(testStr + 2)^ := ord('l');
PuInt8(testStr + 3)^ := ord('l');
PuInt8(testStr + 4)^ := ord('o');
testStr2 := stringNew(5);
puint8(testStr2)^ := ord('W');
puint8(testStr2 + 1)^ := ord('o');
puint8(testStr2 + 2)^ := ord('r');
puint8(testStr2 + 3)^ := ord('l');
puint8(testStr2 + 4)^ := ord('d');
strList := STRLL_New;
STRLL_Add(strList, testStr);
STRLL_Add(strList, testStr2);
console.writeString('String Linked List Size: ');
console.writeintln(STRLL_Size(strList));
for i := 0 to STRLL_Size(strList) - 1 do
begin
console.writeString('String ');
console.writeint(i);
console.writeString(': ');
console.writeString(STRLL_Get(strList, i));
end;
STRLL_Free(strList);
{ --- Test Dynamic List --- }
console.writeString('');
console.writeString('--- Testing Dynamic List ---');
dlist := DL_New(sizeof(uint32));
for i := 1 to 5 do
begin
intPtr := DL_Add(dlist);
intPtr^ := i * 10; // Store values: 10, 20, 30, 40, 50
end;
console.writeString('Dynamic List Size: ');
console.writeintln(DL_Size(dlist));
for i := 0 to DL_Size(dlist) - 1 do
begin
intPtr := DL_Get(dlist, i);
console.writeString('Element ');
console.writeint(i);
console.writeString(': ');
console.writeintln(intPtr^);
end;
{ Insert a new element at index 2 }
intPtr := DL_Insert(dlist, 2);
if intPtr <> nil then
intPtr^ := 999; // Inserted value 999 at index 2
console.writeString('After insertion at index 2, size: ');
console.writeintln(DL_Size(dlist));
for i := 0 to DL_Size(dlist) - 1 do
begin
intPtr := DL_Get(dlist, i);
console.writeString('Element ');
console.writeint(i);
console.writeString(': ');
console.writeintln(intPtr^);
end;
{ Delete element at index 3 }
if DL_Delete(dlist, 3) then
console.writeString('Deleted element at index 3.');
console.writeString('After deletion, size: ');
console.writeintln(DL_Size(dlist));
for i := 0 to DL_Size(dlist) - 1 do
begin
intPtr := DL_Get(dlist, i);
console.writeString('Element ');
console.writeint(i);
console.writeString(': ');
console.writeintln(intPtr^);
end;
DL_Free(dlist);
end;
end.