MQLArticles/Utils/FA/Managers.mqh

1203 lines
72 KiB
MQL5

2025-10-01 12:22:08 -05:00
<EFBFBD><EFBFBD>//+------------------------------------------------------------------+
//| Managers.mqh |
//| Copyright 2025, Niquel Mendoza. |
//| https://www.mql5.com/es/users/nique_372/news |
//+------------------------------------------------------------------+
2025-11-10 09:33:53 -05:00
#property copyright "Copyright 2025, Niquel Mendoza."
2025-10-01 12:22:08 -05:00
#property link "https://www.mql5.com/es/users/nique_372/news"
#property strict
#ifndef MQLARTICLES_UTILS_FA_MANAGERS_MQH
2025-10-01 12:22:08 -05:00
#define MQLARTICLES_UTILS_FA_MANAGERS_MQH
//+------------------------------------------------------------------+
//| Includes |
//+------------------------------------------------------------------+
//--- CLoggerBase
#include "SimpleLogger.mqh"
//--- Basic Functions
2025-10-01 12:22:08 -05:00
#include "FuncionesBases.mqh"
2025-10-01 12:22:08 -05:00
//+------------------------------------------------------------------+
//| Base class for object managers |
//+------------------------------------------------------------------+
2026-03-15 15:43:37 -05:00
2026-03-16 21:29:03 -05:00
/*
2026-03-15 15:43:37 -05:00
ADVERTENCIA: ESTA CLASE SI ELIMINA LOS ITEMS POR DEFECTO, TAMBIEN PROPAGA LOS FLAGS, ES COMO UNA EVOLUCION DE CSPECILZIAEDMANAGER, DADO QUE
NO SOLO PROPAGA LOS FLAGS SI NO QUE SE PEUDEN HACER MAS COSAS CON LOS ITEMS.
WARNING: IF THIS CLASS REMOVES THE DEFAULT ITEMS, IT ALSO PROPAGATES THE FLAGS. IT IS LIKE AN EVOLUTION OF CSPECILZIAEDMANAGER,
SINCE IT NOT ONLY PROPAGATES THE FLAGS BUT MORE THINGS CAN BE DONE WITH THE ITEMS.
*/
/*
2026-01-28 12:07:29 -05:00
2026-01-29 08:50:09 -05:00
Spanish:
2026-02-01 16:17:01 -05:00
Antes de incluir mqhs se podria poner CMANAGERBASE_STRICT_MODE, este define unicamente quitara verificacion de fuera de rango... para CheckPoitner
2026-01-29 08:50:09 -05:00
este siempre se debera de hacer, esto lo hago para optimizar, por que si se que un codigo no dara errores entonces, creo que hacer esos check, estan
2026-02-01 16:17:01 -05:00
por demas.. cuando lo programo, ahi creo qeu si es muy necesario..
2026-01-29 08:19:08 -05:00
English:
Before including mqhs, you could set CMANAGERBASE_STRICT_MODE. This defines that it will only remove the out-of-range check... for CheckPointer,
2025-10-01 12:22:08 -05:00
this should always be done. I do this for optimization purposes, because if I know a code won't produce errors, then I think doing those checks is unnecessary...
when I program it, I think it's very necessary.
*/
//---
//#define CMANAGERBASE_STRICT_MODE
//--- New element
// push\set element
#define MANAGER_BASE_ACTION_NEWEL_PUSH (0)
2025-10-01 12:22:08 -05:00
2026-03-15 15:43:37 -05:00
#define MANAGER_BASE_ACTION_NEWEL_REPLACE (1)
#define MANAGER_BASE_ACTION_NEWEL_INSERT (2)
2025-10-01 12:22:08 -05:00
//--- Delete element
#define MANAGER_BASE_ACTION_DEL_REMOVE (0)
2025-10-01 12:22:08 -05:00
#define MANAGER_BASE_ACTION_DEL_DELETE (1)
2025-12-21 12:41:35 -05:00
#define MANAGER_BASE_ACTION_DEL_REPLACE (2)
2025-10-01 12:22:08 -05:00
#define MANAGER_BASE_ACTION_DEL_REMOVE_LAST (3)
//--- Clear items
2025-10-01 12:22:08 -05:00
#define MANAGER_BASE_ACTION_CLEAR_ITEMS (4)
2025-10-01 12:22:08 -05:00
// Notas para CManagerBasesSimple:
// P no debe tener constructor con parametros o tener uno default, si la clase es pura entonces se tendra que crear una clase hija.
// P should not have a constructor with parameters or should have a default one; if the class is pure, then a child class will have to be created.
// P debe de heredar de CLoggerBase
2025-10-01 12:22:08 -05:00
// T es libre puede o no ser heredero de CLoggerBase
2025-10-01 12:22:08 -05:00
//+------------------------------------------------------------------+
2025-10-01 12:22:08 -05:00
class CEmptyClass
{
2025-10-01 12:22:08 -05:00
public:
CEmptyClass(void) {}
~CEmptyClass(void) {}
};
2025-11-23 08:05:40 -05:00
2026-03-16 21:29:03 -05:00
//+------------------------------------------------------------------+
//| CManagerBasesSimple class |
//+------------------------------------------------------------------+
2025-10-01 12:22:08 -05:00
template<typename T, typename P>
class CManagerBasesSimple : public P
{
2025-10-01 12:22:08 -05:00
protected:
2025-10-01 12:22:08 -05:00
T* items[]; // Pointers Array
int total; // Total number of items
bool clean_in_destructor; // Flag indicating whether items will be deleted in the destructor
2025-10-01 12:22:08 -05:00
//--- Internal methods (without calling virtual functions, class-specific use)
2025-10-01 12:22:08 -05:00
void RemoveInternal(int index);
inline void AddItemInternal(T* item);
inline void ReplaceInternal(int index, T* new_item);
void InsertInternal(T* item, int index);
2025-10-01 12:22:08 -05:00
//--- Virtual functions
virtual void OnAntesClear() {}
// Function executed each time a new element is added at index new_pos
virtual void OnNewElement(int new_pos, int8_t action) {}
2025-10-01 12:22:08 -05:00
// Function executed each time an element is deleted at delete_pos
virtual void OnDeleteElement(int delete_pos, T* item, int8_t action) {}
2025-10-01 12:22:08 -05:00
// Funcion on swap
virtual void OnSwap(int pos1, int pos2) {}
// Cambio masivo
virtual void OnMasiveIndexChange() {}
2025-10-01 12:22:08 -05:00
public:
CManagerBasesSimple();
~CManagerBasesSimple();
//--- General
2025-10-01 12:22:08 -05:00
void SetCleanInDestructor(bool new_val) { clean_in_destructor = new_val; }
__forceinline int Size() const { return total; }
void CleanItems(bool delete_ptrs); // Clean the class and remove items
//--- Basic operations
2025-10-01 12:22:08 -05:00
// Add
virtual bool AddItem(T* item, bool check_duplicate); // Add item to end of array (pointer)
bool AddItem(T& item, bool check_duplicate); // Add item to end of array (reference - usage: instance)
2025-10-01 12:22:08 -05:00
inline void AddItemFast(T* item); // Add item to end of array (pointer), without checks
// Insert
virtual bool Insert(T* item, int index); // Insert at position
bool Insert(T& item, int index); // Insert at position
// Remove
bool Remove(int index); // Remove by index (The function does not delete the pointer)
bool Remove(T* item); // Remove by object (The function does not delete the pointer)
bool RemoveFirst(); // Remove first (The function does not delete the pointer)
2025-10-01 12:22:08 -05:00
bool RemoveLast(); // Remove last (The function does not delete the pointer)
T* RemoveLastAndReturn(); // Remove last and return (The function does not delete the pointer)
2025-10-01 12:22:08 -05:00
// Delete
bool Delete(int index); // Delete pointer
// Replace
2025-10-01 12:22:08 -05:00
virtual bool Replace(int index, T* new_item); // Replace item (The function does not delete the pointer)
// Check index
inline bool InRange(const int index, const string& function_name) const; // Check index
2025-10-01 12:22:08 -05:00
// Search
2026-03-15 15:43:37 -05:00
int Find(T* item); // Find index
2025-10-01 12:22:08 -05:00
int Exist(T* item); // Check if item exists (unique), returns its index if exists
T* GetFirst(); // First item
2025-10-01 12:22:08 -05:00
T* GetLast(); // Last item
2025-10-01 12:22:08 -05:00
int GetActiveCount(); // Non-invalid items
// Get
2025-10-01 12:22:08 -05:00
2025-12-21 12:41:35 -05:00
bool GetData(T* &dst_array[], int start = 0, int count = WHOLE_ARRAY) const;
2026-03-15 15:43:37 -05:00
2025-10-01 12:22:08 -05:00
// Assign
virtual void Assign(T* &data[], bool delete_prev);
2025-10-01 12:22:08 -05:00
2025-10-01 12:22:08 -05:00
// Copy to
template <typename TClass>
bool CopyTo(TClass* obj, int start = 0, int count = WHOLE_ARRAY) const;
2025-10-01 12:22:08 -05:00
//--- Utilities
2025-10-01 12:22:08 -05:00
void Compact(); // Remove invalid items
bool Swap(int index1, int index2); // Swap items
void Reverse(); // Reverse order
virtual void Summary(); // Info
//--- Operators
#ifdef CMANAGERBASE_STRICT_MODE
2026-03-15 15:43:37 -05:00
2025-10-01 12:22:08 -05:00
T* operator[](const int index) { return InRange(index, FUNCION_ACTUAL) ? items[index] : NULL; }
#else
T* operator[](const int index) { return items[index]; }
#endif
};
2025-10-01 12:22:08 -05:00
//+------------------------------------------------------------------+
2026-03-16 21:29:03 -05:00
//| Constructor and Destructor |
//+------------------------------------------------------------------+
template<typename T, typename P> CManagerBasesSimple::CManagerBasesSimple() : total(0)
{
ArrayResize(items, 0);
this.clean_in_destructor = true;
2025-10-01 12:22:08 -05:00
}
2026-03-16 21:29:03 -05:00
//+------------------------------------------------------------------+
template<typename T, typename P> CManagerBasesSimple::~CManagerBasesSimple()
{
if(clean_in_destructor)
CleanItems(true);
else
{
ArrayResize(items, 0);
}
}
2026-03-15 15:43:37 -05:00
//+------------------------------------------------------------------+
//| Function to check if an index is in the range [0 - (total-1)] |
//+------------------------------------------------------------------+
template<typename T, typename P>
inline bool CManagerBasesSimple::InRange(const int index, const string& function_name) const
{
if(index < total && index >= 0)
return true;
else
2025-10-01 12:22:08 -05:00
{
LogError(StringFormat("Index %d is invalid, array range [ %d - %d ]", index, total - 1), function_name);
2025-10-01 12:22:08 -05:00
return false;
}
2026-03-15 15:43:37 -05:00
}
2025-10-01 12:22:08 -05:00
//+------------------------------------------------------------------+
//| Functions for (add - insert - replace) elements |
//| (without calling virtual functions, class-specific use) |
//+------------------------------------------------------------------+
template<typename T, typename P>
void CManagerBasesSimple::RemoveInternal(int index)
{
2025-10-01 12:22:08 -05:00
//---
2025-10-01 12:22:08 -05:00
for(int i = index; i < total - 1; i++)
items[i] = items[i + 1];
2025-10-01 12:22:08 -05:00
//---
2025-10-01 12:22:08 -05:00
total--;
ArrayResize(items, total);
}
2025-10-01 12:22:08 -05:00
//+------------------------------------------------------------------+
template<typename T, typename P>
2026-03-15 15:43:37 -05:00
inline void CManagerBasesSimple::AddItemInternal(T* item)
2025-10-01 12:22:08 -05:00
2026-03-15 15:43:37 -05:00
{
2025-10-01 12:22:08 -05:00
ArrayResize(items, total + 1);
items[total] = item;
total++;
}
2025-10-01 12:22:08 -05:00
//+------------------------------------------------------------------+
template<typename T, typename P>
2025-10-01 12:22:08 -05:00
inline void CManagerBasesSimple::ReplaceInternal(int index, T* new_item)
{
items[index] = new_item;
2026-03-15 15:43:37 -05:00
2025-10-01 12:22:08 -05:00
}
//+------------------------------------------------------------------+
template<typename T, typename P>
2025-10-01 12:22:08 -05:00
void CManagerBasesSimple::InsertInternal(T* item, int index)
{
//---
2025-10-01 12:22:08 -05:00
ArrayResize(items, total + 1);
//---
2025-10-01 12:22:08 -05:00
for(int i = total; i > index; i--)
items[i] = items[i - 1];
//---
2025-10-01 12:22:08 -05:00
items[index] = item;
2025-10-01 12:22:08 -05:00
total++;
}
//+------------------------------------------------------------------+
//| Functions to (add - insert - replace) items |
//| (public with calls to virtual functions) |
//+------------------------------------------------------------------+
template<typename T, typename P>
bool CManagerBasesSimple::AddItem(T* item, bool check_duplicate)
2025-10-01 12:22:08 -05:00
{
//---
2025-10-01 12:22:08 -05:00
if(!CheckPointer(item))
{
LogError("Cannot add NULL item", FUNCION_ACTUAL);
2025-10-01 12:22:08 -05:00
return false;
}
2025-10-01 12:22:08 -05:00
//--- Only check if exists if requested
if(check_duplicate)
{
if(Exist(item) != -1)
2025-10-01 12:22:08 -05:00
{
LogWarning("Item not added, it's duplicated", FUNCION_ACTUAL);
return false;
}
}
2025-10-01 12:22:08 -05:00
//--- Resize
ArrayResize(items, total + 1);
items[total] = item;
//---
2025-10-01 12:22:08 -05:00
total++;
//---
OnNewElement((total - 1), MANAGER_BASE_ACTION_NEWEL_PUSH);
return true;
}
2025-10-01 12:22:08 -05:00
//+------------------------------------------------------------------+
template<typename T, typename P>
bool CManagerBasesSimple::AddItem(T& item, bool check_duplicate)
{
return AddItem(GetPointer(item), check_duplicate);
}
//+------------------------------------------------------------------+
template<typename T, typename P>
inline void CManagerBasesSimple::AddItemFast(T* item)
2026-03-15 15:43:37 -05:00
{
2025-10-01 12:22:08 -05:00
items[ArrayResize(items, (++total)) - 1] = item;
OnNewElement((total - 1), MANAGER_BASE_ACTION_NEWEL_PUSH);
}
2025-10-01 12:22:08 -05:00
//+------------------------------------------------------------------+
2025-10-01 12:22:08 -05:00
template<typename T, typename P>
bool CManagerBasesSimple::Insert(T* item, int index)
{
//---
2026-03-15 15:43:37 -05:00
if(!CheckPointer(item))
{
2025-10-01 12:22:08 -05:00
LogError("Cannot insert NULL item", FUNCION_ACTUAL);
return false;
}
2025-10-01 12:22:08 -05:00
//---
2026-03-15 15:43:37 -05:00
2025-10-01 12:22:08 -05:00
#ifdef CMANAGERBASE_STRICT_MODE
if(!InRange(index, FUNCION_ACTUAL))
return false;
2025-10-01 12:22:08 -05:00
#endif
//---
ArrayResize(items, total + 1);
//--- Move elements to the right
2025-10-01 12:22:08 -05:00
for(int i = total; i > index; i--)
items[i] = items[i - 1];
//---
2026-02-01 16:17:01 -05:00
items[index] = item;
2025-10-01 12:22:08 -05:00
total++;
//---
OnNewElement(index, MANAGER_BASE_ACTION_NEWEL_INSERT);
2025-11-23 08:05:40 -05:00
//--- Log
LogInfo(StringFormat("Item inserted at index %d. Total: %d", index, total), FUNCION_ACTUAL);
2025-11-23 08:05:40 -05:00
return true;
}
//+------------------------------------------------------------------+
template<typename T, typename P>
bool CManagerBasesSimple::Insert(T& item, int index)
2025-10-01 12:22:08 -05:00
{
return Insert(::GetPointer(item), index);
}
2025-10-01 12:22:08 -05:00
2025-10-01 12:22:08 -05:00
//+------------------------------------------------------------------+
template<typename T, typename P>
void CManagerBasesSimple::Assign(T* &data[], bool delete_prev)
{
CleanItems(delete_prev); // limpia con o sin delete
total = ArrayResize(items, ArraySize(data));
for(int i = 0; i < total; i++)
{
items[i] = data[i];
OnNewElement(i, MANAGER_BASE_ACTION_NEWEL_PUSH);
}
}
2025-11-23 08:05:40 -05:00
//+------------------------------------------------------------------+
2025-11-23 08:05:40 -05:00
template<typename T, typename P>
template<typename TClass>
bool CManagerBasesSimple::CopyTo(TClass* obj, int start = 0, int count = WHOLE_ARRAY) const
2026-03-16 21:29:03 -05:00
{
T* arr[];
if(!GetData(arr, start, count))
return false;
obj.Assign(arr, false); // false = no delete, son punteros compartidos
return true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
template<typename T, typename P>
bool CManagerBasesSimple::Delete(int index)
{
//---
#ifdef CMANAGERBASE_STRICT_MODE
if(!InRange(index, FUNCION_ACTUAL))
return false;
#endif
//---
OnDeleteElement(index, items[index], MANAGER_BASE_ACTION_DEL_DELETE);
//---
if(CheckPointer(items[index]) == POINTER_DYNAMIC)
{
delete items[index];
2026-03-15 15:43:37 -05:00
items[index] = NULL;
return true;
}
else
{
LogError("The pointer was not removed because it is not dynamic or is invalid", FUNCION_ACTUAL);
return false;
}
}
//+------------------------------------------------------------------+
template<typename T, typename P>
bool CManagerBasesSimple::Remove(int index)
{
//---
#ifdef CMANAGERBASE_STRICT_MODE
if(!InRange(index, FUNCION_ACTUAL))
return false;
#endif
2026-03-15 15:43:37 -05:00
//---
OnDeleteElement(index, items[index], MANAGER_BASE_ACTION_DEL_REMOVE);
2026-03-16 21:29:03 -05:00
//---
for(int i = index; i < total - 1; i++)
items[i] = items[i + 1];
//---
total--;
ArrayResize(items, total);
//--- Log
LogInfo(StringFormat("Item removed from index %d. Total: %d", index, total), FUNCION_ACTUAL);
return true;
}
//+------------------------------------------------------------------+
template<typename T, typename P>
bool CManagerBasesSimple::Remove(T* item)
{
// Find already checks if the item is invalid, and if it returns -1, Remove handles it (if CMANAGERBASE_STRICT_MODE is enabled)
return Remove(Find(item));
}
2026-03-15 15:43:37 -05:00
2026-03-15 15:43:37 -05:00
//+------------------------------------------------------------------+
template<typename T, typename P>
bool CManagerBasesSimple::Replace(int index, T* new_item)
{
#ifdef CMANAGERBASE_STRICT_MODE
if(!InRange(index, FUNCION_ACTUAL))
2025-10-01 12:22:08 -05:00
return false;
#endif
//---
if(!CheckPointer(new_item))
{
LogError("Cannot replace with NULL item", FUNCION_ACTUAL);
return false;
}
//---
OnDeleteElement(index, items[index], MANAGER_BASE_ACTION_DEL_REMOVE);
items[index] = new_item;
OnNewElement(index, MANAGER_BASE_ACTION_NEWEL_REPLACE);
//---
LogInfo(StringFormat("Item replaced at index %d", index), FUNCION_ACTUAL);
return true;
}
//+------------------------------------------------------------------+
template<typename T, typename P>
T* CManagerBasesSimple::RemoveLastAndReturn()
{
//---
if(total == 0)
{
LogWarning("No items to RemoveLastAndReturn", FUNCION_ACTUAL);
return NULL;
}
//---
const int last_index = total - 1;
T* last_item = items[last_index];
OnDeleteElement(last_index, last_item, MANAGER_BASE_ACTION_DEL_REMOVE_LAST);
2025-10-01 12:22:08 -05:00
//---
total--;
ArrayResize(items, total);
//---
LogInfo(StringFormat("RemoveLastAndReturn executed. Total: %d", total), FUNCION_ACTUAL);
return last_item;
2026-01-28 12:07:29 -05:00
}
2025-10-01 12:22:08 -05:00
2026-01-29 08:19:08 -05:00
2025-10-01 12:22:08 -05:00
//+------------------------------------------------------------------+
2026-01-29 08:19:08 -05:00
template<typename T, typename P>
2025-10-01 12:22:08 -05:00
2026-01-29 08:19:08 -05:00
bool CManagerBasesSimple::RemoveFirst()
2026-02-14 17:00:45 -05:00
{
2026-01-29 08:19:08 -05:00
return Remove(0);
2025-10-01 12:22:08 -05:00
2025-12-21 12:41:35 -05:00
}
2026-01-29 08:19:08 -05:00
//+------------------------------------------------------------------+
2025-10-01 12:22:08 -05:00
template<typename T, typename P>
2026-01-29 08:19:08 -05:00
bool CManagerBasesSimple::RemoveLast()
2025-10-01 12:22:08 -05:00
{
return Remove(total - 1);
}
//+------------------------------------------------------------------+
2026-01-28 12:07:29 -05:00
2025-10-01 12:22:08 -05:00
template<typename T, typename P>
2026-01-28 12:07:29 -05:00
2025-10-01 12:22:08 -05:00
int CManagerBasesSimple::Find(T* item)
{
//---
if(!CheckPointer(item))
{
2026-01-28 12:07:29 -05:00
2025-10-01 12:22:08 -05:00
LogError("Cannot find index of item*, its pointer is invalid", FUNCION_ACTUAL);
return -1;
2026-01-28 12:07:29 -05:00
}
2025-10-01 12:22:08 -05:00
2026-01-29 08:19:08 -05:00
//---
2026-02-01 16:17:01 -05:00
for(int i = 0; i < total; i++)
if(items[i] == item)
2026-01-29 08:19:08 -05:00
return i;
//---
LogWarning("Index not found for item pointer", FUNCION_ACTUAL);
2026-02-01 16:17:01 -05:00
2026-01-29 08:19:08 -05:00
return -1;
}
2026-02-01 16:17:01 -05:00
//+------------------------------------------------------------------+
2025-10-01 12:22:08 -05:00
template<typename T, typename P>
int CManagerBasesSimple::Exist(T* item)
{
2025-12-21 12:41:35 -05:00
//---
2025-10-01 12:22:08 -05:00
if(!CheckPointer(item))
{
LogError("Pointer is invalid", FUNCION_ACTUAL);
2026-01-28 12:07:29 -05:00
return -1;
2025-10-01 12:22:08 -05:00
}
//---
2025-12-21 12:41:35 -05:00
2025-10-01 12:22:08 -05:00
const void* const ptr1 = item;
for(int i = 0; i < total; i++)
2026-01-29 08:19:08 -05:00
{
const void* const ptr2 = items[i];
if(ptr2 == ptr1)
return i;
}
2026-03-05 12:10:29 -05:00
2026-01-29 08:19:08 -05:00
//---
return -1;
}
//+------------------------------------------------------------------+
template<typename T, typename P>
T* CManagerBasesSimple::GetFirst()
{
if(total > 0)
return items[0];
2026-02-14 17:00:45 -05:00
LogWarning("Total size of items is less than 1, there is no first item, will return NULL", FUNCION_ACTUAL);
return NULL;
}
//+------------------------------------------------------------------+
template<typename T, typename P>
T* CManagerBasesSimple::GetLast()
{
if(total > 0)
return items[total - 1];
LogWarning("Total size of items is less than 1, there is no last item, will return NULL", FUNCION_ACTUAL);
return NULL;
}
//+------------------------------------------------------------------+
template<typename T, typename P>
2026-01-29 08:19:08 -05:00
int CManagerBasesSimple::GetActiveCount()
2025-10-01 12:22:08 -05:00
{
int count = 0;
2026-01-28 12:07:29 -05:00
for(int i = 0; i < total; i++)
2025-10-01 12:22:08 -05:00
if(CheckPointer(items[i]) != POINTER_INVALID)
2026-01-28 12:07:29 -05:00
2025-10-01 12:22:08 -05:00
count++;
2026-01-28 12:07:29 -05:00
2025-10-01 12:22:08 -05:00
//---
return count;
}
//+------------------------------------------------------------------+
template<typename T, typename P>
void CManagerBasesSimple::Compact()
{
//---
int write_pos = 0;
2026-01-28 12:07:29 -05:00
2025-10-01 12:22:08 -05:00
2026-01-28 12:07:29 -05:00
2025-10-01 12:22:08 -05:00
//---
for(int i = 0; i < total; i++)
{
if(CheckPointer(items[i]) != POINTER_INVALID)
2026-01-28 12:07:29 -05:00
2025-10-01 12:22:08 -05:00
{
2026-01-28 12:07:29 -05:00
2025-10-01 12:22:08 -05:00
if(write_pos != i)
items[write_pos] = items[i];
write_pos++;
}