From 42fa547cb47fdadaa15385a81265ea0637ea9f12 Mon Sep 17 00:00:00 2001 From: Arkshine Date: Fri, 25 Jul 2014 00:25:31 +0200 Subject: [PATCH] Improve CellArray. --- amxmodx/amxmodx.h | 6 +- amxmodx/datastructs.cpp | 1011 +++++++++++++++++-------------- amxmodx/datastructs.h | 427 ++++--------- amxmodx/meta_api.cpp | 2 +- amxmodx/sorting.cpp | 16 +- amxmodx/string.cpp | 6 +- amxmodx/util.cpp | 12 +- plugins/include/cellarray.inc | 306 +++++++--- plugins/testsuite/arraytest.sma | 679 ++++++++++++--------- 9 files changed, 1299 insertions(+), 1166 deletions(-) diff --git a/amxmodx/amxmodx.h b/amxmodx/amxmodx.h index 331e433c..326a640e 100755 --- a/amxmodx/amxmodx.h +++ b/amxmodx/amxmodx.h @@ -147,8 +147,8 @@ void UTIL_ShowMenu(edict_t* pEntity, int slots, int time, char *menu, int mlen); void UTIL_ClientSayText(edict_t *pEntity, int sender, char *msg); void UTIL_TeamInfo(edict_t *pEntity, int playerIndex, const char *pszTeamName); -template -int UTIL_CheckValidChar(D *c); +template int UTIL_CheckValidChar(D *c); +template unsigned int strncopy(D *dest, const char *src, size_t count); unsigned int UTIL_GetUTF8CharBytes(const char *stream); unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace, bool caseSensitive); char *UTIL_ReplaceEx(char *subject, size_t maxLen, const char *search, size_t searchLen, const char *replace, size_t replaceLen, bool caseSensitive); @@ -300,7 +300,7 @@ int amxstring_len(cell* cstr); int load_amxscript(AMX* amx, void** program, const char* path, char error[64], int debug); int set_amxnatives(AMX* amx, char error[64]); int set_amxstring(AMX *amx, cell amx_addr, const char *source, int max); -int set_amxstring_utf8(AMX *amx, cell amx_addr, const char *source, size_t sourcelen, size_t maxlen); +template int set_amxstring_utf8(AMX *amx, cell amx_addr, const T *source, size_t sourcelen, size_t maxlen); int unload_amxscript(AMX* amx, void** program); void copy_amxmemory(cell* dest, cell* src, int len); diff --git a/amxmodx/datastructs.cpp b/amxmodx/datastructs.cpp index cf14a477..97595734 100644 --- a/amxmodx/datastructs.cpp +++ b/amxmodx/datastructs.cpp @@ -30,13 +30,13 @@ #include "amxmodx.h" #include "datastructs.h" - +#include // Note: All handles start at 1. 0 and below are invalid handles. // This way, a plugin that doesn't initialize a vector or // string will not be able to modify another plugin's data // on accident. -CVector VectorHolder; +ke::Vector VectorHolder; // Array:ArrayCreate(cellsize=1, reserved=32); @@ -44,19 +44,19 @@ static cell AMX_NATIVE_CALL ArrayCreate(AMX* amx, cell* params) { // params[1] (cellsize) is how big in cells each element is. // this MUST be greater than 0! - int cellsize=params[1]; + int cellsize = params[1]; // params[2] (reserved) is how many elements to allocate // immediately when the list is created. // this MUST be greater than 0! - int reserved=params[2]; + int reserved = params[2]; - if (cellsize<=0) + if (cellsize <= 0) { LogError(amx, AMX_ERR_NATIVE, "Invalid array size (%d)", cellsize); return -1; } - if (reserved<=0) + if (reserved <= 0) { LogError(amx, AMX_ERR_NATIVE, "Invalid reserved size (%d)", reserved); return -1; @@ -64,64 +64,64 @@ static cell AMX_NATIVE_CALL ArrayCreate(AMX* amx, cell* params) // Scan through the vector list to see if any are NULL. // NULL means the vector was previously destroyed. - for (unsigned int i=0; i < VectorHolder.size(); ++i) + for (unsigned int i=0; i < VectorHolder.length(); ++i) { if (VectorHolder[i]==NULL) { - VectorHolder[i]=new CellVector(cellsize); - VectorHolder[i]->Grow(reserved); + VectorHolder[i] = new CellArray(cellsize); + //VectorHolder[i]->resize(reserved); return i + 1; } } // None are NULL, create a new vector - CellVector* NewVector=new CellVector(cellsize); - NewVector->Grow(reserved); + CellArray* NewVector = new CellArray(cellsize); + //NewVector->resize(reserved); - VectorHolder.push_back(NewVector); + VectorHolder.append(NewVector); - return VectorHolder.size(); + return VectorHolder.length(); } -// ArrayClear(Array:which) +// native ArrayClear(Array:which); static cell AMX_NATIVE_CALL ArrayClear(AMX* amx, cell* params) { - CellVector* vec=HandleToVector(amx, params[1]); - - if (vec==NULL) - { - return 0; - } - - vec->Clear(); - - return 1; -} - -// ArraySize(Array:which) -static cell AMX_NATIVE_CALL ArraySize(AMX* amx, cell* params) -{ - CellVector* vec=HandleToVector(amx, params[1]); - - if (vec==NULL) - { - return 0; - } - - return vec->Size(); -} - -// ArrayResize(Array:which, newsize); -static cell AMX_NATIVE_CALL ArrayResize(AMX* amx, cell* params) -{ - CellVector* vec = HandleToVector(amx, params[1]); + CellArray* vec = HandleToVector(amx, params[1]); if (vec == NULL) { return 0; } - if (!vec->Resize(params[2])) + vec->clear(); + + return 1; +} + +// native ArraySize(Array:which); +static cell AMX_NATIVE_CALL ArraySize(AMX* amx, cell* params) +{ + CellArray* vec = HandleToVector(amx, params[1]); + + if (vec == NULL) + { + return 0; + } + + return vec->size(); +} + +// native bool:ArrayResize(Array:which, newsize); +static cell AMX_NATIVE_CALL ArrayResize(AMX* amx, cell* params) +{ + CellArray* vec = HandleToVector(amx, params[1]); + + if (vec == NULL) + { + return 0; + } + + if (!vec->resize(params[2])) { LogError(amx, AMX_ERR_NATIVE, "Unable to resize array to \"%u\"", params[2]); return 0; @@ -130,21 +130,21 @@ static cell AMX_NATIVE_CALL ArrayResize(AMX* amx, cell* params) return 1; } -// ArrayClone(Array:which) +// native Array:ArrayClone(Array:which); static cell AMX_NATIVE_CALL ArrayClone(AMX* amx, cell* params) { - CellVector* vec = HandleToVector(amx, params[1]); + CellArray* vec = HandleToVector(amx, params[1]); if (vec == NULL) { return 0; } - CellVector *clonevec = vec->Clone(); + CellArray *clonevec = vec->clone(); // Scan through the vector list to see if any are NULL. // NULL means the vector was previously destroyed. - for (unsigned int i = 0; i < VectorHolder.size(); ++i) + for (unsigned int i = 0; i < VectorHolder.length(); ++i) { if (VectorHolder[i] == NULL) { @@ -153,172 +153,310 @@ static cell AMX_NATIVE_CALL ArrayClone(AMX* amx, cell* params) } } - VectorHolder.push_back(clonevec); + VectorHolder.append(clonevec); - return VectorHolder.size(); + return VectorHolder.length(); } -// ArrayGetArray(Array:which, item, any:output[]); +// native ArrayGetArray(Array:which, item, any:output[], size = -1); static cell AMX_NATIVE_CALL ArrayGetArray(AMX* amx, cell* params) { - CellVector* vec=HandleToVector(amx, params[1]); - - if (vec==NULL) - { - return 0; - } - - if (vec->GetArray(params[2],get_amxaddr(amx, params[3]))!=1) - { - LogError(amx, AMX_ERR_NATIVE, "Invalid cellvector handle provided (%d:%d:%d)", params[1], params[2], vec->Size()); - return 0; - } - - return 1; -} -// ArrayGetCell(Array:which, item, any:&output); -static cell AMX_NATIVE_CALL ArrayGetCell(AMX* amx, cell* params) -{ - CellVector* vec=HandleToVector(amx, params[1]); - - if (vec==NULL) - { - return 0; - } - - cell ret; - if (vec->GetCell(params[2],&ret)!=1) - { - LogError(amx, AMX_ERR_NATIVE, "Invalid cellvector handle provided (%d:%d:%d)", params[1], params[2], vec->Size()); - return 0; - } - - return ret; -} -// ArrayGetString(Array:which, item, any:output[], size); -static cell AMX_NATIVE_CALL ArrayGetString(AMX* amx, cell* params) -{ - CellVector* vec=HandleToVector(amx, params[1]); - - if (vec==NULL) - { - return 0; - } - - if (vec->GetString(params[2],get_amxaddr(amx, params[3]),params[4])!=1) - { - LogError(amx, AMX_ERR_NATIVE, "Invalid cellvector handle provided (%d:%d:%d)", params[1], params[2], vec->Size()); - return 0; - } - - return 1; -} -// ArraySetArray(Array:which, item, any:output[]); -static cell AMX_NATIVE_CALL ArraySetArray(AMX* amx, cell* params) -{ - CellVector* vec=HandleToVector(amx, params[1]); - - if (vec==NULL) - { - return 0; - } - - if (vec->SetArray(params[2],get_amxaddr(amx, params[3]))!=1) - { - LogError(amx, AMX_ERR_NATIVE, "Invalid cellvector handle provided (%d:%d:%d)", params[1], params[2], vec->Size()); - return 0; - } - - return 1; -} -// ArraySetCell(Array:which, item, any:&output); -static cell AMX_NATIVE_CALL ArraySetCell(AMX* amx, cell* params) -{ - CellVector* vec=HandleToVector(amx, params[1]); - - if (vec==NULL) - { - return 0; - } - - if (vec->SetCell(params[2], params[3])!=1) - { - LogError(amx, AMX_ERR_NATIVE, "Invalid cellvector handle provided (%d:%d:%d)", params[1], params[2], vec->Size()); - return 0; - } - - return 1; -} -// ArraySetString(Array:which, item, any:output[]); -static cell AMX_NATIVE_CALL ArraySetString(AMX* amx, cell* params) -{ - CellVector* vec=HandleToVector(amx, params[1]); - - if (vec==NULL) - { - return 0; - } - - if (vec->SetString(params[2],get_amxaddr(amx, params[3]))!=1) - { - LogError(amx, AMX_ERR_NATIVE, "Invalid cellvector handle provided (%d:%d:%d)", params[1], params[2], vec->Size()); - return 0; - } - - return 1; -} -// ArrayPushArray(Array:which, any:output[]); -static cell AMX_NATIVE_CALL ArrayPushArray(AMX* amx, cell* params) -{ - CellVector* vec=HandleToVector(amx, params[1]); - - if (vec==NULL) - { - return 0; - } - - vec->SetArray(vec->Push(),get_amxaddr(amx, params[2])); - - return 1; -} -// ArrayPushCell(Array:which, &any:output); -static cell AMX_NATIVE_CALL ArrayPushCell(AMX* amx, cell* params) -{ - CellVector* vec=HandleToVector(amx, params[1]); - - if (vec==NULL) - { - return 0; - } - - vec->SetCell(vec->Push(), params[2]); - - return 1; -} -// ArrayPushString(Array:which, any:output[]); -static cell AMX_NATIVE_CALL ArrayPushString(AMX* amx, cell* params) -{ - CellVector* vec=HandleToVector(amx, params[1]); - - if (vec==NULL) - { - return 0; - } - - vec->SetString(vec->Push(),get_amxaddr(amx, params[2])); - - return 1; -} -static cell AMX_NATIVE_CALL ArrayGetStringHandle(AMX* amx, cell* params) -{ - CellVector* vec=HandleToVector(amx, params[1]); + CellArray* vec = HandleToVector(amx, params[1]); if (vec == NULL) { return 0; } - cell* ptr=vec->GetCellPointer(params[2]); + size_t idx = (size_t)params[2]; + + if (idx >= vec->size()) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid index %d (count: %d)", idx, vec->size()); + return 0; + } + + cell *blk = vec->at(idx); + size_t indexes = vec->blocksize(); + + if (params[4] != -1 && (size_t)params[4] <= vec->blocksize()) + { + indexes = params[4]; + } + + cell *addr = get_amxaddr(amx, params[3]); + + memcpy(addr, blk, sizeof(cell) * indexes); + + return 1; +} + +// native any:ArrayGetCell(Array:which, item, block = 0, bool:asChar = false); +static cell AMX_NATIVE_CALL ArrayGetCell(AMX* amx, cell* params) +{ + CellArray* vec = HandleToVector(amx, params[1]); + + if (vec == NULL) + { + return 0; + } + + size_t idx = (size_t)params[2]; + + if (idx >= vec->size()) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid index %d (count: %d)", idx, vec->size()); + return 0; + } + + cell *blk = vec->at(idx); + idx = (size_t)params[3]; + + if (!params[4]) + { + if (idx >= vec->blocksize()) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid block %d (blocksize: %d)", idx, vec->blocksize()); + return 0; + } + + return blk[idx]; + } + else + { + if (idx >= vec->blocksize() * 4) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid byte %d (blocksize: %d bytes)", idx, vec->blocksize() * 4); + return 0; + } + + return (cell)*((char *)blk + idx); + } + + return 0; +} + +// native ArrayGetString(Array:which, item, output[], size); +static cell AMX_NATIVE_CALL ArrayGetString(AMX* amx, cell* params) +{ + CellArray* vec = HandleToVector(amx, params[1]); + + if (vec == NULL) + { + return 0; + } + + size_t idx = (size_t)params[2]; + + if (idx >= vec->size()) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid index %d (count: %d)", idx, vec->size()); + return 0; + } + + cell *blk = vec->at(idx); + return set_amxstring_utf8(amx, params[3], blk, amxstring_len(blk), params[4] + 1); // + EOS. +} + +// native ArraySetArray(Array:which, item, const any:input[], size =-1); +static cell AMX_NATIVE_CALL ArraySetArray(AMX* amx, cell* params) +{ + CellArray* vec = HandleToVector(amx, params[1]); + + if (vec == NULL) + { + return 0; + } + + size_t idx = (size_t)params[2]; + + if (idx >= vec->size()) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid index %d (count: %d)", idx, vec->size()); + return 0; + } + + cell *blk = vec->at(idx); + size_t indexes = vec->blocksize(); + + if (params[4] != -1 && (size_t)params[4] <= vec->blocksize()) + { + indexes = params[4]; + } + + cell *addr = get_amxaddr(amx, params[3]); + + memcpy(blk, addr, sizeof(cell) * indexes); + + return 1; +} + +// native ArraySetCell(Array:which, item, any:input, block = 0, bool:asChar = false); +static cell AMX_NATIVE_CALL ArraySetCell(AMX* amx, cell* params) +{ + CellArray* vec = HandleToVector(amx, params[1]); + + if (vec == NULL) + { + return 0; + } + + size_t idx = (size_t)params[2]; + + if (idx >= vec->size()) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid index %d (count: %d)", idx, vec->size()); + return 0; + } + + cell *blk = vec->at(idx); + idx = (size_t)params[4]; + + if (params[5] == 0) + { + if (idx >= vec->blocksize()) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid block %d (blocksize: %d)", idx, vec->blocksize()); + return 0; + } + blk[idx] = params[3]; + } + else + { + if (idx >= vec->blocksize() * 4) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid byte %d (blocksize: %d bytes)", idx, vec->blocksize() * 4); + return 0; + } + *((char *)blk + idx) = (char)params[3]; + } + + return 1; +} + +// native ArraySetString(Array:which, item, const input[]); +static cell AMX_NATIVE_CALL ArraySetString(AMX* amx, cell* params) +{ + CellArray* vec = HandleToVector(amx, params[1]); + + if (vec == NULL) + { + return 0; + } + + size_t idx = (size_t)params[2]; + + if (idx >= vec->size()) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid index %d (count: %d)", idx, vec->size()); + return 0; + } + + cell *blk = vec->at(idx); + + int len; + char *str = get_amxstring(amx, params[3], 0, len); + + return strncopy(blk, str, ke::Min((size_t)len + 1, vec->blocksize())); +} + +// native ArrayPushArray(Array:which, const any:input[], size = -1); +static cell AMX_NATIVE_CALL ArrayPushArray(AMX* amx, cell* params) +{ + CellArray* vec = HandleToVector(amx, params[1]); + + if (vec == NULL) + { + return 0; + } + + cell *blk = vec->push(); + + if (!blk) + { + LogError(amx, AMX_ERR_NATIVE, "Failed to grow array"); + return 0; + } + + cell *addr = get_amxaddr(amx, params[2]); + size_t indexes = vec->blocksize(); + + if (params[3] != -1 && (size_t)params[3] <= vec->blocksize()) + { + indexes = params[3]; + } + + memcpy(blk, addr, sizeof(cell) * indexes); + + return static_cast((vec->size() - 1)); +} + +// native ArrayPushCell(Array:which, any:input); +static cell AMX_NATIVE_CALL ArrayPushCell(AMX* amx, cell* params) +{ + CellArray* vec = HandleToVector(amx, params[1]); + + if (vec == NULL) + { + return 0; + } + + cell *blk = vec->push(); + + if (!blk) + { + LogError(amx, AMX_ERR_NATIVE, "Failed to grow array"); + return 0; + } + + *blk = params[2]; + + return static_cast((vec->size() - 1)); +} + +// native ArrayPushString(Array:which, const input[]); +static cell AMX_NATIVE_CALL ArrayPushString(AMX* amx, cell* params) +{ + CellArray* vec = HandleToVector(amx, params[1]); + + if (vec == NULL) + { + return 0; + } + + cell *blk = vec->push(); + cell *start = blk; + if (!blk) + { + LogError(amx, AMX_ERR_NATIVE, "Failed to grow array"); + return 0; + } + + memcpy(blk, get_amxaddr(amx, params[2]), sizeof(cell) * vec->blocksize()); + blk[vec->blocksize() - 1]= '\0'; + + return static_cast((vec->size() - 1)); +} + +// native DoNotUse : ArrayGetStringHandle(Array : which, item); +static cell AMX_NATIVE_CALL ArrayGetStringHandle(AMX* amx, cell* params) +{ + CellArray* vec = HandleToVector(amx, params[1]); + + if (vec == NULL) + { + return 0; + } + + size_t idx = (size_t)params[2]; + + if (idx >= vec->size()) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid index %d (count: %d)", idx, vec->size()); + return 0; + } + + cell* ptr = vec->at(idx); if (ptr == NULL) { @@ -326,491 +464,444 @@ static cell AMX_NATIVE_CALL ArrayGetStringHandle(AMX* amx, cell* params) } return reinterpret_cast(ptr); - } -// ArrayInsertArrayAfter(Array:which, item, const value[]) + +// native ArrayInsertArrayAfter(Array:which, item, const any:input[]); static cell AMX_NATIVE_CALL ArrayInsertArrayAfter(AMX* amx, cell* params) { - CellVector* vec=HandleToVector(amx, params[1]); + CellArray* vec = HandleToVector(amx, params[1]); - if (!vec) + if (vec == NULL) { return 0; } - int item=params[2]+1; + size_t idx = params[2] + 1; - if (vec->ShiftUpFrom(item)!=1) + if (idx > vec->size()) { - LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertArrayAfter (%d:%d)", params[1], vec->Size()); + LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertArrayAfter (%d:%d)", idx, vec->size()); return 0; } - vec->SetArray(item, get_amxaddr(amx, params[3])); + cell *addr = get_amxaddr(amx, params[3]); + + memcpy(vec->insert_at(idx), addr, sizeof(cell) * vec->blocksize()); return 1; } -// ArrayInsertCellAfter(Array:which, item, value[]) + +// native ArrayInsertCellAfter(Array:which, item, any:input); static cell AMX_NATIVE_CALL ArrayInsertCellAfter(AMX* amx, cell* params) { - CellVector* vec=HandleToVector(amx, params[1]); + CellArray* vec = HandleToVector(amx, params[1]); - if (!vec) + if (vec == NULL) { return 0; } - int item=params[2]+1; + size_t idx = params[2] + 1; - if (vec->ShiftUpFrom(item)!=1) + if (idx > vec->size()) { - LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertCellAfter (%d:%d)", params[1], vec->Size()); + LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertCellAfter (%d:%d)", idx, vec->size()); return 0; } - vec->SetCell(item, params[3]); + *vec->insert_at(idx) = params[3]; return 1; } -// ArrayInsertStringAfter(Array:which, item, const value[]) + +// native ArrayInsertStringAfter(Array:which, item, const input[]); static cell AMX_NATIVE_CALL ArrayInsertStringAfter(AMX* amx, cell* params) { - CellVector* vec=HandleToVector(amx, params[1]); + CellArray* vec = HandleToVector(amx, params[1]); - if (!vec) + if (vec == NULL) { return 0; } - int item=params[2]+1; + size_t idx = params[2] + 1; - if (vec->ShiftUpFrom(item)!=1) + if (idx > vec->size()) { - LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertStringAfter (%d:%d)", params[1], vec->Size()); + LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertStringAfter (%d:%d)", idx, vec->size()); return 0; } - vec->SetString(item, get_amxaddr(amx, params[3])); + int len; + const char *str = get_amxstring(amx, params[3], 0, len); - return 1; + return strncopy(vec->insert_at(idx), str, ke::Min((size_t)len + 1, vec->blocksize())); } -// ArrayInsertArrayBefore(Array:which, item, const value[]) + +// native ArrayInsertArrayBefore(Array:which, item, const any:input[]); static cell AMX_NATIVE_CALL ArrayInsertArrayBefore(AMX* amx, cell* params) { - CellVector* vec=HandleToVector(amx, params[1]); + CellArray* vec = HandleToVector(amx, params[1]); - if (!vec) + if (vec == NULL) { return 0; } - int item=params[2]; + size_t idx = params[2]; - if (item==vec->Size()) + if (idx >= vec->size()) { - LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertArrayBefore (%d:%d)", params[2], vec->Size()); - return 0; - } - if (vec->ShiftUpFrom(item)!=1) - { - LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertArrayBefore (%d:%d)", params[2], vec->Size()); + LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertArrayBefore (%d:%d)", idx, vec->size()); return 0; } - vec->SetArray(item, get_amxaddr(amx, params[3])); + cell *addr = get_amxaddr(amx, params[3]); + + memcpy(vec->insert_at(idx), addr, vec->blocksize() * sizeof(cell)); return 1; } -// ArrayInsertCellBefore(Array:which, item, const value) + +// native ArrayInsertCellBefore(Array:which, item, const any:input); static cell AMX_NATIVE_CALL ArrayInsertCellBefore(AMX* amx, cell* params) { - CellVector* vec=HandleToVector(amx, params[1]); + CellArray* vec = HandleToVector(amx, params[1]); - if (!vec) + if (vec == NULL) { return 0; } - int item=params[2]; + size_t idx = params[2]; - if (item==vec->Size()) + if (idx >= vec->size()) { - LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertCellBefore (%d:%d)", params[2], vec->Size()); - return 0; - } - if (vec->ShiftUpFrom(item)!=1) - { - LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertCellBefore (%d:%d)", params[2], vec->Size()); + LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertCellBefore (%d:%d)", idx, vec->size()); return 0; } - vec->SetCell(item, params[3]); + *vec->insert_at(idx) = params[3]; return 1; } -// ArrayInsertStringBefore(Array:which, item, const value[]) +// native ArrayInsertStringBefore(Array:which, item, const input[]); static cell AMX_NATIVE_CALL ArrayInsertStringBefore(AMX* amx, cell* params) { - CellVector* vec=HandleToVector(amx, params[1]); + CellArray* vec = HandleToVector(amx, params[1]); - if (!vec) + if (vec == NULL) { return 0; } - int item=params[2]; + size_t idx = params[2]; - if (item==vec->Size()) + if (idx >= vec->size()) { - LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertStringBefore (%d:%d)", params[2], vec->Size()); - return 0; - } - if (vec->ShiftUpFrom(item)!=1) - { - LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertStringBefore (%d:%d)", params[2], vec->Size()); + LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertStringBefore (%d:%d)", idx, vec->size()); return 0; } - vec->SetString(item, get_amxaddr(amx, params[3])); + int len; + const char *str = get_amxstring(amx, params[3], 0, len); - return 1; + return strncopy(vec->insert_at(idx), str, ke::Min((size_t)len + 1, vec->blocksize())); } -// ArraySwap(Array:which, item1, item2) +// native ArraySwap(Array:which, item1, item2); static cell AMX_NATIVE_CALL ArraySwap(AMX* amx, cell* params) { - CellVector* vec=HandleToVector(amx, params[1]); + CellArray* vec = HandleToVector(amx, params[1]); + + if (vec == NULL) + { + return 0; + } - if (!vec) + size_t idx1 = (size_t)params[2]; + size_t idx2 = (size_t)params[3]; + + if (idx1 >= vec->size()) { + LogError(amx, AMX_ERR_NATIVE, "Invalid index %d (count: %d)", idx1, vec->size()); return 0; } - if (vec->Swap(params[2], params[3])!=1) + + if (idx2 >= vec->size()) { - LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArraySwap (%d , %d:%d)",params[2], params[3], vec->Size()); + LogError(amx, AMX_ERR_NATIVE, "Invalid index %d (count: %d)", idx2, vec->size()); return 0; } + vec->swap(params[2], params[3]); + return 1; } -// ArrayDeleteItem(Array:which, item); +// native ArrayDeleteItem(Array:which, item); static cell AMX_NATIVE_CALL ArrayDeleteItem(AMX* amx, cell* params) { - CellVector* vec=HandleToVector(amx, params[1]); + CellArray* vec = HandleToVector(amx, params[1]); - if (!vec) + if (vec == NULL) { return 0; } - if (vec->Delete(params[2])!=1) + size_t idx = (size_t)params[2]; + + if (idx >= vec->size()) { - LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayDeleteItem (%d:%d)", params[2], vec->Size()); + LogError(amx, AMX_ERR_NATIVE, "Invalid index %d (count: %d)", idx, vec->size()); return 0; } + + vec->remove(idx); + return 1; } - -// ArrayDestroy(Array:&which) +// native ArrayDestroy(&Array:which); static cell AMX_NATIVE_CALL ArrayDestroy(AMX* amx, cell* params) { - // byref the handle here so we can zero it out after destroying - // this way they cannot accidentally reuse it - cell* handle=get_amxaddr(amx,params[1]); - CellVector* vec=HandleToVector(amx, *handle); + cell* handle = get_amxaddr(amx, params[1]); + CellArray* vec = HandleToVector(amx, *handle); - if (!vec) + if (vec == NULL) { return 0; } delete vec; - VectorHolder[*handle-1]=NULL; + VectorHolder[*handle-1] = NULL; - *handle=0; + *handle = 0; return 1; } -typedef struct ArraySort_s + +struct ArraySort_s { - int handle; - int forward; - cell data; - cell size; - CellVector *vec; - AMX* amx; - cell addr1; - cell addr2; + int func; + cell array_hndl; + cell* array_base; + cell array_bsize; + cell data; + cell size; + cell addr1; + cell addr2; + AMX *amx; +}; -} ArraySort_t; +ArraySort_s SortInfo; -static CStack ArraySortStack; - -int SortArrayList(const void *itema, const void *itemb) +int SortArrayList(const void *elem1, const void *elem2) { - ArraySort_t *Info = ArraySortStack.front(); - - return executeForwards(Info->forward, Info->handle, *((int *)itema), *((int *)itemb), Info->data, Info->size); - + return executeForwards( + SortInfo.func, + SortInfo.array_hndl, + ((cell)((cell *)elem1 - SortInfo.array_base)) / SortInfo.array_bsize, + ((cell)((cell *)elem2 - SortInfo.array_base)) / SortInfo.array_bsize, + SortInfo.data, + SortInfo.size + ); } + // native ArraySort(Array:array, const comparefunc[], data[]="", data_size=0); static cell AMX_NATIVE_CALL ArraySort(AMX* amx, cell* params) { - int handle=params[1]; - CellVector* vec=HandleToVector(amx, handle); + int handle = params[1]; + CellArray* vec = HandleToVector(amx, handle); - if (!vec) + if (vec == NULL) { return 0; } - - // This is kind of a cheating way to go about this but... - // Create an array of integers as big as however many elements are in the vector. - // Pass that array to qsort - // After the array is sorted out, then create a NEW cellvector - // and copy in the old data in the order of what was sorted + int len; - char* FuncName=get_amxstring(amx, params[2], 0, len); + char* funcName = get_amxstring(amx, params[2], 0, len); + // MySortFunc(Array:array, item1, item2, const data[], data_size) - int Forward = registerSPForwardByName(amx, FuncName, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_DONE); - if (Forward < 0) + int func = registerSPForwardByName(amx, funcName, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_DONE); + if (func < 0) { - LogError(amx, AMX_ERR_NATIVE, "The public function \"%s\" was not found.", FuncName); + LogError(amx, AMX_ERR_NATIVE, "The public function \"%s\" was not found.", funcName); return 0; - } - - int *IntList=new int[vec->Size()]; - - for (int i=0; i< vec->Size(); i++) - { - IntList[i]=i; } - ArraySort_t *Info=new ArraySort_t; + size_t arraysize = vec->size(); + size_t blocksize = vec->blocksize(); + cell *array = vec->base(); - Info->handle=handle; - Info->forward=Forward; - Info->data=params[3]; - Info->size=params[4]; + ArraySort_s oldinfo = SortInfo; - ArraySortStack.push(Info); - qsort(IntList, vec->Size(), sizeof(int), SortArrayList); - ArraySortStack.pop(); + SortInfo.func = func; + SortInfo.array_base = array; + SortInfo.array_bsize = static_cast(blocksize); + SortInfo.array_hndl = handle; + SortInfo.data = params[3]; + SortInfo.size = params[4]; - CellVector* newvec=new CellVector(vec->GetCellCount()); + qsort(array, arraysize, blocksize * sizeof(cell), SortArrayList); - // Set the new vector's values - for (int i=0; i< vec->Size(); i++) - { - if (newvec->SetArray(newvec->Push(), vec->GetCellPointer(IntList[i]))!=1) - { - // This should never happen.. - LogError(amx, AMX_ERR_NATIVE, "Failed to SetArray in ArraySort (i=%d, IntList=%d)",i,IntList[i]); + SortInfo = oldinfo; - return 0; - } - } - - // Delete the old vector - delete vec; - - // Now save the new vector in its handle location - VectorHolder[handle-1]=newvec; - - // Cleanup - delete Info; - delete IntList; - - unregisterSPForward(Forward); + unregisterSPForward(func); return 1; } -int SortArrayListExCell(const void *itema, const void *itemb) +int SortArrayListExCell(const void *elem1, const void *elem2) { - ArraySort_t *Info = ArraySortStack.front(); - cell vala = 0, valb = 0; + size_t index1 = ((cell)((cell *)elem1 - SortInfo.array_base)) / SortInfo.array_bsize; + size_t index2 = ((cell)((cell *)elem2 - SortInfo.array_base)) / SortInfo.array_bsize; - Info->vec->GetCell(*((int *)itema), &vala); - Info->vec->GetCell(*((int *)itemb), &valb); - - return executeForwards(Info->forward, Info->handle, vala, valb, Info->data, Info->size); + return executeForwards( + SortInfo.func, + SortInfo.array_hndl, + *&SortInfo.array_base[index1 * SortInfo.array_bsize], + *&SortInfo.array_base[index2 * SortInfo.array_bsize], + SortInfo.data, + SortInfo.size + ); } -int SortArrayListExArray(const void *itema, const void *itemb) +int SortArrayListExArray(const void *elem1, const void *elem2) { - ArraySort_t *Info = ArraySortStack.front(); + size_t index1 = ((cell)((cell *)elem1 - SortInfo.array_base)) / SortInfo.array_bsize; + size_t index2 = ((cell)((cell *)elem2 - SortInfo.array_base)) / SortInfo.array_bsize; - Info->vec->GetArray(*((int *)itema), get_amxaddr(Info->amx, Info->addr1)); - Info->vec->GetArray(*((int *)itemb), get_amxaddr(Info->amx, Info->addr2)); + cell *addr1 = get_amxaddr(SortInfo.amx, SortInfo.addr1); + cell *addr2 = get_amxaddr(SortInfo.amx, SortInfo.addr2); - return executeForwards(Info->forward, Info->handle, Info->addr1, Info->addr2, Info->data, Info->size); + memcpy(addr1, &SortInfo.array_base[index1 * SortInfo.array_bsize], SortInfo.array_bsize * sizeof(cell)); + memcpy(addr2, &SortInfo.array_base[index2 * SortInfo.array_bsize], SortInfo.array_bsize * sizeof(cell)); + + return executeForwards( + SortInfo.func, + SortInfo.array_hndl, + SortInfo.addr1, + SortInfo.addr2, + SortInfo.data, + SortInfo.size + ); } // native ArraySortEx(Array:array, const comparefunc[], data[]="", data_size=0); static cell AMX_NATIVE_CALL ArraySortEx(AMX* amx, cell* params) { - int handle=params[1]; - CellVector* vec=HandleToVector(amx, handle); + int handle = params[1]; + CellArray* vec = HandleToVector(amx, handle); - if (!vec) + if (vec == NULL) { return 0; } - cell amx_addr1 = 0, amx_addr2 = 0, *phys_addr = NULL; - size_t cellcount = vec->GetCellCount(); + int len; + char* funcName = get_amxstring(amx, params[2], 0, len); - if (cellcount > 1) + int func = registerSPForwardByName(amx, funcName, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_DONE); + + if (!func) + { + LogError(amx, AMX_ERR_NATIVE, "The public function \"%s\" was not found.", funcName); + return 0; + } + + size_t arraysize = vec->size(); + size_t blocksize = vec->blocksize(); + cell *array = vec->base(); + cell amx_addr1 = 0, amx_addr2 = 0, *phys_addr = NULL; + + if (blocksize > 1) { int err; - if ((err=amx_Allot(amx, cellcount, &amx_addr1, &phys_addr)) != AMX_ERR_NONE - || (err=amx_Allot(amx, cellcount, &amx_addr2, &phys_addr)) != AMX_ERR_NONE) + if ((err = amx_Allot(amx, blocksize, &amx_addr1, &phys_addr)) != AMX_ERR_NONE + || ( err = amx_Allot(amx, blocksize, &amx_addr2, &phys_addr)) != AMX_ERR_NONE) { LogError(amx, err, "Ran out of memory"); return 0; } } - // This is kind of a cheating way to go about this but... - // Create an array of integers as big as however many elements are in the vector. - // Pass that array to qsort - // After the array is sorted out, then create a NEW cellvector - // and copy in the old data in the order of what was sorted - int len; - char* FuncName=get_amxstring(amx, params[2], 0, len); - // MySortFunc(Array:array, item1, item2, const data[], data_size) - int Forward = registerSPForwardByName(amx, FuncName, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_DONE); - if (Forward < 0) - { - if (cellcount > 1) - { - amx_Release(amx, amx_addr1); - amx_Release(amx, amx_addr2); - } + ArraySort_s oldinfo = SortInfo; - LogError(amx, AMX_ERR_NATIVE, "The public function \"%s\" was not found.", FuncName); - return 0; - } + SortInfo.func = func; + SortInfo.array_base = array; + SortInfo.array_bsize = static_cast(blocksize); + SortInfo.array_hndl = params[1]; + SortInfo.data = params[3]; + SortInfo.size = params[4]; + SortInfo.amx = amx; + SortInfo.addr1 = amx_addr1; + SortInfo.addr2 = amx_addr2; - int *IntList=new int[vec->Size()]; + qsort(array, arraysize, blocksize * sizeof(cell), blocksize > 1 ? SortArrayListExArray : SortArrayListExCell); - for (int i=0; i< vec->Size(); i++) - { - IntList[i]=i; - } + SortInfo = oldinfo; - ArraySort_t *Info=new ArraySort_t; - - Info->handle=handle; - Info->forward=Forward; - Info->data=params[3]; - Info->size=params[4]; - Info->vec = vec; - Info->amx = amx; - Info->addr1 = amx_addr1; - Info->addr2 = amx_addr2; - - ArraySortStack.push(Info); - qsort(IntList, vec->Size(), sizeof(int), cellcount > 1 ? SortArrayListExArray : SortArrayListExCell); - ArraySortStack.pop(); - - CellVector* newvec=new CellVector(vec->GetCellCount()); - - // Set the new vector's values - for (int i=0; i< vec->Size(); i++) - { - if (newvec->SetArray(newvec->Push(), vec->GetCellPointer(IntList[i]))!=1) - { - // This should never happen.. - LogError(amx, AMX_ERR_NATIVE, "Failed to SetArray in ArraySort (i=%d, IntList=%d)",i,IntList[i]); - - if (cellcount > 1) - { - amx_Release(amx, amx_addr1); - amx_Release(amx, amx_addr2); - } - - return 0; - } - } - - // Delete the old vector - delete vec; - - // Now save the new vector in its handle location - VectorHolder[handle-1]=newvec; - - // Cleanup - delete Info; - delete IntList; - - unregisterSPForward(Forward); - - if (cellcount > 1) + if (blocksize > 1) { amx_Release(amx, amx_addr1); amx_Release(amx, amx_addr2); } + unregisterSPForward(func); + return 1; } extern bool fastcellcmp(cell *a, cell *b, cell len); extern int amxstring_len(cell* a); -// ArrayFindString(Array:which, const item[]) +// native ArrayFindString(Array:which, const item[]); static cell AMX_NATIVE_CALL ArrayFindString(AMX* amx, cell* params) { - int handle = params[1]; - CellVector* vec = HandleToVector(amx, handle); + CellArray* vec = HandleToVector(amx, params[1]); - if (!vec) + if (vec == NULL) { return -1; } cell *b, *a = get_amxaddr(amx, params[2]); - size_t cellcount = vec->GetCellCount(); - size_t a_len = amxstring_len(a); - if (a_len < 1) - a_len = 1; + size_t cellcount = vec->blocksize(); + size_t a_len = ke::Max(1, amxstring_len(a)); size_t len = a_len > cellcount ? cellcount : a_len; - for (int i = 0; i < vec->Size(); i++) + for (size_t i = 0; i < vec->size(); i++) { - b = vec->GetCellPointer(i); + b = vec->at(i); if (fastcellcmp(a, b, len)) { - return (cell)i; + return static_cast(i); } } return -1; } -// ArrayFindValue(Array:which, any:item); +// native ArrayFindValue(Array:which, any:item); static cell AMX_NATIVE_CALL ArrayFindValue(AMX* amx, cell* params) { - int handle = params[1]; - CellVector* vec = HandleToVector(amx, handle); + CellArray* vec = HandleToVector(amx, params[1]); - if (!vec) + if (vec == NULL) { return -1; } - for (int i = 0; i < vec->Size(); i++) + for (size_t i = 0; i < vec->size(); i++) { - if (params[2] == *vec->GetCellPointer(i)) + if (params[2] == *vec->at(i)) { - return (cell)i; + return static_cast(i); } } diff --git a/amxmodx/datastructs.h b/amxmodx/datastructs.h index 345843b4..15b94d53 100644 --- a/amxmodx/datastructs.h +++ b/amxmodx/datastructs.h @@ -31,352 +31,193 @@ #ifndef DATASTRUCTS_H #define DATASTRUCTS_H -class CellVector +#include + +class CellArray { -private: - cell* data; // allocated with malloc - size_t cellcount; // how many cells per element - size_t cursize; // current size of the vector (maximum elements) - size_t count; // how many units of the vector are in use - - bool GrowIfNeeded(size_t howmany) - { - /* Shortcut out if we can store this */ - if (count + howmany <= cursize) - { - return true; - } - - /* Set a base allocation size of 8 items */ - if (!cursize) - { - cursize = 8; - } - - /* If it's not enough, keep doubling */ - while (count + howmany > cursize) - { - cursize *= 2; - } - - if (data) - { - data = (cell*)realloc(data, (sizeof(cell)* cellcount) * cursize); - } - else - { - data = (cell*)malloc((sizeof(cell)* cellcount) * cursize); - } - - return (data != NULL); - }; - public: - CellVector(): data(NULL), cellcount(0), cursize(0), count(0) + CellArray(size_t blocksize) : m_Data(NULL), m_BlockSize(blocksize), m_AllocSize(0), m_Size(0) { - }; - CellVector(int cellsize): data(NULL), cellcount(cellsize), cursize(0), count(0) - { - }; - ~CellVector() - { - if (data) - { - free(data); - } - }; - size_t GetCellCount() - { - return cellcount; - }; - void Grow(size_t howmany) - { - cursize+=howmany; - if (data) - { - data=(cell*)realloc(data, (sizeof(cell) * cellcount) * cursize); - } - else - { - data=(cell*)malloc((sizeof(cell) * cellcount) * cursize); - } - }; - void FreeUnused(void) - { - if (cursize != count && - data != NULL) - { - cursize=count; - data=(cell*)realloc(data, cursize * (sizeof(cell) * cellcount)); - } - }; - // Returns 1 on success - // 0 on out of bounds. - int GetArray(size_t which, cell* output) - { - // make sure it is in bounds. - if (which >= count) - { - return 0; - } - // align output data - cell* out=data + (cellcount * which); - - memcpy(output, out, sizeof(cell) * cellcount); - - return 1; - }; - // Returns 1 on success - // 0 on out of bounds - int GetCell(size_t which, cell* output) - { - // check bounds - if (which >= count) - { - return 0; - } - *output=*(data + (cellcount * which)); - - return 1; } - // Returns 1 on success - // 0 on out of bounds - int GetString(size_t which, cell* output, size_t size) + + ~CellArray() { - // check bounds - if (which >= count) - { - return 0; - } - cell* out=data + (cellcount * which); - - size_t count=cellcount; - - while (size-- && - count-- && - (*output++=*out++)!='\0') - /* do nothing */ ; - - // If size is zero here, then the string was never null terminated. - if (size==0) - { - *out='\0'; - } - - /* Don't truncate a multi-byte character */ - if (*(output - 1) & 1 << 7) - { - size = UTIL_CheckValidChar(output - 1); - *(output - size) = '\0'; - } - - return 1; + free(m_Data); } - // Returns 1 on success - // 0 on out of bounds - int SetArray(size_t which, cell* output) + + size_t size() const { - if (which >= count) + return m_Size; + } + + cell *push() + { + if (!GrowIfNeeded(1)) { - return 0; + return NULL; } - // align output - cell* out=data + (cellcount * which); + cell *arr = &m_Data[m_Size * m_BlockSize]; + m_Size++; + return arr; + } - memcpy(out, output, sizeof(cell) * cellcount); - - return 1; - }; - // Returns 1 on success - // 0 on out of bounds - int SetCell(size_t which, cell output) + cell *at(size_t b) const { - if (which >= count) - { - return 0; - } - // align output - *(data + (cellcount * which))=output; + return &m_Data[b * m_BlockSize]; + } - return 1; - }; - // Returns 1 on success - // 0 on out of bounds - int SetString(size_t which, cell* output) + size_t blocksize() const { - if (which >= count) - { - return 0; - } - // align output - cell* out=data + (cellcount * which); + return m_BlockSize; + } - memcpy(out, output, sizeof(cell) * cellcount); - - // now force a null terminator on the last entry. - out+=(cellcount - 1); - *out='\0'; - - return 1; - }; - int Push() + void clear() { - if (count >= cursize) - { - // Grow in 8s to cause less reallocation - this->Grow(8); - }; - - this->count++; + m_Size = 0; + } - return this->count-1; - }; - int Size() + bool swap(size_t item1, size_t item2) { - return this->count; - }; - - bool Resize(size_t newsize) - { - if (newsize <= count) - { - count = newsize; - return true; - } - - if (!GrowIfNeeded(newsize - count)) + /* Make sure there is extra space available */ + if (!GrowIfNeeded(1)) { return false; } - count = newsize; + cell *pri = at(item1); + cell *alt = at(item2); + + /* Get our temporary array 1 after the limit */ + cell *temp = &m_Data[m_Size * m_BlockSize]; + + memcpy(temp, pri, sizeof(cell)* m_BlockSize); + memcpy(pri, alt, sizeof(cell)* m_BlockSize); + memcpy(alt, temp, sizeof(cell)* m_BlockSize); + return true; } - CellVector *Clone() + void remove(size_t index) { - CellVector *array = new CellVector(cellcount); - array->count = count; - array->cursize = cursize; - array->data = (cell *)malloc((sizeof(cell)* cellcount) * cursize); - memcpy(array->data, data, (sizeof(cell)* cellcount) * count); - return array; + /* If we're at the end, take the easy way out */ + if (index == m_Size - 1) + { + m_Size--; + return; + } + + /* Otherwise, it's time to move stuff! */ + size_t remaining_indexes = (m_Size - 1) - index; + cell *src = at(index + 1); + cell *dest = at(index); + memmove(dest, src, sizeof(cell)* m_BlockSize * remaining_indexes); + + m_Size--; } - void Clear() + cell *insert_at(size_t index) { - free(data); - data=(cell*)malloc(sizeof(cell) * cellcount); - cursize=1; - count=0; - }; - cell* Base() - { - return data; - } - cell* GetCellPointer(size_t which) - { - if (which >= count) + /* Make sure it'll fit */ + if (!GrowIfNeeded(1)) { return NULL; } - return data + (which * cellcount); - }; - // Shifts all items from this item, and including this item up 1. - int ShiftUpFrom(size_t which) + + /* move everything up */ + cell *src = at(index); + cell *dst = at(index + 1); + memmove(dst, src, sizeof(cell)* m_BlockSize * (m_Size - index)); + + m_Size++; + + return src; + } + + bool resize(size_t count) { - // No point shifting this. - if (which > this->count) + if (count <= m_Size) { - - return 0; - } - // First make a new item. - this->Push(); - - // If we got an InsertAfter(lastitem), then which will equal this->count - 1 - // all we needed to do was Push() - if (which == this->count || - which == this->count - 1) - { - return 1; + m_Size = count; + return true; } - // Allocate a temporary buffer to store data in - size_t tempbuffsize=(sizeof(cell) * cellcount) * (this->count - 1 - which); + if (!GrowIfNeeded(count - m_Size)) + { + return false; + } - cell* temp=(cell*)malloc(tempbuffsize); + m_Size = count; + return true; + } - // Copy old data to temp buffer - memcpy(temp, GetCellPointer(which), tempbuffsize); - - // Now copy temp buffer to adjusted location - memcpy(GetCellPointer(which+1), temp, tempbuffsize); - - // cleanup - free(temp); - - return 1; - - }; - // Shifts all items from this item, and including this item down 1. - // This deletes the item specified. - int Delete(size_t which) + CellArray *clone() { - // No point shifting this. - if (which >= this->count) - { - return 0; - } + CellArray *array = new CellArray(m_BlockSize); + array->m_AllocSize = m_AllocSize; + array->m_Size = m_Size; + array->m_Data = (cell *)malloc(sizeof(cell)* m_BlockSize * m_AllocSize); + memcpy(array->m_Data, m_Data, sizeof(cell)* m_BlockSize * m_Size); + return array; + } - - for (size_t i=which; icount - 1; i++) - { - memcpy(GetCellPointer(i), GetCellPointer(i + 1), sizeof(cell) * cellcount); - } - this->count--; - return 1; - }; - int Swap(size_t item1, size_t item2) + cell *base() { - if (item1 >= this->count || - item2 >= this->count) + return m_Data; + } + + size_t mem_usage() + { + return m_AllocSize * m_BlockSize * sizeof(cell); + } + +private: + bool GrowIfNeeded(size_t count) + { + /* Shortcut out if we can store this */ + if (m_Size + count <= m_AllocSize) { - return 0; + return true; } - - // Make a temp buffer to store item2 - cell* temp=(cell*)malloc(sizeof(cell) * cellcount); - memcpy(temp, GetCellPointer(item2), sizeof(cell) * cellcount); - - // copy item1 to item2 - memcpy(GetCellPointer(item2), GetCellPointer(item1), sizeof(cell) * cellcount); - - // copy item2 to item1 - memcpy(GetCellPointer(item1), temp, sizeof(cell) * cellcount); - - // Cleanup - free(temp); - - return 1; - }; - + /* Set a base allocation size of 8 items */ + if (!m_AllocSize) + { + m_AllocSize = 8; + } + /* If it's not enough, keep doubling */ + while (m_Size + count > m_AllocSize) + { + m_AllocSize *= 2; + } + /* finally, allocate the new block */ + if (m_Data) + { + m_Data = (cell *)realloc(m_Data, sizeof(cell)* m_BlockSize * m_AllocSize); + } + else { + m_Data = (cell *)malloc(sizeof(cell)* m_BlockSize * m_AllocSize); + } + return (m_Data != NULL); + } +private: + cell *m_Data; + size_t m_BlockSize; + size_t m_AllocSize; + size_t m_Size; }; -extern CVector VectorHolder; +extern ke::Vector VectorHolder; -inline CellVector* HandleToVector(AMX* amx, int handle) +inline CellArray* HandleToVector(AMX* amx, int handle) { - if (handle <= 0 || - handle > (int)VectorHolder.size()) + if (handle <= 0 || handle > (int)VectorHolder.length()) { LogError(amx, AMX_ERR_NATIVE, "Invalid array handle provided (%d)", handle); return NULL; } - CellVector* ret=VectorHolder[handle-1]; + CellArray* ret = VectorHolder[handle - 1]; if (ret == NULL) { diff --git a/amxmodx/meta_api.cpp b/amxmodx/meta_api.cpp index 67172920..b8fbe3d1 100755 --- a/amxmodx/meta_api.cpp +++ b/amxmodx/meta_api.cpp @@ -407,7 +407,7 @@ int C_Spawn(edict_t *pent) FlagMan.LoadFile(); - for (unsigned int i=0; iSize(); + size_t arraysize = cArray->size(); srand((unsigned int)time(NULL)); @@ -413,15 +413,15 @@ void sort_adt_random(CellVector *cArray) { int n = rand() % (i + 1); - cArray->Swap(i, n); + cArray->swap(i, n); } } static cell AMX_NATIVE_CALL SortADTArray(AMX *amx, cell *params) { - CellVector* vec=HandleToVector(amx, params[1]); + CellArray* vec = HandleToVector(amx, params[1]); - if (vec==NULL) + if (vec == NULL) { return 0; } @@ -436,9 +436,9 @@ static cell AMX_NATIVE_CALL SortADTArray(AMX *amx, cell *params) } cell type = params[3]; - size_t arraysize = vec->Size(); - size_t blocksize = vec->GetCellCount(); - cell *array = vec->Base(); + size_t arraysize = vec->size(); + size_t blocksize = vec->blocksize(); + cell *array = vec->base(); if (type == Sort_Integer) { diff --git a/amxmodx/string.cpp b/amxmodx/string.cpp index 7d911099..249bc5a9 100755 --- a/amxmodx/string.cpp +++ b/amxmodx/string.cpp @@ -110,7 +110,11 @@ int set_amxstring(AMX *amx, cell amx_addr, const char *source, int max) return dest - start; } -int set_amxstring_utf8(AMX *amx, cell amx_addr, const char *source, size_t sourcelen, size_t maxlen) +template int set_amxstring_utf8(AMX *, cell, const cell *, size_t, size_t); +template int set_amxstring_utf8(AMX *, cell, const char *, size_t, size_t); + +template +int set_amxstring_utf8(AMX *amx, cell amx_addr, const T *source, size_t sourcelen, size_t maxlen) { size_t len = sourcelen; bool needtocheck = false; diff --git a/amxmodx/util.cpp b/amxmodx/util.cpp index 32427449..4986926d 100755 --- a/amxmodx/util.cpp +++ b/amxmodx/util.cpp @@ -514,18 +514,24 @@ unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search return total; } -unsigned int strncopy(char *dest, const char *src, size_t count) +template unsigned int strncopy(char *, const char *src, size_t count); +template unsigned int strncopy(cell *, const char *src, size_t count); + +template +unsigned int strncopy(D *dest, const char *src, size_t count) { if (!count) { return 0; } - char *start = dest; + D *start = dest; + while ((*src) && (--count)) { - *dest++ = *src++; + *dest++ = *(unsigned char*)src++; } + *dest = '\0'; return (dest - start); diff --git a/plugins/include/cellarray.inc b/plugins/include/cellarray.inc index 58f5f6a3..54b47c7d 100644 --- a/plugins/include/cellarray.inc +++ b/plugins/include/cellarray.inc @@ -4,10 +4,6 @@ #define _cellarray_included -enum Array -{ - Invalid_Array = 0 -}; /** * These arrays are intended to be used for a form of global storage without * requiring a #define that needs to be increased each time a person needs more @@ -15,40 +11,72 @@ enum Array * These are not designed to be used as a replacement for normal arrays, as * normal arrays are faster and should be used whenever possible. */ + +enum Array +{ + Invalid_Array = 0 +}; + + /** + * Given a maximum string size (including the null terminator), + * returns the number of cells required to fit that string. + * + * @param size Number of bytes. + * @return Minimum number of cells required to fit the byte count. + */ +stock ByteCountToCells(size) +{ + if (!size) + { + return 1; + } + + return (size + 3) / 4; +} /** * Creates a handle to a dynamically sized array. * It is very important that the cellsize you provide matches up with the buffer sizes * that you pass with subsequent Array{Get,Set,Push} calls. * - * @param cellsize How many cells each entry in the array is. - * @param reserved How many blank entries are created immediately when the array is created. These entries are not valid to read from until called with ArraySet. - * @return Handle to the array. + * @note As per AMX Mod X 1.8.3, reserved parameter has no effect. + * + * @param cellsize How many cells each entry in the array is. + * @param reserved How many blank entries are created immediately when the array is created. + * These entries are not valid to read from until called with ArraySet. + * + * @return Handle to the array. */ -native Array:ArrayCreate(cellsize=1, reserved=32); +native Array:ArrayCreate(cellsize = 1, reserved = 32); /** * Clones an array, returning a new handle with the same size and data. * You must close it. * - * @param which Array handle to be cloned. - * @return New handle to the cloned array object on success, 0 on failure. + * @param which Array handle to be cloned. + * + * @return New handle to the cloned array object on success, 0 on failure. + * @error Invalid handle. */ native Array:ArrayClone(Array:which); /** * Clears all entries from the array. * - * @param which The array to clear. - * @return 1 on success, 0 on failure. + * @param which The array to clear. + * + * @noreturn + * @error Invalid handle. */ native ArrayClear(Array:which); /** * Returns the number of elements in the array. * - * @param which The array to check. - * @return How many elements are in the array. + * @param which The array to check. + * + * @return How many elements are in the array. + * @error Invalid handle. */ native ArraySize(Array:which); @@ -56,9 +84,11 @@ native ArraySize(Array:which); * Resizes an array. If the size is smaller than the current size, * the array is truncated. * - * @param which Array Handle. - * @param newsize New size. - * @return 1 on success, 0 on failure. + * @param which Array handle. + * @param newsize New size. + * + * @noreturn + * @error Invalid handle or out of memory. */ native bool:ArrayResize(Array:which, newsize); @@ -66,29 +96,42 @@ native bool:ArrayResize(Array:which, newsize); * Returns data within an array. * Make sure the output buffer matches the size the array was created with! * - * @param which The array to retrieve the item from. - * @param item The item to retrieve (zero-based). - * @param output The output buffer to write. + * @param which The array to retrieve the item from. + * @param item The item to retrieve (zero-based). + * @param size If not set, assumes the buffer size is equal to the + * cellsize. Otherwise, the size passed is used. + * @param output The output buffer to write. + * + * @return Number of cells copied. + * @error Invalid handle or invalid index. */ -native ArrayGetArray(Array:which, item, any:output[]); +native ArrayGetArray(Array:which, item, any:output[], size = -1); /** * Returns a single cell of data from an array. * Use this only with arrays that were created with a cellsize of 1! * - * @param which The array to retrieve the item from. - * @param item The item to retrieve (zero-based). - * @return The value of the cell. + * @param which The array to retrieve the item from. + * @param item The item to retrieve (zero-based). + * @param block Optionally specify which block to read from + * (useful if the blocksize > 0). + * @param asChar Optionally read as a byte instead of a cell. + * + * @return Value read. + * @error Invalid handle, invalid index, or invalid block. */ -native any:ArrayGetCell(Array:which, item); +native any:ArrayGetCell(Array:which, item, block = 0, bool:asChar = false); /** * Returns a string value from an array. * - * @param which The array to retrieve the item from. - * @param item The item to retrieve (zero-based). - * @param output The variable to store the value in. - * @param size Character size of the output buffer. + * @param which The array to retrieve the item from. + * @param item The item to retrieve (zero-based). + * @param output The variable to store the value in. + * @param size Character size of the output buffer. + * + * @return Number of characters copied. + * @error Invalid handle or invalid index. */ native ArrayGetString(Array:which, item, output[], size); @@ -97,30 +140,44 @@ native ArrayGetString(Array:which, item, output[], size); * The buffer size must match what the cellsize that the array was created with! * The item must already exist, use ArrayPushArray to create a new item within the array. * - * @param which The array to set the item from within. - * @param item The item to set (zero-based). - * @param input The input buffer to store. + * @param which The array to set the item from within. + * @param item The item to set (zero-based). + * @param input The input buffer to store. + * @param size If not set, assumes the buffer size is equal to the + * blocksize. Otherwise, the size passed is used. + * + * @return Number of cells copied. + * @error Invalid handle or invalid index. */ -native ArraySetArray(Array:which, item, const any:input[]); +native ArraySetArray(Array:which, item, const any:input[], size =-1); /** * Sets an array's single cell value. Use this only on array that were created with a cellsize of 1! * The item must already exist, use ArrayPushCell to create a new item within the array. * - * @param which The array to set the item from within. - * @param item The item to set (zero-based). - * @param input The value to set. + * @param which The array to set the item from within. + * @param item The item to set (zero-based). + * @param input The value to set. + * @param block Optionally specify which block to write to + * (useful if the blocksize > 0). + * @param asChar Optionally set as a byte instead of a cell. + * + * @noreturn + * @error Invalid handle, invalid index, or invalid block. */ -native ArraySetCell(Array:which, item, any:input); +native ArraySetCell(Array:which, item, any:input, block = 0, bool:asChar = false); /** * Sets a string value from an array. * The stored string will be truncated if it is longer than the cellsize the array was created with! * The item must already exist, use ArrayPushString to create a new item within the array. * - * @param which The array to set the item from within. - * @param item The item to set (zero-based). - * @param input The string to set the item as. + * @param which The array to set the item from within. + * @param item The item to set (zero-based). + * @param input The string to set the item as. + * + * @return Number of characters copied. + * @error Invalid handle or invalid index. */ native ArraySetString(Array:which, item, const input[]); @@ -128,17 +185,26 @@ native ArraySetString(Array:which, item, const input[]); * Creates a new item at the end of the array and sets its data with that of a local buffer. * The buffer size must match what the cellsize that the array was created with! * - * @param which The array to add the item to. - * @param input The input buffer to store. + * @param which The array to add the item to. + * @param input The input buffer to store. + * @param size If not set, the number of elements copied from the array + * will be equal to the blocksize. If set higher than the + * blocksize, the operation will be truncated. + * + * @return Index of the new entry. + * @error Invalid handle or out of memory. */ -native ArrayPushArray(Array:which, const any:input[]); +native ArrayPushArray(Array:which, const any:input[], size = -1); /** * Creates a new item and sets the array's single cell value. * Use this only on array that were created with a cellsize of 1! * - * @param which The array to add the item to. - * @param input The value to set. + * @param which The array to add the item to. + * @param input The value to set. + * + * @return Index of the new entry. + * @error Invalid handle or out of memory. */ native ArrayPushCell(Array:which, any:input); @@ -146,8 +212,11 @@ native ArrayPushCell(Array:which, any:input); * Creates a new element in the array and sets its value to the input buffer. * The stored string will be truncated if it is longer than the cellsize the array was created with! * - * @param which The array to add the item to. - * @param input The string to set the item as. + * @param which The array to add the item to. + * @param input The string to set the item as. + * + * @return Index of the new entry. + * @error Invalid handle or out of memory. */ native ArrayPushString(Array:which, const input[]); @@ -155,9 +224,12 @@ native ArrayPushString(Array:which, const input[]); * Inserts an item after the selected item. All items beyond it get shifted up 1 space. * The buffer size must match what the cellsize that the array was created with! * - * @param which The array to add the item to. - * @param item The item to insert after. - * @param input The input buffer to store. + * @param which The array to add the item to. + * @param item The item to insert after. + * @param input The input buffer to store. + * + * @noreturn + * @error Invalid handle or invalid index. */ native ArrayInsertArrayAfter(Array:which, item, const any:input[]); @@ -165,9 +237,12 @@ native ArrayInsertArrayAfter(Array:which, item, const any:input[]); * Inserts an item after the selected item. All items beyond it get shifted up 1 space. * Use this only on an array that was created with a cellsize of 1! * - * @param which The array to add the item to. - * @param item The item to insert after. - * @param input The value to set. + * @param which The array to add the item to. + * @param item The item to insert after. + * @param input The value to set. + * + * @noreturn + * @error Invalid handle or invalid index. */ native ArrayInsertCellAfter(Array:which, item, any:input); @@ -175,9 +250,12 @@ native ArrayInsertCellAfter(Array:which, item, any:input); * Inserts an item after the selected item. All items beyond it get shifted up 1 space. * The stored string will be truncated if it is longer than the cellsize the array was created with! * - * @param which The array to add the item to. - * @param item The item to insert after. - * @param input The value to set. + * @param which The array to add the item to. + * @param item The item to insert after. + * @param input The value to set. + * + * @noreturn + * @error Invalid handle or invalid index. */ native ArrayInsertStringAfter(Array:which, item, const input[]); @@ -185,9 +263,12 @@ native ArrayInsertStringAfter(Array:which, item, const input[]); * Inserts an item before the selected item. All items beyond it, and the selected item get shifted up 1 space. * The buffer size must match what the cellsize that the array was created with! * - * @param which The array to add the item to. - * @param item The item to insert before. - * @param input The input buffer to store. + * @param which The array to add the item to. + * @param item The item to insert before. + * @param input The input buffer to store. + * + * @noreturn + * @error Invalid handle or invalid index. */ native ArrayInsertArrayBefore(Array:which, item, const any:input[]); @@ -195,9 +276,12 @@ native ArrayInsertArrayBefore(Array:which, item, const any:input[]); * Inserts an item before the selected item. All items beyond it, and the selected item get shifted up 1 space. * Use this only on an array that was created with a cellsize of 1! * - * @param which The array to add the item to. - * @param item The item to insert after. - * @param input The value to set. + * @param which The array to add the item to. + * @param item The item to insert after. + * @param input The value to set. + * + * @noreturn + * @error Invalid handle or invalid index. */ native ArrayInsertCellBefore(Array:which, item, const any:input); @@ -205,26 +289,35 @@ native ArrayInsertCellBefore(Array:which, item, const any:input); * Inserts an item before the selected item. All items beyond it, and the selected item get shifted up 1 space. * The stored string will be truncated if it is longer than the cellsize the array was created with! * - * @param which The array to add the item to. - * @param item The item to insert before. - * @param input The value to set. + * @param which The array to add the item to. + * @param item The item to insert before. + * @param input The value to set. + * + * @noreturn + * @error Invalid handle or invalid index. */ native ArrayInsertStringBefore(Array:which, item, const input[]); /** * Swaps the position of two items. * - * @param which The array that contains the items. - * @param item1 The first item to swap. - * @param item2 The second item to swap. + * @param which The array that contains the items. + * @param item1 The first item to swap. + * @param item2 The second item to swap. + * + * @noreturn + * @error Invalid handle or invalid index. */ native ArraySwap(Array:which, item1, item2); /** * Deletes an item from the array. All items beyond it get shifted down 1 space. * - * @param which The array that contains the item to delete. - * @param item The item to delete. + * @param which The array that contains the item to delete. + * @param item The item to delete. + * + * @noreturn + * @error Invalid handle or invalid index. */ native ArrayDeleteItem(Array:which, item); @@ -232,9 +325,11 @@ native ArrayDeleteItem(Array:which, item); * Returns the index for the first occurance of the provided string. If the string * cannot be located, -1 will be returned. * - * @param which Array Handle. - * @param item String to search for. - * @return Array index, or -1 on failure. + * @param which Array handle. + * @param item String to search for. + * + * @return Array index, or -1 on failure. + * @error Invalid handle. */ native ArrayFindString(Array:which, const item[]); @@ -242,9 +337,11 @@ native ArrayFindString(Array:which, const item[]); * Returns the index for the first occurance of the provided value. If the value * cannot be located, -1 will be returned. * - * @param which Array Handle. - * @param item Value to search for. - * @return Array index, or -1 on failure. + * @param which Array handle. + * @param item Value to search for. + * + * @return Array index, or -1 on failure. + * @error Invalid handle. */ native ArrayFindValue(Array:which, any:item); @@ -255,21 +352,25 @@ native ArrayFindValue(Array:which, any:item); * * An example usage: client_print(id, print_chat, "%a", ArrayGetStringHandle(MessageArray, i)); * - * @param which The array the string is stored in. - * @param item Which item to print the string value of. - * @return Handle to the item directly. Do not use or save stale handles. + * @param which The array the string is stored in. + * @param item Which item to print the string value of. + * + * @return Handle to the item directly. Do not use or save stale handles. + * @error Invalid handle or invalid index. */ native DoNotUse:ArrayGetStringHandle(Array:which, item); /** * Destroys the array, and resets the handle to 0 to prevent accidental usage after it is destroyed. * - * @param which The array to destroy. + * @param which The array to destroy. + * + * @noreturn + * @error Invalid handle. */ native ArrayDestroy(&Array:which); - /** * Similar to sorting.inc's CustomSort. * The sorting algorithm then uses your comparison function to sort the data. @@ -277,18 +378,26 @@ native ArrayDestroy(&Array:which); * * public MySortFunc(Array:array, item1, item2, const data[], data_size) * - * array - Array handle in its current un-sorted state. - * item1, item2 - Current item pair being compared - * data[] - Extra data array you passed to the sort func. - * data_size - Size of extra data you passed to the sort func. + * array - Array handle in its current un-sorted state. + * item1, item2 - Current item pair being compared + * data[] - Extra data array you passed to the sort func. + * data_size - Size of extra data you passed to the sort func. * * Your function should return: * -1 if item1 should go before item2 * 0 if item1 and item2 are equal * 1 if item1 should go after item2 - * Note that the parameters after item2 are all optional and you do not need to specify them. * + * Note that the parameters after item2 are all optional and you do not need to specify them. * Note that unlike the sorting.inc versions, the array passed to the callback is not in mid-sorted state. + * + * @param array Array handle. + * @param comparefunc A callback function used for comparison. + * @param data Extra data array you passed to the sort func. + * @param data_size Size of extra data you passed to the sort func. + * + * @noreturn + * @error Invalid handle or invalid callback. */ native ArraySort(Array:array, const comparefunc[], data[]="", data_size=0); @@ -306,20 +415,20 @@ native ArraySort(Array:array, const comparefunc[], data[]="", data_size=0); * * public MySortFunc(Array:array, elem1, elem2, const data[], data_size) * - * array - Array handle in its current un-sorted state. - * elem1, elem2 - Current element pair being compared - * data[] - Extra data array you passed to the sort func. - * data_size - Size of extra data you passed to the sort func. + * array - Array handle in its current un-sorted state. + * elem1, elem2 - Current element pair being compared + * data[] - Extra data array you passed to the sort func. + * data_size - Size of extra data you passed to the sort func. * * For Arrays with a cellsize larger than 1 (used for storing arrays and strings), * the function is called in the following manner: * * public MySortFunc(Array:array, elem1[], elem2[], const data[], data_size) * - * array - Array handle in its current un-sorted state. - * elem1[], elem2[] - Current element pair being compared - * data[] - Extra data array you passed to the sort func. - * data_size - Size of extra data you passed to the sort func. + * array - Array handle in its current un-sorted state. + * elem1[], elem2[] - Current element pair being compared + * data[] - Extra data array you passed to the sort func. + * data_size - Size of extra data you passed to the sort func. * * * In both cases your function should return: @@ -328,7 +437,14 @@ native ArraySort(Array:array, const comparefunc[], data[]="", data_size=0); * 1 if elem1 should go after elem2 * * Note that the parameters after elem2 are all optional and you do not need to specify them. - * * Note that unlike the sorting.inc versions, the array passed to the callback is not in mid-sorted state. + * + * @param array Array handle. + * @param comparefunc A callback function used for comparison. + * @param data Extra data array you passed to the sort func. + * @param data_size Size of extra data you passed to the sort func. + * + * @noreturn + * @error Invalid handle, invalid callback or out of memory. */ native ArraySortEx(Array:array, const comparefunc[], data[]="", data_size=0); diff --git a/plugins/testsuite/arraytest.sma b/plugins/testsuite/arraytest.sma index 8eedcad9..87f28708 100644 --- a/plugins/testsuite/arraytest.sma +++ b/plugins/testsuite/arraytest.sma @@ -24,39 +24,39 @@ new TestWords[6][] = { "!=" }; -stock test(any:A,any:B=0,TestType:Type=TT_Equal) +stock test(any:A, any:B = 0, TestType:Type = TT_Equal) { ++__testnumber; - + new passed=0; - + switch (Type) { - case TT_Equal: if (A==B) passed=1; - case TT_LessThan: if (AB) passed=1; - case TT_LessThanEqual: if (A<=B) passed=1; - case TT_GreaterThanEqual: if (A>=B) passed=1; - case TT_NotEqual: if (A!=B) passed=1; + case TT_Equal: if (A == B) passed = 1; + case TT_LessThan: if (A < B) passed = 1; + case TT_GreaterThan: if (A > B) passed = 1; + case TT_LessThanEqual: if (A <= B) passed = 1; + case TT_GreaterThanEqual: if (A >= B) passed = 1; + case TT_NotEqual: if (A != B) passed =1; } - + if (!passed) { - log_amx("Failed test #%d (%d %s %d)",__testnumber,A,TestWords[_:Type],B); + log_amx("Failed test #%d (%d %s %d)", __testnumber, A, TestWords[_:Type], B); errcount++; } } stock starttests(const startfunc[]) { - __testnumber=0; - errcount=0; - __testfuncnum=1; + __testnumber = 0; + errcount = 0; + __testfuncnum = 1; server_print("Starting tests..."); - formatex(__testfunc,sizeof(__testfunc)-1,"%s",startfunc); + formatex(__testfunc, charsmax(__testfunc), "%s", startfunc); new func[32]; - formatex(func,sizeof(func)-1,"%s%d",__testfunc,__testfuncnum++); - set_task(0.1,func); + formatex(func, charsmax(func), "%s%d", __testfunc, __testfuncnum++); + set_task(0.1, func); } stock showres() @@ -64,16 +64,16 @@ stock showres() if (errcount==0) { new func[32]; - formatex(func,sizeof(func)-1,"%s%d",__testfunc,__testfuncnum++); - if (get_func_id(func)==-1) + formatex(func, charsmax(func), "%s%d", __testfunc, __testfuncnum++); + if (get_func_id(func) == -1) { server_print("All tests ok!"); } else { server_print("Test ok, moving on..."); - - set_task(0.1,func); + + set_task(0.1, func); } } else @@ -96,134 +96,128 @@ public arraytest() public arraytest1() { server_print("Testing 1000 iterations of 1-cell arrays..."); - + new Float:f; - new Array:a=ArrayCreate(1); + new Array:a = ArrayCreate(1); + if (a == Invalid_Array) { } - for (new i=0; i<1000; i++) + + for (new i = 0; i < 1000; i++) { - f=float(i); + f = float(i); ArrayPushCell(a,f); } + new Float:r; - for (new i=0; i<1000; i++) + for (new i = 0; i < 1000; i++) { - f=float(i); - r=Float:ArrayGetCell(a, i); - + f = float(i); + r = Float:ArrayGetCell(a, i); + // This is normally bad for float "casting", but in this case it should be fine. test(_:f, _:r); - - + // Reset with inversed values new g=_:f; g=~g; - + ArraySetCell(a, i, g); - - r=Float:ArrayGetCell(a,i); - + r = Float:ArrayGetCell(a,i); test(g, _:r); - } - + ArrayDestroy(a); - + showres(); } + stock bool:checkarray(const a[], const b[], size) { while (size--) { - if (a[size]!=b[size]) + if (a[size] != b[size]) { return false; } } - + return true; } + stock invarray(a[],size) { while (size--) { a[size] = ~a[size]; } - + } public arraytest2() { server_print("Testing 1000 iterations of 40-cell arrays..."); - - new Array:a=ArrayCreate(40); + + new Array:a = ArrayCreate(40); new buff[40]; new buffb[40]; - for (new i=0; i<1000; i++) - { - arrayset(buff,i,sizeof(buff)); - - ArrayPushArray(a, buff); - } - for (new i=0; i<1000; i++) + + for (new i = 0; i < 1000; i++) { arrayset(buff, i, sizeof(buff)); - - ArrayGetArray(a, i, buffb); - - test(_:checkarray(buff,buffb,sizeof(buff)),1); - - // Now overwrite the array with inversed value - invarray(buff,sizeof(buff)); - - ArraySetArray(a, i, buff); - - ArrayGetArray(a, i, buffb); - - test(_:checkarray(buff,buffb,sizeof(buff)),1); + + ArrayPushArray(a, buff); } - + for (new i = 0; i < 1000; i++) + { + arrayset(buff, i, sizeof(buff)); + ArrayGetArray(a, i, buffb); + test(_:checkarray(buff, buffb, sizeof(buff)), 1); + + // Now overwrite the array with inversed value + invarray(buff, sizeof(buff)); + + ArraySetArray(a, i, buff); + ArrayGetArray(a, i, buffb); + test(_:checkarray(buff, buffb, sizeof(buff)), 1); + } + ArrayDestroy(a); - + showres(); } + public arraytest3() { server_print("Testing 1000 iterations of strings..."); - + // The string is 10 long, the string we're trying to pass is 20 long. - - new Array:a=ArrayCreate(10); - - new buff[20]="1234567890abcdefghi"; + new Array:a = ArrayCreate(10); + + new buff[20] = "1234567890abcdefghi"; new buffb[20]; - - for (new i=0; i<1000; i++) + + for (new i = 0; i < 1000; i++) { ArrayPushString(a, buff); } - for (new i=0; i<1000; i++) + + for (new i = 0; i < 1000; i++) { - ArrayGetString(a, i, buffb, sizeof(buffb)-1); - + ArrayGetString(a, i, buffb, charsmax(buffb)); test(strcmp(buffb,"123456789"),0); - + ArraySetString(a, i, "9876543210"); - - ArrayGetString(a, i, buffb, sizeof(buffb)-1); - + ArrayGetString(a, i, buffb, charsmax(buffb)); test(strcmp(buffb,"987654321"),0); - - buffb[0]=0; - - formatex(buffb,sizeof(buffb)-1,"%a", ArrayGetStringHandle(a, i)); - + + buffb[0] = EOS; + formatex(buffb, charsmax(buffb),"%a", ArrayGetStringHandle(a, i)); test(strcmp(buffb, "987654321"),0); } - + ArrayDestroy(a); - + showres(); } @@ -231,17 +225,19 @@ public sortcallback(Array:a, b, c) { static stra[40]; static strb[40]; - - ArrayGetString(a, b, stra, sizeof(stra)-1); - ArrayGetString(a, c, strb, sizeof(strb)-1); + + ArrayGetString(a, b, stra, charsmax(stra)); + ArrayGetString(a, c, strb, charsmax(strb)); + return strcmp(stra,strb); } + public arraytest4() { server_print("Testing sorting function..."); - - new Array:a=ArrayCreate(40); - + + new Array:a = ArrayCreate(40); + ArrayPushString(a, "z"); ArrayPushString(a, "yz"); ArrayPushString(a, "xyz"); @@ -268,203 +264,286 @@ public arraytest4() ArrayPushString(a, "cdefghijklmnopqrstuvwxyz"); ArrayPushString(a, "bcdefghijklmnopqrstuvwxyz"); ArrayPushString(a, "abcdefghijklmnopqrstuvwxyz"); - - new OldSize=ArraySize(a); - + + new OldSize = ArraySize(a); + ArraySort(a, "sortcallback"); - - test(ArraySize(a),OldSize); - + test(ArraySize(a), OldSize); + new buff[40]; - - ArrayGetString(a,0,buff,sizeof(buff)-1); - + ArrayGetString(a, 0, buff, charsmax(buff)); test(strcmp(buff,"abcdefghijklmnopqrstuvwxyz"),0); - - ArrayGetString(a,25,buff,sizeof(buff)-1); - - test(strcmp(buff,"z"),0); - - new start='a'; - - for (new i=0;i=0 ; i--) + + for (new i = ArraySize(a) - 1; i >= 0; i--) { if (i % 2 == 0) { ArrayDeleteItem(a, i); } } + test(ArraySize(a), 500); - for (new i=0; i< 500; i++) + + for (new i = 0; i < 500; i++) { - v=ArrayGetCell(a, i); - + v = ArrayGetCell(a, i); + // All items should be incrementing odd numbers test(((i + 1) * 2) - 1, v); // All remaining entries should be odd test((v & 1), 1); } + ArrayDestroy(a); - - a=ArrayCreate(1); + + a = ArrayCreate(1); + // Repeat the same test, but check even numbers - for (new i=0; i<1000; i++) + for (new i = 0; i < 1000; i++) { ArrayPushCell(a, i); } - for (new i=ArraySize(a) - 1; i>=0 ; i--) + + for (new i = ArraySize(a) - 1; i >= 0 ; i--) { if (i % 2 == 1) { ArrayDeleteItem(a, i); } } + test(ArraySize(a), 500); - for (new i=0; i< 500; i++) + + for (new i = 0; i < 500; i++) { - v=ArrayGetCell(a, i); - + v = ArrayGetCell(a, i); + // All items should be incrementing even numbers test(((i + 1) * 2) - 2, v); // All remaining entries should be even test((v & 1), 0); } + ArrayDestroy(a); - + showres(); } + public arraytest6() { server_print("Testing ArrayInsertCellAfter()..."); - - new Array:a=ArrayCreate(1); - - for (new i=0; i<10;i++) + + new Array:a = ArrayCreate(1); + + for (new i = 0; i < 10;i++) { ArrayPushCell(a, i); - new item=ArraySize(a)-1; - for (new j=0; j<10; j++) + new item = ArraySize(a) - 1; + + for (new j = 0; j < 10; j++) { ArrayInsertCellAfter(a, item + j, j); } } - + test(ArraySize(a), 110); - + new v; - for (new i=0; i<110; i++) + for (new i = 0; i < 110; i++) { - v=ArrayGetCell(a, i); - + v = ArrayGetCell(a, i); test(v, i / 10); - for (new j=0; j<10; j++) + + for (new j = 0; j < 10; j++) { - v=ArrayGetCell(a, ++i); + v = ArrayGetCell(a, ++i); test(v, j); } } - - + ArrayDestroy(a); - + showres(); } + public arraytest7() { - server_print("Testing ArrayInsertCellBefore()..."); + server_print("Testing ArrayInsertStringAfter()..."); + + new Array:a = ArrayCreate(4); + new buffer[4]; - new Array:a=ArrayCreate(1); - - for (new i=0; i<10;i++) + for (new i = 0; i < 10;i++) { - ArrayPushCell(a, i); - new item=ArraySize(a)-1; - for (new j=0; j<10; j++) + formatex(buffer, charsmax(buffer), "%d", i); + ArrayPushString(a, buffer); + new item = ArraySize(a) - 1; + + for (new j = 0; j < 10; j++) + { + formatex(buffer, charsmax(buffer), "%d", j); + ArrayInsertStringAfter(a, item + j, buffer); + } + } + + test(ArraySize(a), 110); + + for (new i = 0; i < 110; i++) + { + ArrayGetString(a, i, buffer, charsmax(buffer)); + test(str_to_num(buffer), i / 10); + + for (new j = 0; j < 10; j++) + { + ArrayGetString(a, ++i, buffer, charsmax(buffer)); + test(str_to_num(buffer), j); + } + } + + ArrayDestroy(a); + + showres(); +} + +public arraytest8() +{ + server_print("Testing ArrayInsertCellBefore()..."); + + new Array:a = ArrayCreate(1); + + for (new i = 0; i < 10; i++) + { + new item = ArrayPushCell(a, i); + + for (new j = 0; j < 10; j++) { ArrayInsertCellBefore(a, item, j); } } - + test(ArraySize(a), 110); - - new v; - for (new i=0; i<110; i++) + + for (new i = 0; i < 110; i++) { - for (new j=9; j>=0; j--) + for (new j = 9; j >= 0; j--) { - v=ArrayGetCell(a, i++); - test(v, j); + test(ArrayGetCell(a, i++), j); } - - v=ArrayGetCell(a, i); - - test(v, (i - 10) / 10); + + test(ArrayGetCell(a, i), (i - 10) / 10); } - - + ArrayDestroy(a); - - showres(); -} -public arraytest8() -{ - server_print("Testing ArraySwap()..."); - new Array:a=ArrayCreate(1); - - for (new i=0; i<10; i++) - { - ArrayPushCell(a, i); - } - for (new i=0; i<5; i++) - { - ArraySwap(a, i, (10 - (i + 1))); - } - new v; - for (new i=0; i<5; i++) - { - v=ArrayGetCell(a, i); - - test(v, (10 - (i + 1))); - } - - ArrayDestroy(a); - + showres(); } -public sortcallbackex_string(Array:a, const b[], const c[], d) -{ - return strcmp(b,c); -} public arraytest9() +{ + server_print("Testing ArrayInsertStringBefore()..."); + + new buffer[4]; + new Array:a = ArrayCreate(4); + + for (new i = 0; i < 10; i++) + { + formatex(buffer, charsmax(buffer), "%d", i); + new item = ArrayPushString(a, buffer); + + for (new j = 0; j < 10; j++) + { + formatex(buffer, charsmax(buffer), "%d", j); + ArrayInsertStringBefore(a, item, buffer); + } + } + + test(ArraySize(a), 110); + + for (new i = 0; i < 110; i++) + { + for (new j = 9; j >= 0; j--) + { + ArrayGetString(a, i++, buffer, charsmax(buffer)); + test(str_to_num(buffer), j); + } + + ArrayGetString(a, i, buffer, charsmax(buffer)); + test(str_to_num(buffer), (i - 10) / 10); + } + + ArrayDestroy(a); + + showres(); +} + +public arraytest10() +{ + server_print("Testing ArraySwap()..."); + + new Array:a = ArrayCreate(1); + + for (new i = 0; i < 10; i++) + { + ArrayPushCell(a, i); + } + + for (new i = 0; i < 5; i++) + { + ArraySwap(a, i, (10 - (i + 1))); + } + + new v; + for (new i = 0; i < 5; i++) + { + v = ArrayGetCell(a, i); + + test(v, (10 - (i + 1))); + } + + ArrayDestroy(a); + + showres(); +} + +public Sortcallbackex_string(Array:a, const b[], const c[], d) +{ + return strcmp(b, c); +} + +public arraytest11() { server_print("Testing (new) sorting function with string..."); - - new Array:a=ArrayCreate(40); - + + new Array:a = ArrayCreate(40); + ArrayPushString(a, "z"); ArrayPushString(a, "yz"); ArrayPushString(a, "xyz"); @@ -491,48 +570,44 @@ public arraytest9() ArrayPushString(a, "cdefghijklmnopqrstuvwxyz"); ArrayPushString(a, "bcdefghijklmnopqrstuvwxyz"); ArrayPushString(a, "abcdefghijklmnopqrstuvwxyz"); - - new OldSize=ArraySize(a); - - ArraySortEx(a, "sortcallbackex_string"); - - test(ArraySize(a),OldSize); - + + new OldSize = ArraySize(a); + + ArraySortEx(a, "Sortcallbackex_string"); + test(ArraySize(a), OldSize); + new buff[40]; - - ArrayGetString(a,0,buff,sizeof(buff)-1); + ArrayGetString(a, 0, buff, charsmax(buff)); + test(strcmp(buff, "abcdefghijklmnopqrstuvwxyz"), 0); - test(strcmp(buff,"abcdefghijklmnopqrstuvwxyz"),0); - - ArrayGetString(a,25,buff,sizeof(buff)-1); - - test(strcmp(buff,"z"),0); + ArrayGetString(a, 25, buff, charsmax(buff)); + test(strcmp(buff, "z"),0); - - new start='a'; - - for (new i=0;i