From 1ff337d9801e2fbd9ad210bc1285d31679b3029e Mon Sep 17 00:00:00 2001 From: Vincent Herbet Date: Mon, 5 Aug 2013 16:56:59 +0200 Subject: [PATCH] Add ArraySortEx/SortADTArray natives and new sort method Sort_Random (bug 5494, r=Nextra) --- amxmodx/datastructs.cpp | 134 ++++++++++++++++++++++++++++++ amxmodx/datastructs.h | 4 + amxmodx/sorting.cpp | 139 +++++++++++++++++++++++++++++++- plugins/include/cellarray.inc | 41 ++++++++++ plugins/include/sorting.inc | 27 ++++++- plugins/testsuite/arraytest.sma | 105 +++++++++++++++++++++++- plugins/testsuite/sorttest.sma | 138 +++++++++++++++++++++++++++++++ 7 files changed, 581 insertions(+), 7 deletions(-) diff --git a/amxmodx/datastructs.cpp b/amxmodx/datastructs.cpp index 51f3a581..71321bab 100644 --- a/amxmodx/datastructs.cpp +++ b/amxmodx/datastructs.cpp @@ -491,6 +491,10 @@ typedef struct ArraySort_s int forward; cell data; cell size; + CellVector *vec; + AMX* amx; + cell addr1; + cell addr2; } ArraySort_t; @@ -577,6 +581,135 @@ static cell AMX_NATIVE_CALL ArraySort(AMX* amx, cell* params) } +int SortArrayListExCell(const void *itema, const void *itemb) +{ + ArraySort_t *Info = ArraySortStack.front(); + cell vala, valb; + + Info->vec->GetCell(*((int *)itema), &vala); + Info->vec->GetCell(*((int *)itemb), &valb); + + return executeForwards(Info->forward, Info->handle, vala, valb, Info->data, Info->size); +} + +int SortArrayListExArray(const void *itema, const void *itemb) +{ + ArraySort_t *Info = ArraySortStack.front(); + + Info->vec->GetArray(*((int *)itema), get_amxaddr(Info->amx, Info->addr1)); + Info->vec->GetArray(*((int *)itemb), get_amxaddr(Info->amx, Info->addr2)); + + return executeForwards(Info->forward, Info->handle, Info->addr1, Info->addr2, Info->data, Info->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); + + if (!vec) + { + return 0; + } + + cell amx_addr1, amx_addr2, *phys_addr = NULL; + size_t cellcount = vec->GetCellCount(); + + if (cellcount > 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) + { + 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); + } + + 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; + + 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) + { + amx_Release(amx, amx_addr1); + amx_Release(amx, amx_addr2); + } + + return 1; +} AMX_NATIVE_INFO g_DataStructNatives[] = { @@ -603,6 +736,7 @@ AMX_NATIVE_INFO g_DataStructNatives[] = { "ArrayGetStringHandle", ArrayGetStringHandle }, { "ArrayDestroy", ArrayDestroy }, { "ArraySort", ArraySort }, + { "ArraySortEx", ArraySortEx }, { NULL, NULL } }; diff --git a/amxmodx/datastructs.h b/amxmodx/datastructs.h index 2609d12c..e4a59037 100644 --- a/amxmodx/datastructs.h +++ b/amxmodx/datastructs.h @@ -203,6 +203,10 @@ public: cursize=1; count=0; }; + cell* Base() + { + return data; + } cell* GetCellPointer(size_t which) { if (which >= count) diff --git a/amxmodx/sorting.cpp b/amxmodx/sorting.cpp index b0f24b12..d727f791 100644 --- a/amxmodx/sorting.cpp +++ b/amxmodx/sorting.cpp @@ -1,5 +1,7 @@ #include "amxmodx.h" #include +#include +#include "datastructs.h" /*********************************** * About the double array hack * @@ -51,6 +53,7 @@ enum SortOrder { Sort_Ascending = 0, Sort_Descending = 1, + Sort_Random = 2, }; int sort_ints_asc(const void *int1, const void *int2) @@ -63,17 +66,40 @@ int sort_ints_desc(const void *int1, const void *int2) return (*(int *)int2) - (*(int *)int1); } +void sort_random(cell *array, cell size) +{ + srand((unsigned int)time(NULL)); + + for (int i = size-1; i > 0; i--) + { + int n = rand() % (i + 1); + + if (array[i] != array[n]) + { + array[i] ^= array[n]; + array[n] ^= array[i]; + array[i] ^= array[n]; + } + } +} + static cell AMX_NATIVE_CALL SortIntegers(AMX *amx, cell *params) { cell *array = get_amxaddr(amx, params[1]); cell array_size = params[2]; cell type = params[3]; - if (type == Sort_Ascending) + if (type == Sort_Ascending) { qsort(array, array_size, sizeof(cell), sort_ints_asc); - } else { + } + else if (type == Sort_Descending) + { qsort(array, array_size, sizeof(cell), sort_ints_desc); + } + else + { + sort_random(array, array_size); } return 1; @@ -118,9 +144,15 @@ static cell AMX_NATIVE_CALL SortFloats(AMX *amx, cell *params) if (type == Sort_Ascending) { qsort(array, array_size, sizeof(cell), sort_floats_asc); - } else { + } + else if (type == Sort_Descending) + { qsort(array, array_size, sizeof(cell), sort_floats_desc); } + else + { + sort_random(array, array_size); + } return 1; } @@ -193,9 +225,15 @@ static cell AMX_NATIVE_CALL SortStrings(AMX *amx, cell *params) if (type == Sort_Ascending) { qsort(array, array_size, sizeof(cell), sort_strings_asc); - } else { + } + else if (type == Sort_Descending) + { qsort(array, array_size, sizeof(cell), sort_strings_desc); } + else + { + sort_random(array, array_size); + } /* END HACKHACK - restore what we damaged so Pawn doesn't throw up. * We'll browse through each index of the array and patch up the distance. @@ -348,6 +386,98 @@ static cell AMX_NATIVE_CALL SortCustom2D(AMX *amx, cell *params) return 1; } +enum SortType +{ + Sort_Integer = 0, + Sort_Float, + Sort_String, +}; + +int sort_adtarray_strings_asc(const void *str1, const void *str2) +{ + return strcmp((char *) str1, (char *) str2); +} + +int sort_adtarray_strings_desc(const void *str1, const void *str2) +{ + return strcmp((char *) str2, (char *) str1); +} + +void sort_adt_random(CellVector *cArray) +{ + size_t arraysize = cArray->Size(); + + srand((unsigned int)time(NULL)); + + for (int i = arraysize-1; i > 0; i--) + { + int n = rand() % (i + 1); + + cArray->Swap(i, n); + } +} + +static cell AMX_NATIVE_CALL SortADTArray(AMX *amx, cell *params) +{ + CellVector* vec=HandleToVector(amx, params[1]); + + if (vec==NULL) + { + return 0; + } + + cell order = params[2]; + + if (order == Sort_Random) + { + sort_adt_random(vec); + + return 1; + } + + cell type = params[3]; + size_t arraysize = vec->Size(); + size_t blocksize = vec->GetCellCount(); + cell *array = vec->Base(); + + if (type == Sort_Integer) + { + if (order == Sort_Ascending) + { + qsort(array, arraysize, blocksize * sizeof(cell), sort_ints_asc); + } + else + { + qsort(array, arraysize, blocksize * sizeof(cell), sort_ints_desc); + } + } + else if (type == Sort_Float) + { + if (order == Sort_Ascending) + { + qsort(array, arraysize, blocksize * sizeof(cell), sort_floats_asc); + } + else + { + qsort(array, arraysize, blocksize * sizeof(cell), sort_floats_desc); + } + } + else if (type == Sort_String) + { + if (order == Sort_Ascending) + { + qsort(array, arraysize, blocksize * sizeof(cell), sort_adtarray_strings_asc); + } + else + { + qsort(array, arraysize, blocksize * sizeof(cell), sort_adtarray_strings_desc); + } + } + + return 1; +} + + AMX_NATIVE_INFO g_SortNatives[] = { {"SortIntegers", SortIntegers}, @@ -355,6 +485,7 @@ AMX_NATIVE_INFO g_SortNatives[] = {"SortStrings", SortStrings}, {"SortCustom1D", SortCustom1D}, {"SortCustom2D", SortCustom2D}, + {"SortADTArray", SortADTArray}, {NULL, NULL}, }; diff --git a/plugins/include/cellarray.inc b/plugins/include/cellarray.inc index 77e7a10b..f6012b1c 100644 --- a/plugins/include/cellarray.inc +++ b/plugins/include/cellarray.inc @@ -252,3 +252,44 @@ native ArrayDestroy(&Array:which); * Note that unlike the sorting.inc versions, the array passed to the callback is not in mid-sorted state. */ native ArraySort(Array:array, const comparefunc[], data[]="", data_size=0); + +/** + * A faster version of ArraySort. + * The sorting algorithm then uses your comparison function to sort the data. + * + * The advantage of this native is that the Array elements being compared are + * directly passed into your function, instead of the item indexes that are passed by ArraySort. + * This removes the need for calling ArrayGet[Cell|String|Array] every time before being + * able to compare the elements. + * + * For Arrays with a cellsize of 1 (used for storing integers and floats), + * 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. + * + * 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. + * + * + * In both cases your function should return: + * -1 if elem1 should go before elem2 + * 0 if elem1 and elem2 are equal + * 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. + */ +native ArraySortEx(Array:array, const comparefunc[], data[]="", data_size=0); diff --git a/plugins/include/sorting.inc b/plugins/include/sorting.inc index 567080c7..a96b5a60 100644 --- a/plugins/include/sorting.inc +++ b/plugins/include/sorting.inc @@ -1,4 +1,4 @@ -/* Sorting functions. + /* Sorting functions. * * by the AMX Mod X Development Team * @@ -14,12 +14,25 @@ #endif #define _sorting_included +/** + * Contains sorting orders. + */ enum SortMethod { Sort_Ascending = 0, - Sort_Descending = 1, + Sort_Descending, + Sort_Random, }; +/** + * Data types for ADT Array Sorts + */ +enum SortType +{ + Sort_Integer = 0, + Sort_Float, + Sort_String, +}; /** * Basic sorting functions below. */ @@ -74,3 +87,13 @@ native SortCustom1D(array[], array_size, const comparefunc[], data[]="", data_si * Note that the parameters after elem2[] are all optional and you do not need to specify them. */ native SortCustom2D(array[][], array_size, const comparefunc[], data[]="", data_size=0); + +/** + * Sort an ADT Array. Specify the type as Integer, Float, or String. + * + * @param array Array Handle to sort + * @param order Sort order to use, same as other sorts. + * @param type Data type stored in the ADT Array + * @noreturn + */ +native SortADTArray(Array:array, SortMethod:order, SortType:type); diff --git a/plugins/testsuite/arraytest.sma b/plugins/testsuite/arraytest.sma index 609e6d71..4832e859 100644 --- a/plugins/testsuite/arraytest.sma +++ b/plugins/testsuite/arraytest.sma @@ -220,7 +220,7 @@ public arraytest3() buffb[0]=0; - formatex(buffb,sizeof(buffb)-1,"%S", ArrayGetStringHandle(a, i)); + formatex(buffb,sizeof(buffb)-1,"%a", ArrayGetStringHandle(a, i)); test(strcmp(buffb, "987654321"),0); } @@ -457,3 +457,106 @@ public arraytest8() showres(); } + +public sortcallbackex_string(Array:a, const b[], const c[], d) +{ + return strcmp(b,c); +} +public arraytest9() +{ + server_print("Testing (new) sorting function with string..."); + + new Array:a=ArrayCreate(40); + + ArrayPushString(a, "z"); + ArrayPushString(a, "yz"); + ArrayPushString(a, "xyz"); + ArrayPushString(a, "wxyz"); + ArrayPushString(a, "vwxyz"); + ArrayPushString(a, "uvwxyz"); + ArrayPushString(a, "tuvwxyz"); + ArrayPushString(a, "stuvwxyz"); + ArrayPushString(a, "rstuvwxyz"); + ArrayPushString(a, "qrstuvwxyz"); + ArrayPushString(a, "pqrstuvwxyz"); + ArrayPushString(a, "opqrstuvwxyz"); + ArrayPushString(a, "nopqrstuvwxyz"); + ArrayPushString(a, "mnopqrstuvwxyz"); + ArrayPushString(a, "lmnopqrstuvwxyz"); + ArrayPushString(a, "klmnopqrstuvwxyz"); + ArrayPushString(a, "jklmnopqrstuvwxyz"); + ArrayPushString(a, "ijklmnopqrstuvwxyz"); + ArrayPushString(a, "hijklmnopqrstuvwxyz"); + ArrayPushString(a, "ghijklmnopqrstuvwxyz"); + ArrayPushString(a, "fghijklmnopqrstuvwxyz"); + ArrayPushString(a, "efghijklmnopqrstuvwxyz"); + ArrayPushString(a, "defghijklmnopqrstuvwxyz"); + ArrayPushString(a, "cdefghijklmnopqrstuvwxyz"); + ArrayPushString(a, "bcdefghijklmnopqrstuvwxyz"); + ArrayPushString(a, "abcdefghijklmnopqrstuvwxyz"); + + new OldSize=ArraySize(a); + + ArraySortEx(a, "sortcallbackex_string"); + + test(ArraySize(a),OldSize); + + new buff[40]; + + ArrayGetString(a,0,buff,sizeof(buff)-1); + + 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