a small include that does this job in a generic way, so I can reuse it when I need that again.
Here's the code (note that there is a brief documentation on top of it):
Code: Select all
; Vector library - a simple dynamic array
; Requires Purebasic 4.4x or higher
; Copyright (c) 2009 Milan Schoemig <milan1612@gmx.de>
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
; The above copyright notice and this permission notice shall be included in
; all copies or substantial portions of the Software.
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
; THE SOFTWARE.
EnableExplicit
; NOTE: If debugging is enabled during compilation, this vector libray will
; check your parameters every time you call one of the procedures. If an error
; is detected, a debug message will show up guarding you to the procedure
; that caused the error. If debugging is disabled, all checks are disabled
; to increase the overall performance.
;
; DOCUMENTATION
; Vector_New(element_size, initial_size = 0)
; Creates a new vector object and returns a pointer to it.
;
; element_size is the size in bytes of the size of one element
;
; initial_size (if set) causes the vector to initialize enough
; memory, to hold initial_size of elements. If you know that you
; will add a certain number of elements, use of this parameter
; can greatly improve performance during insertions.
;
; Vector_Delete(*vector._Vector)
; Deletes the vector object and deallocates all associated memory.
;
; Note: If you use the vector to store pointers to memory you
; allocated yourself, you must free it prior to calling this method!
;
; Vector_Size(*vector._Vector)
; Returns the number of elements hold inside the vector object.
;
; Vector_Reserve(*vector._Vector, size)
; Causes the vector object to allocate enough memory to hold
; size numbers of elements.
;
; size is the number of elements you want the vector to reserve space for.
;
; Vector_Clear(*vector._Vector)
; Removes all elements inside the vector object.
;
; Note: The vector may or may not deallocate reserved memory. So
; don't rely on this function if you want to release all the memory.
;
; Vector_Add(*vector._Vector, *data_ptr)
; Adds the specified element to the vector.
;
; *data_ptr is a pointer to some memory where your element is located.
;
; Note: This function actually copies the memory. It does not just
; store the pointer to it.
;
; Vector_Remove(*vector._Vector, index)
; Removes the element of the vector at the specified index.
;
; index is - well - the index to the element you want to remove.
;
; Note: index is zero-based!
;
; Vector_Get(*vector._Vector, index)
; Returns a pointer to the element at the specified index of the vector.
;
; index is the index to the element you want to get.
;
; Note: index is zero-based!
;
; Vector_Set(*vector._Vector, index, *data_ptr)
; Sets the element at the specified index of the vector to the
; value you provide with *data_ptr.
;
; index is the index to the element you want to set.
;
; *data_ptr is a pointer to the memory location of your data.
;
; Note: This function copies the data from your location to the vector's memory.
;
; Vector_ForEach(*vector._Vector, *user_data, *ProcAddr._ForEachProc)
; This function calls the procedure you provide with *ProcAddr for
; each element that is inside of the vector.
;
; *user_data is a variable for you to provide your for-each-procedure with custom
; data that is passed to every call to the procedure.
;
; *ProcAddr is a pointer to a procedure with the following signature:
; Procedure procname(*data_ptr, index, *user_data) - where *data_ptr is a pointer
; to the data of the current element, index is the current element's index and *user_data
; is the variable you passed to Vector_ForEach
;
; Note: In your procedure, you need to return 1 if you want to proceed with the enumeration.
; Returning 0 causes Vector_ForEach to stop calling your procedure.
Structure _Vector
*data_ptr
size.i
capacity.i
element_size.i
EndStructure
Prototype _ForEachProc(*data_ptr, index, *user_data)
DeclareDLL Vector_New(element_size, initial_size = 0)
DeclareDLL Vector_Delete(*vector._Vector)
DeclareDLL Vector_Size(*vector._Vector)
DeclareDLL Vector_Reserve(*vector._Vector, size)
DeclareDLL Vector_Clear(*vector._Vector)
DeclareDLL Vector_Add(*vector._Vector, *data_ptr)
DeclareDLL Vector_Remove(*vector._Vector, index)
DeclareDLL Vector_Get(*vector._Vector, index)
DeclareDLL Vector_Set(*vector._Vector, index, *data_ptr)
DeclareDLL Vector_ForEach(*vector._Vector, *user_data, *ProcAddr._ForEachProc)
Macro _FailIf(Cond)
CompilerIf #PB_Compiler_Debugger
If (cond)
Debug #PB_Compiler_Procedure + " failed, check your parameters."
ProcedureReturn 0
EndIf
CompilerEndIf
EndMacro
ProcedureDLL Vector_New(element_size, initial_size = 0)
Protected *vector._Vector = 0
_FailIf(element_size <= 0 Or initial_size < 0)
*vector = AllocateMemory(SizeOf(_Vector))
*vector\element_size = element_size
If initial_size
*vector\data_ptr = AllocateMemory(initial_size * element_size)
*vector\capacity = initial_size
EndIf
ProcedureReturn *vector
EndProcedure
ProcedureDLL Vector_Delete(*vector._Vector)
_FailIf(*vector <= 0)
FreeMemory(*vector\data_ptr)
FreeMemory(*vector)
ProcedureReturn 1
EndProcedure
ProcedureDLL Vector_Size(*vector._Vector)
_FailIf(*vector <= 0)
ProcedureReturn *vector\size
EndProcedure
ProcedureDLL Vector_Reserve(*vector._Vector, size)
_FailIf(*vector <= 0 Or size <= *vector\size)
If *vector\size = 0 And *vector\capacity = 0
*vector\data_ptr = AllocateMemory(size * *vector\element_size)
*vector\capacity = size
ElseIf *vector\capacity < size
*vector\data_ptr = ReAllocateMemory(*vector\data_ptr, size * *vector\element_size)
*vector\capacity = size
EndIf
ProcedureReturn 1
EndProcedure
ProcedureDLL Vector_Clear(*vector._Vector)
_FailIf(*vector <= 0)
If *vector\size
FillMemory(*vector\data_ptr, *vector\size * *vector\element_size)
*vector\size = 0
EndIf
ProcedureReturn 1
EndProcedure
ProcedureDLL Vector_Add(*vector._Vector, *data_ptr)
_FailIf(*vector <= 0 Or *data_ptr <= 0)
If *vector\size = *vector\capacity
Vector_Reserve(*vector, Int(*vector\capacity * 1.5) + 1)
EndIf
CopyMemory(*data_ptr, *vector\data_ptr + (*vector\element_size * *vector\size), *vector\element_size)
*vector\size + 1
ProcedureReturn *vector\size
EndProcedure
ProcedureDLL Vector_Remove(*vector._Vector, index)
_FailIf(*vector <= 0 Or index < 0 Or index >= *vector\size)
If index = 0
If *vector\size > 1
CopyMemory(*vector\data_ptr + *vector\element_size, *vector\data_ptr, (*vector\size - 1) * *vector\element_size)
*vector\size - 1
FillMemory(*vector\data_ptr + (*vector\size * *vector\element_size), *vector\element_size)
Else
FillMemory(*vector\data_ptr, *vector\element_size)
*vector\size = 0
EndIf
ElseIf index = *vector\size - 1
*vector\size - 1
FillMemory(*vector\data_ptr + (*vector\size * *vector\element_size), *vector\element_size)
Else
Protected *destination = *vector\data_ptr + (index * *vector\element_size)
Protected *source = *vector\data_ptr + ((index + 1) * *vector\element_size)
CopyMemory(*source, *destination, (*vector\size - index) * *vector\element_size)
*vector\size - 1
FillMemory(*vector\data_ptr + (*vector\size * *vector\element_size), *vector\element_size)
EndIf
EndProcedure
ProcedureDLL Vector_Get(*vector._Vector, index)
_FailIf(*vector <= 0 Or index < 0 Or index >= *vector\size)
ProcedureReturn *vector\data_ptr + (*vector\element_size * index)
EndProcedure
ProcedureDLL Vector_Set(*vector._Vector, index, *data_ptr)
_FailIf(*vector <= 0 Or index < 0 Or index >= *vector\size Or *data_ptr <= 0)
CopyMemory(*data_ptr, *vector\data_ptr + (*vector\element_size * index), *vector\element_size)
ProcedureReturn 1
EndProcedure
ProcedureDLL Vector_ForEach(*vector._Vector, *user_data, *ProcAddr._ForEachProc)
_FailIf(*vector <= 0 Or *ProcAddr <= 0)
Protected i, ret
While i < *vector\size
ret = *ProcAddr(Vector_Get(*vector, i), i, *user_data)
If ret = 0
Break
EndIf
i + 1
Wend
ProcedureReturn 1
EndProcedure
Code: Select all
EnableExplicit
XIncludeFile "Vector.pbi"
; Create a vector holding integer elements
Define vector = Vector_New(SizeOf(Integer))
; Populate the vector with all numbers from 1 to 20
Define value, i
For i = 1 To 20
value = i
Vector_Add(vector, @value)
Next
; Print all inserted elements
Debug "All elements:"
For i = 0 To Vector_Size(vector) - 1
Debug PeekI(Vector_Get(vector, i))
Next
Debug ""
; Our procedure for Vector_ForEach
Procedure PrintEven(*ptr.Integer, index, *user_data)
; Print all even values
If *ptr\i % 2 = 0
Debug *ptr\i
EndIf
; Return 1 if you want to proceed with the enumeration
ProcedureReturn 1
EndProcedure
; Enumerate all elements with the foreach helper
Debug "Even elements:"
Vector_ForEach(vector, 0, @PrintEven())
; Delete the vector
Vector_Delete(vector)
on, but if nevertheless you find any bugs or crashes, please report them.