diff --git a/amxmodx/datastructs.cpp b/amxmodx/datastructs.cpp index 47630519..881905ca 100644 --- a/amxmodx/datastructs.cpp +++ b/amxmodx/datastructs.cpp @@ -82,6 +82,7 @@ static cell AMX_NATIVE_CALL ArrayCreate(AMX* amx, cell* params) return VectorHolder.size(); } + // ArrayClear(Array:which) static cell AMX_NATIVE_CALL ArrayClear(AMX* amx, cell* params) { @@ -96,6 +97,7 @@ static cell AMX_NATIVE_CALL ArrayClear(AMX* amx, cell* params) return 1; } + // ArraySize(Array:which) static cell AMX_NATIVE_CALL ArraySize(AMX* amx, cell* params) { @@ -108,6 +110,54 @@ static cell AMX_NATIVE_CALL ArraySize(AMX* amx, cell* params) return vec->Size(); } + +// ArrayResize(Array:which, newsize); +static cell AMX_NATIVE_CALL ArrayResize(AMX* amx, cell* params) +{ + CellVector* 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; + } + + return 1; +} + +// ArrayClone(Array:which) +static cell AMX_NATIVE_CALL ArrayClone(AMX* amx, cell* params) +{ + CellVector* vec = HandleToVector(amx, params[1]); + + if (vec == NULL) + { + return 0; + } + + CellVector *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) + { + if (VectorHolder[i] == NULL) + { + VectorHolder[i] = clonevec; + return i + 1; + } + } + + VectorHolder.push_back(clonevec); + + return VectorHolder.size(); +} + // ArrayGetArray(Array:which, item, any:output[]); static cell AMX_NATIVE_CALL ArrayGetArray(AMX* amx, cell* params) { @@ -613,7 +663,7 @@ static cell AMX_NATIVE_CALL ArraySortEx(AMX* amx, cell* params) return 0; } - cell amx_addr1, amx_addr2, *phys_addr = NULL; + cell amx_addr1 = 0, amx_addr2 = 0, *phys_addr = NULL; size_t cellcount = vec->GetCellCount(); if (cellcount > 1) @@ -711,11 +761,67 @@ static cell AMX_NATIVE_CALL ArraySortEx(AMX* amx, cell* params) return 1; } +extern bool fastcellcmp(cell *a, cell *b, cell len); +extern int amxstring_len(cell* a); + +// ArrayFindString(Array:which, const item[]) +static cell AMX_NATIVE_CALL ArrayFindString(AMX* amx, cell* params) +{ + int handle = params[1]; + CellVector* vec = HandleToVector(amx, handle); + + if (!vec) + { + return -1; + } + + cell *b, *a = get_amxaddr(amx, params[2]); + size_t cellcount = vec->GetCellCount(); + size_t a_len = max(1, amxstring_len(a)); + size_t len = a_len > cellcount ? cellcount : a_len; + + for (int i = 0; i < vec->Size(); i++) + { + b = vec->GetCellPointer(i); + + if (fastcellcmp(a, b, len)) + { + return (cell)i; + } + } + + return -1; +} + +// ArrayFindValue(Array:which, any:item); +static cell AMX_NATIVE_CALL ArrayFindValue(AMX* amx, cell* params) +{ + int handle = params[1]; + CellVector* vec = HandleToVector(amx, handle); + + if (!vec) + { + return -1; + } + + for (int i = 0; i < vec->Size(); i++) + { + if (params[2] == *vec->GetCellPointer(i)) + { + return (cell)i; + } + } + + return -1; +} + AMX_NATIVE_INFO g_DataStructNatives[] = { { "ArrayCreate", ArrayCreate }, { "ArrayClear", ArrayClear }, + { "ArrayClone", ArrayClone }, { "ArraySize", ArraySize }, + { "ArrayResize", ArrayResize }, { "ArrayGetArray", ArrayGetArray }, { "ArrayGetCell", ArrayGetCell }, { "ArrayGetString", ArrayGetString }, @@ -737,6 +843,8 @@ AMX_NATIVE_INFO g_DataStructNatives[] = { "ArrayDestroy", ArrayDestroy }, { "ArraySort", ArraySort }, { "ArraySortEx", ArraySortEx }, + { "ArrayFindString", ArrayFindString }, + { "ArrayFindValue", ArrayFindValue }, { NULL, NULL } }; diff --git a/amxmodx/datastructs.h b/amxmodx/datastructs.h index e4a59037..ee36680a 100644 --- a/amxmodx/datastructs.h +++ b/amxmodx/datastructs.h @@ -39,6 +39,38 @@ private: 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) { @@ -196,6 +228,34 @@ public: { return this->count; }; + + bool Resize(size_t newsize) + { + if (newsize <= count) + { + count = newsize; + return true; + } + + if (!GrowIfNeeded(newsize - count)) + { + return false; + } + + count = newsize; + return true; + } + + CellVector *Clone() + { + 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; + } + void Clear() { free(data); diff --git a/plugins/include/cellarray.inc b/plugins/include/cellarray.inc index f6012b1c..58f5f6a3 100644 --- a/plugins/include/cellarray.inc +++ b/plugins/include/cellarray.inc @@ -23,14 +23,23 @@ enum Array * * @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. + * @return Handle to the array. */ 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. + */ +native Array:ArrayClone(Array:which); + /** * Clears all entries from the array. * - * @param which The array to clear. + * @param which The array to clear. * @return 1 on success, 0 on failure. */ native ArrayClear(Array:which); @@ -38,16 +47,26 @@ native ArrayClear(Array:which); /** * Returns the number of elements in the array. * - * @param which The array to check. + * @param which The array to check. * @return How many elements are in the array. */ 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. + */ +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 which The array to retrieve the item from. * @param item The item to retrieve (zero-based). * @param output The output buffer to write. */ @@ -57,7 +76,7 @@ native ArrayGetArray(Array:which, item, any:output[]); * 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 which The array to retrieve the item from. * @param item The item to retrieve (zero-based). * @return The value of the cell. */ @@ -209,6 +228,26 @@ native ArraySwap(Array:which, item1, item2); */ 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. + */ +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. + */ +native ArrayFindValue(Array:which, any:item); + /** * Creates a handle that is passable to a format compliant routine for printing as a string (with the %a format option). * It is suggested to pass the function directly as a parameter to the format routine. diff --git a/plugins/testsuite/arraytest.sma b/plugins/testsuite/arraytest.sma index 4832e859..8eedcad9 100644 --- a/plugins/testsuite/arraytest.sma +++ b/plugins/testsuite/arraytest.sma @@ -1,6 +1,5 @@ #include - new __testnumber; new errcount; new __testfunc[32]; @@ -25,9 +24,7 @@ new TestWords[6][] = { "!=" }; - - -stock test(A,B=0,TestType:Type=TT_Equal) +stock test(any:A,any:B=0,TestType:Type=TT_Equal) { ++__testnumber; @@ -521,6 +518,8 @@ public arraytest9() test(buff[0],start++); } + ArrayDestroy(a); + showres(); } @@ -558,5 +557,112 @@ public arraytest10() test(ArrayGetCell(a, i),i+1); } + ArrayDestroy(a); + + showres(); +} + +public arraytest11() +{ + server_print("Testing cloning function..."); + + new Array:a = ArrayCreate(1); + + ArrayPushCell(a, 42); + ArrayPushCell(a, 9); + ArrayPushCell(a, -1); + ArrayPushCell(a, 0); + ArrayPushCell(a, 5); + ArrayPushCell(a, 10); + ArrayPushCell(a, 15); + ArrayPushCell(a, 6.5); + + new Array:b = ArrayClone(a); + + ArrayPushCell(b, 48); + ArrayPushCell(b, 3.14); + + test(a, b, TT_NotEqual); + test(ArraySize(a), ArraySize(b) - 2); + test(ArrayGetCell(b, 0), 42); + test(ArrayGetCell(b, 2), -1); + test(ArrayGetCell(b, 7), 6.5); + test(ArrayGetCell(b, 9), 3.14); + + ArrayDestroy(a); + ArrayDestroy(b); + + showres(); +} + +public arraytest12() +{ + server_print("Testing resizing function..."); + + new Array:a = ArrayCreate(16); + + ArrayPushString(a, "egg"); + + ArrayResize(a, 50); + ArrayPushString(a, "boileregg"); + + ArraySetString(a, 50, "no more egg v2"); + + new buffer[16]; + ArrayGetString(a, 50, buffer, charsmax(buffer)); + + test(ArraySize(a), 50 + 1); + test(strcmp(buffer, "no more egg v2"), 0); + + ArrayDestroy(a); + + showres(); +} + +public arraytest13() +{ + server_print("Testing finding string in array..."); + + new Array:a = ArrayCreate(16); + + ArrayPushString(a, "z"); + ArrayPushString(a, "egg"); + ArrayPushString(a, "boilerplate"); + ArrayPushString(a, "amxmodx"); + ArrayPushString(a, "something"); + ArrayPushString(a, ""); + ArrayPushString(a, "eggeggeggeggeggeggegg"); + + test(ArrayFindString(a, "egg"), 1); + test(ArrayFindString(a, "doh"), -1); + test(ArrayFindString(a, "something"), 4); + test(ArrayFindString(a, "eggeggeggeggegg"), 6); + test(ArrayFindString(a, ""), 5); + test(ArrayFindString(a, "zz"), -1); + + ArrayDestroy(a); + + showres(); +} + +public arraytest14() +{ + server_print("Testing finding value in array..."); + + new Array:a = ArrayCreate(1); + + ArrayPushCell(a, 2); + ArrayPushCell(a, 1); + ArrayPushCell(a, 5); + ArrayPushCell(a, 3.14); + ArrayPushCell(a, -1); + + test(ArrayFindValue(a, -1), 4); + test(ArrayFindValue(a, 2), 0); + test(ArrayFindValue(a, 3), -1); + test(ArrayFindValue(a, 3.14), 3); + + ArrayDestroy(a); + showres(); }