diff --git a/src/include/lists.pas b/src/include/lists.pas index 2935e76f..62380f4f 100644 --- a/src/include/lists.pas +++ b/src/include/lists.pas @@ -1,3 +1,6 @@ +// +// lists.pas +// // Copyright 2021 Kieron Morris // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -11,12 +14,9 @@ // 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. - -{ - Include->Lists - Linked List Data Structures & Helpers. - - @author(Kieron Morris ) -} +// +// @author (Kieron Morris ) +// @author (Aaron Hance ) unit lists; interface @@ -24,585 +24,1203 @@ interface uses console, lmemorymanager, - util, - tracer; + tracer, + util; type - { Managed Linked List } + {** + @abstract Pointer to a managed linked list node. + **} + PLinkedList = ^TLinkedList; - PLinkedList = ^TLinkedList; - TLinkedList = record - Previous : PLinkedList; - Data : void; - Next : PLinkedList; - end; + {** + @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; - PLinkedListBase = ^TLinkedListBase; - TLinkedListBase = record - Count : uint32; - Head : PLinkedList; - ElementSize : uint32; - end; + {** + @abstract Pointer to the base of a managed linked list. + **} + PLinkedListBase = ^TLinkedListBase; - { Dynamic List } + {** + @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; - PDList = ^TDList; - TDList = record - Count : uint32; - Data : void; - ElementSize : uint32; - DataSize : uint32; + {** + @abstract Pointer to a dynamic list. + **} + PDList = ^TDList; - end; - - { Dynamic List Iterator } + {** + @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 } + // ******************* String Linked List Routines ******************* -procedure STRLL_Add(LinkedList : PLinkedListBase; str : pchar); -function STRLL_Get(LinkedList : PLinkedListBase; idx : uint32) : pchar; -function STRLL_New : PLinkedListBase; -function STRLL_Size(LinkedList : PLinkedListBase) : uint32; -procedure STRLL_Delete(LinkedList : PLinkedListBase; idx : uint32); -procedure STRLL_Free(LinkedList : PLinkedListBase); -procedure STRLL_Clear(LinkedList : PLinkedListBase); -function STRLL_FromString(str : pchar; delimter : char) : PLinkedListBase; + {** + @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); -{ Managed Linked List } + {** + @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; -function LL_New(ElementSize : uint32) : PLinkedListBase; -function LL_Add(LinkedList : PLinkedListBase) : Void; -function LL_Delete(LinkedList : PLinkedListBase; idx : uint32) : boolean; -function LL_Size(LinkedList : PLinkedListBase) : uint32; -function LL_Insert(LinkedList : PLinkedListBase; idx : uint32) : Void; -function LL_Get(LinkedList : PLinkedListBase; idx : uint32) : Void; -procedure LL_Free(LinkedList : PLinkedListBase); -function LL_FromString(str : pchar; delimter : char) : PLinkedListBase; + {** + @abstract Creates a new string linked list. + @returns Pointer to the newly created linked list. + **} + function STRLL_New : PLinkedListBase; - -{ Dynamic List } + {** + @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; -function DL_New(ElementSize : uint32) : PDList; -function DL_Add(DList : PDList) : Void; -function DL_Delete(DList : PDList; idx : uint32) : boolean; -function DL_Size(DList : PDList) : uint32; -function DL_Set(DList : PDList; idx : uint32; elm : puint32) : boolean; -function DL_Get(DList : PDList; idx : uint32) : Void; -procedure DL_Free(DList : PDList); -function DL_IndexOf(DList : PDList; elm : puint32) : uint32; -function DL_Contains(DList : PDList; elm : puint32) : boolean; -function DL_Concat(DList1 : PDList; DList2 : PDList) : PDList; + {** + @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); -{ Dynamic List Iterator } + {** + @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 } +{ ---------------------------------------------------------------------------- } +{ 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; + 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; - Count : uint32; - + 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:= LinkedList^.Count + 1; - LL_Add:= Element^.Data; - end else begin - Base:= LinkedList^.Head; - Count:= 1; - While Base^.Next <> nil do begin - Base:= Base^.Next; - Count:= Count + 1; - end; - Element:= PLinkedList(kalloc(sizeof(TLinkedList))); - Base^.Next:= Element; - Element^.Previous:= Base; - Element^.Next:= nil; - Element^.Data:= kalloc(LinkedList^.ElementSize); - memset(uint32(Element^.Data), 0, LinkedList^.ElementSize); - LinkedList^.Count:= LinkedList^.Count + 1; - LL_Add:= Element^.Data; - end; + 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 - Prev, Next : PLinkedList; - Base : PLinkedList; - i : uint32; - + Base : PLinkedList; + i : uint32; + Prev, Next : PLinkedList; begin - Base:= LinkedList^.Head; - i:= 0; - while (i < idx) and (Base <> nil) do begin - i:= i + 1; - Base:= Base^.Next; - end; - if Base = nil then begin - LL_Delete:= false; - exit; - end; - Prev:= Base^.Previous; - Next:= Base^.Next; - if Prev = nil then begin - LinkedList^.Head:= Next; - end else begin - Prev^.Next:= Next; - end; - if Next <> nil then begin - Next^.Previous:= Prev; - end; - LinkedList^.Count:= LinkedList^.Count - 1; - kfree(void(Base^.Data)); - kfree(void(Base)); - LL_Delete:= True; + 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; + 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; - + 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 - i:= i + 1; - Base:= Base^.Next; - end; - 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; - LinkedList^.Head:= Element; - LinkedList^.Count:= LinkedList^.Count + 1; - LL_Insert:= Element^.Data; - end else begin - 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 begin - LinkedList^.Head:= Element; - end else begin - Prev^.Next:= Element; - end; - if Next <> nil then begin - Next^.Previous:= Element; - end; - LinkedList^.Count:= LinkedList^.Count + 1; - LL_Insert:= Element^.Data; - end; + 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; - + 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 - i:= i + 1; - Base:= Base^.Next; - end; - if Base = nil then exit; - LL_Get:= Base^.Data; + 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 begin - LL_Delete(LinkedList, 0); - end; - kfree(void(LinkedList)); + 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; - i : uint32 = 0; - out_str : pchar; - elm : puint32; - head : pchar; - tail : pchar; - size : uint32; - null_delim : boolean; - + list : PLinkedListBase; + head, tail : pchar; + size : uint32; + null_delim : boolean; + elm : puint32; + out_str : pchar; begin - list := LL_New(sizeof(uint32)); - LL_FromString:= list; + list := LL_New(sizeof(uint32)); + LL_FromString := list; - head:= str; - tail:= head; + head := str; + tail := head; + null_delim := false; - 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); //maybe - memcpy(uint32(tail), uint32(out_str), size); - elm^:= uint32(out_str); - end; - tail:= head+1; - end; - inc(head); + 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 } +{ ---------------------------------------------------------------------------- } +{ 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; - + ptr : puint32; begin - ptr:= puint32(LL_Add(LinkedList)); - ptr^:= uint32(str); + 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; - + ptr : puint32; begin - ptr:= puint32(LL_Get(LinkedList, idx)); - STRLL_Get:= nil; - if ptr <> nil then begin - STRLL_Get:= pchar(ptr^); - end; + 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)); + 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); + 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; - + ptr : pchar; begin - tracer.push_trace('lists.STRLL_Delete (Don''t add static strings)'); - ptr:= STRLL_get(LinkedList, idx); + 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); + 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); + 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); + 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; - i : uint32 = 0; - out_str : pchar; - elm : puint32; - head : pchar; - tail : pchar; - size : uint32; - null_delim : boolean; - + list : PLinkedListBase; + head, tail : pchar; + size : uint32; + null_delim : boolean; + elm : puint32; + out_str : pchar; begin - list := LL_New(sizeof(uint32)); - STRLL_FromString:= list; + list := LL_New(sizeof(uint32)); + STRLL_FromString := list; - head:= str; - tail:= head; + head := str; + tail := head; + null_delim := false; - 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); //maybe - memcpy(uint32(tail), uint32(out_str), size); - elm^:= uint32(out_str); - end; - tail:= head+1; - end; - inc(head); + 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 } +{ ---------------------------------------------------------------------------- } +{ Dynamic List } +{ ---------------------------------------------------------------------------- } -function DL_New(ElementSize : uint32) : PDList; -var - DL : PDList; +{** + @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; - DL := PDList(kalloc(sizeof(DL))); - 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; - if ElementSize > 128 then begin - DL^.data := kalloc(ElementSize * 4); - Dl^.DataSize:= ElementSize * 4; + DL^.Data := kalloc(initialBytes); + DL^.DataSize := initialBytes; - end else if ElementSize > 64 then begin - DL^.data := kalloc(ElementSize * 8); - Dl^.DataSize:= ElementSize * 8; - - end else if ElementSize > 32 then begin - DL^.data := kalloc(ElementSize * 16); - Dl^.DataSize:= ElementSize * 16; - - end else if ElementSize > 16 then begin - DL^.data := kalloc(ElementSize * 32); - Dl^.DataSize:= ElementSize * 32; - end else begin - DL^.data := kalloc(ElementSize * 64); - Dl^.DataSize:= ElementSize * 64; - end; - - DL_New := DL; + DL_New := DL; end; -function DL_Add(DList : PDList) : Void; -var - elm : puint32; - tempList : puint32; +{** + @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; - //check if we need to resize - if (DList^.count + 1) * DList^.ElementSize > DList^.DataSize then begin - push_trace('lists.DL_Add: Resizing'); - tempList := DList^.Data; - DList^.Data := kalloc(DList^.DataSize * 2); - - memset(uint32(DList^.Data), 0, DList^.DataSize * 2); - memcpy(uint32(tempList), uint32(DList^.Data), DList^.DataSize); + DList^.DataSize := DList^.DataSize * 2; + DList^.Data := kalloc(DList^.DataSize); + memset(uint32(DList^.Data), 0, DList^.DataSize); - DList^.DataSize:= DList^.DataSize * 2; + // Copy old data into the new buffer + memcpy(uint32(oldData), uint32(DList^.Data), oldSize); - kfree(void(tempList)); - end; + kfree(oldData); + end; - push_trace('lists.DL_Add'); + // 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); - - elm := puint32(@puint8(DList^.Data)[(DList^.count * DList^.ElementSize)]); - elm^ := 0; - - push_trace('lists.DL_Add: Adding'); - - DList^.count := DList^.count + 1; - - DL_Add := elm; + DList^.Count := DList^.Count + 1; + DL_Add := newElem; end; -function DL_Delete(DList : PDList; idx : uint32) : boolean; -var - elm : puint32; - tempList : puint32; +{** + @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 (idx >= DList^.Count) then + begin + DL_Delete := false; + exit; + end; - elm := puint32( puint8(DList^.Data) + (idx * DList^.ElementSize)); - memcpy(uint32( puint8(elm) + DList^.ElementSize), uInt32(elm), (DList^.count - idx) * DList^.ElementSize); + // 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; - DList^.count := DList^.count - 1; + // Decrement element count + DList^.Count := DList^.Count - 1; - if (DList^.count * DList^.ElementSize) < DList^.DataSize DIV 2 then begin - tempList := DList^.Data; - DList^.DataSize:= DList^.DataSize DIV 2; + // 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^.Data := kalloc(DList^.DataSize); - memcpy(uint32(tempList), uint32(DList^.Data), DList^.DataSize); + DList^.DataSize := DList^.DataSize div 2; + if DList^.DataSize < (DList^.Count * DList^.ElementSize) then + DList^.DataSize := (DList^.Count * DList^.ElementSize); - kfree(void(tempList)); - end; + DList^.Data := kalloc(DList^.DataSize); + memset(uint32(DList^.Data), 0, DList^.DataSize); - DL_Delete := true; + memcpy(uint32(oldData), uint32(DList^.Data), (DList^.Count * DList^.ElementSize)); + kfree(oldData); + end; + + DL_Delete := true; end; -function DL_Get(DList : PDList; idx : uint32) : Void; -var - elm : puint32; +{** + @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; + if (idx >= DList^.Count) then + begin + DL_Get := nil; + exit; + end; - elm := puint32(DList^.Data + (idx * DList^.ElementSize)); - DL_Get := elm; + 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 - tempList : PuInt32; - size : uint32; + targetPtr : pointer; begin - if idx >= DList^.count then begin - DL_Set := false; - exit; - end; + if (idx >= DList^.Count) then + begin + DL_Set := false; + exit; + end; - //check if we need to resize - if (idx + 1) * Dlist^.ElementSize > DList^.DataSize then begin - console.writestring('resising thisthinghere'); + // Copy the data from elm into the dynamic list + targetPtr := pointer(uint32(DList^.Data) + (idx * DList^.ElementSize)); + memcpy(uint32(elm), uint32(targetPtr), DList^.ElementSize); - size := idx * DList^.ElementSize * 2; - - tempList := DList^.Data; - DList^.Data := kalloc(size); - memset(uint32(DList^.Data), 0, size); - - memcpy(uint32(tempList), uint32(DList^.Data), DList^.DataSize); - - DList^.DataSize := size; - - kfree(void(tempList)); - end; - - console.writeString('offset: '); - console.writeintln(idx * DList^.ElementSize); - memcpy(uint32(elm), uint32( PuInt8(DList^.Data) + (idx * DList^.ElementSize)), DList^.ElementSize); - - //check if count is smaller than idx and if so, increase count - if DList^.count < idx then DList^.count := idx; - - DL_Set := true; + 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; + 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 - kfree(void(DList^.Data)); - kfree(void(DList)); + 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; +var + i : uint32; + temp : puint32; begin - for i := 0 to DList^.count - 1 do begin - temp := puint32( puint8(DList^.Data) + (i * DList^.ElementSize)); - if temp = elm then begin - DL_IndexOf := i; - exit; - end; + 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; - - DL_IndexOf := -1; + 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; -var - i : uint32; begin - i := DL_IndexOf(DList, elm); - - if i = -1 then begin - DL_Contains := false; - exit; - end; - - DL_Contains := true; + 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; - temp : puint32; +var + i : uint32; + ptr : pointer; begin + if DList1^.ElementSize <> DList2^.ElementSize then + begin + DL_Concat := nil; + exit; + end; - //check element size - 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; - //check if we need to resize - while true do begin - if (DList1^.count + DList2^.count) * DList1^.ElementSize > DList1^.DataSize then begin - - temp := DList1^.Data; - DList1^.Data := kalloc(DList1^.DataSize * 2); - - memset(uint32(DList1^.Data), 0, DList1^.DataSize * 2); - memcpy(uint32(temp), uint32(DList1^.Data), DList1^.DataSize); - - DList1^.DataSize:= DList1^.DataSize * 2; - - kfree(void(temp)); - end else Break; - end; - - - for i := 0 to DList2^.count - 1 do begin - temp := DL_Add(DList1); - memcpy(uint32(DL_Get(DList2, i)), uint32(temp), DList1^.ElementSize); - end; - - DL_Concat := DList1; + DL_Concat := DList1; end; -end. \ No newline at end of file +{** + @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.