From df7ee94b837ba276d7361bebf0dfb39a87a85cfe Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sun, 20 Aug 2006 04:46:34 +0000 Subject: [PATCH] Committed sorting natives as per request am23838 --- amxmodx/Makefile | 2 +- amxmodx/amxmodx.h | 1 + amxmodx/modules.cpp | 1 + amxmodx/msvc/amxmodx_mm.vcproj | 3 + amxmodx/sorting.cpp | 359 +++++++++++++++++++++++++++++++++ plugins/include/amxmodx.inc | 1 + plugins/include/sorting.inc | 76 +++++++ plugins/testsuite/sorttest.sma | 153 ++++++++++++++ 8 files changed, 595 insertions(+), 1 deletion(-) create mode 100644 amxmodx/sorting.cpp create mode 100644 plugins/include/sorting.inc create mode 100644 plugins/testsuite/sorttest.sma diff --git a/amxmodx/Makefile b/amxmodx/Makefile index f2ec112e..b92b1020 100755 --- a/amxmodx/Makefile +++ b/amxmodx/Makefile @@ -19,7 +19,7 @@ OBJECTS = meta_api.cpp CFile.cpp CVault.cpp vault.cpp float.cpp file.cpp modules srvcmd.cpp strptime.cpp amxcore.cpp amxtime.cpp power.cpp amxxlog.cpp fakemeta.cpp \ amxxfile.cpp CLang.cpp md5.cpp emsg.cpp CForward.cpp CPlugin.cpp CModule.cpp \ CMenu.cpp util.cpp amx.cpp amxdbg.cpp natives.cpp newmenus.cpp debugger.cpp \ - optimizer.cpp format.cpp messages.cpp libraries.cpp vector.cpp + optimizer.cpp format.cpp messages.cpp libraries.cpp vector.cpp sorting.cpp LINK = /lib/libstdc++.a diff --git a/amxmodx/amxmodx.h b/amxmodx/amxmodx.h index fc33bdf2..8b23592e 100755 --- a/amxmodx/amxmodx.h +++ b/amxmodx/amxmodx.h @@ -85,6 +85,7 @@ extern AMX_NATIVE_INFO string_Natives[]; extern AMX_NATIVE_INFO vault_Natives[]; extern AMX_NATIVE_INFO msg_Natives[]; extern AMX_NATIVE_INFO vector_Natives[]; +extern AMX_NATIVE_INFO g_SortNatives[]; #ifndef __linux__ #define DLLOAD(path) (DLHANDLE)LoadLibrary(path) diff --git a/amxmodx/modules.cpp b/amxmodx/modules.cpp index 723da268..85748524 100755 --- a/amxmodx/modules.cpp +++ b/amxmodx/modules.cpp @@ -556,6 +556,7 @@ int set_amxnatives(AMX* amx, char error[128]) amx_Register(amx, g_DebugNatives, -1); amx_Register(amx, msg_Natives, -1); amx_Register(amx, vector_Natives, -1); + amx_Register(amx, g_SortNatives, -1); //we're not actually gonna check these here anymore amx->flags |= AMX_FLAG_PRENIT; diff --git a/amxmodx/msvc/amxmodx_mm.vcproj b/amxmodx/msvc/amxmodx_mm.vcproj index 5866b736..4a67603c 100755 --- a/amxmodx/msvc/amxmodx_mm.vcproj +++ b/amxmodx/msvc/amxmodx_mm.vcproj @@ -442,6 +442,9 @@ + + diff --git a/amxmodx/sorting.cpp b/amxmodx/sorting.cpp new file mode 100644 index 00000000..3c74b99b --- /dev/null +++ b/amxmodx/sorting.cpp @@ -0,0 +1,359 @@ +#include "amxmodx.h" +#include + +/*********************************** + * About the double array hack * + *************************** + + Double arrays in Pawn are vectors offset but the current offset. For example: + + new array[2][2] + + In this array, index 0 contains the offset from the current offset which + results in the final vector [2] (at [0][2]). Meaning, to dereference [1][2], + it is equivalent to: + + address = &array[1] + array[1] + 2 * sizeof(cell) + + The fact that each offset is from the _current_ position rather than the _base_ + position is very important. It means that if you to try to swap vector positions, + the offsets will no longer match, because their current position has changed. A + simple and ingenious way around this is to back up the positions in a separate array, + then to overwrite each position in the old array with absolute indices. Pseudo C++ code: + + cell *array; //assumed to be set to the 2+D array + cell *old_offsets = new cell[2]; + for (int i=0; i<2; i++) + { + old_offsets = array[i]; + array[i] = i; + } + + Now, you can swap the array indices with no problem, and do a reverse-lookup to find the original addresses. + After sorting/modification is done, you must relocate the new indices. For example, if the two vectors in our + demo array were swapped, array[0] would be 1 and array[1] would be 0. This is invalid to the virtual machine. + Luckily, this is also simple -- all the information is there. + + for (int i=0; i<2; i++) + { + //get the # of the vector we want to relocate in + cell vector_index = array[i]; + //get the real address of this vector + char *real_address = (char *)array + (vector_index * sizeof(cell)) + old_offsets[vector_index]; + //calc and store the new distance offset + array[i] = real_address - ( (char *)array + (vector_index + sizeof(cell)) ) + } + + Note that the inner expression can be heavily reduced; it is expanded for readability. + **********************************/ + +enum SortOrder +{ + Sort_Ascending = 0, + Sort_Descending = 1, +}; + +int sort_ints_asc(const void *int1, const void *int2) +{ + return (*(int *)int1) - (*(int *)int2); +} + +int sort_ints_desc(const void *int1, const void *int2) +{ + return (*(int *)int2) - (*(int *)int1); +} + +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) + { + qsort(array, array_size, sizeof(cell), sort_ints_asc); + } else { + qsort(array, array_size, sizeof(cell), sort_ints_desc); + } + + return 1; +} + +int sort_floats_asc(const void *float1, const void *float2) +{ + REAL r1 = *(REAL *)float1; + REAL r2 = *(REAL *)float2; + + if (r1 < r2) + { + return -1; + } else if (r2 < r1) { + return 1; + } else { + return 0; + } +} + +int sort_floats_desc(const void *float1, const void *float2) +{ + REAL r1 = *(REAL *)float1; + REAL r2 = *(REAL *)float2; + + if (r1 < r2) + { + return 1; + } else if (r2 < r1) { + return -1; + } else { + return 0; + } +} + +static cell AMX_NATIVE_CALL SortFloats(AMX *amx, cell *params) +{ + cell *array = get_amxaddr(amx, params[1]); + cell array_size = params[2]; + cell type = params[3]; + + if (type == Sort_Ascending) + { + qsort(array, array_size, sizeof(cell), sort_floats_asc); + } else { + qsort(array, array_size, sizeof(cell), sort_floats_desc); + } + + return 1; +} + +static cell *g_CurStringArray = NULL; +static cell *g_CurRebaseMap = NULL; + +int sort_strings_asc(const void *blk1, const void *blk2) +{ + cell reloc1 = *(cell *)blk1; + cell reloc2 = *(cell *)blk2; + + register cell *str1 = (cell *)((char *)(&g_CurStringArray[reloc1]) + g_CurRebaseMap[reloc1]); + register cell *str2 = (cell *)((char *)(&g_CurStringArray[reloc2]) + g_CurRebaseMap[reloc2]); + + while (*str1 == *str2++) + { + if (*str1++ == 0) + { + return 0; + } + } + + return (*str1 - *(str2 - 1)); +} + +int sort_strings_desc(const void *blk1, const void *blk2) +{ + cell reloc1 = *(cell *)blk1; + cell reloc2 = *(cell *)blk2; + + register cell *str1 = (cell *)((char *)(&g_CurStringArray[reloc1]) + g_CurRebaseMap[reloc1]); + register cell *str2 = (cell *)((char *)(&g_CurStringArray[reloc2]) + g_CurRebaseMap[reloc2]); + + while (*str1 == *str2++) + { + if (*str1++ == 0) + { + return 0; + } + } + + return (*(str2 - 1) - *str1); +} + +static cell AMX_NATIVE_CALL SortStrings(AMX *amx, cell *params) +{ + cell *array = get_amxaddr(amx, params[1]); + cell array_size = params[2]; + cell type = params[3]; + + /** HACKHACK - back up the old indices, replace the indices with something easier */ + cell amx_addr, *phys_addr; + int err; + if ((err=amx_Allot(amx, array_size, &amx_addr, &phys_addr)) != AMX_ERR_NONE) + { + LogError(amx, err, "Ran out of memory"); + return 0; + } + + g_CurStringArray = array; + g_CurRebaseMap = phys_addr; + + for (int i=0; i g_AMXSortStack; + +int sort1d_amx_custom(const void *elem1, const void *elem2) +{ + cell c1 = *(cell *)elem1; + cell c2 = *(cell *)elem2; + sort_info *pInfo = g_AMXSortStack.front(); + + return executeForwards(pInfo->pfn, c1, c2, pInfo->array_addr, pInfo->data_addr, pInfo->data_size); +} + +static cell AMX_NATIVE_CALL SortCustom1D(AMX *amx, cell *params) +{ + cell *array = get_amxaddr(amx, params[1]); + cell array_size = params[2]; + int len; + const char *funcname = get_amxstring(amx, params[3], 0, len); + + int pfn = registerSPForwardByName(amx, funcname, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_CELL); + if (pfn < 0) + { + LogError(amx, AMX_ERR_NATIVE, "The public function \"%s\" was not found.", funcname); + return 0; + } + + sort_info *pInfo = new sort_info; + + pInfo->pfn = pfn; + pInfo->data_addr = params[4]; + pInfo->data_size = params[5]; + pInfo->array_addr = params[1]; + pInfo->array_remap = NULL; + pInfo->array_base = NULL; + + g_AMXSortStack.push(pInfo); + qsort(array, array_size, sizeof(cell), sort1d_amx_custom); + g_AMXSortStack.pop(); + + delete pInfo; + + return 1; +} + +int sort2d_amx_custom(const void *elem1, const void *elem2) +{ + cell c1 = *(cell *)elem1; + cell c2 = *(cell *)elem2; + sort_info *pInfo = g_AMXSortStack.front(); + + cell c1_addr = pInfo->array_addr + (c1 * sizeof(cell)) + pInfo->array_remap[c1]; + cell c2_addr = pInfo->array_addr + (c2 * sizeof(cell)) + pInfo->array_remap[c2]; + + cell *c1_r = get_amxaddr(pInfo->amx, c1_addr); + cell *c2_r = get_amxaddr(pInfo->amx, c2_addr); + + return executeForwards(pInfo->pfn, c1_addr, c2_addr, pInfo->array_addr, pInfo->data_addr, pInfo->data_size); +} + +static cell AMX_NATIVE_CALL SortCustom2D(AMX *amx, cell *params) +{ + cell *array = get_amxaddr(amx, params[1]); + cell array_size = params[2]; + int len; + const char *funcname = get_amxstring(amx, params[3], 0, len); + + /** back up the old indices, replace the indices with something easier */ + cell amx_addr, *phys_addr; + int err; + if ((err=amx_Allot(amx, array_size, &amx_addr, &phys_addr)) != AMX_ERR_NONE) + { + LogError(amx, err, "Ran out of memory"); + return 0; + } + + int pfn = registerSPForwardByName(amx, funcname, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_CELL); + if (pfn < 0) + { + amx_Release(amx, amx_addr); + LogError(amx, AMX_ERR_NATIVE, "The public function \"%s\" was not found.", funcname); + return 0; + } + + sort_info *pInfo = new sort_info; + + pInfo->pfn = pfn; + pInfo->data_addr = params[4]; + pInfo->data_size = params[5]; + pInfo->array_addr = params[1]; + pInfo->amx = amx; + + /** Same process as in strings, back up the old indices for later fixup */ + pInfo->array_base = array; + pInfo->array_remap = phys_addr; + + for (int i=0; ipfn); + delete pInfo; + + return 1; +} + +AMX_NATIVE_INFO g_SortNatives[] = +{ + {"SortIntegers", SortIntegers}, + {"SortFloats", SortFloats}, + {"SortStrings", SortStrings}, + {"SortCustom1D", SortCustom1D}, + {"SortCustom2D", SortCustom2D}, + + {NULL, NULL}, +}; diff --git a/plugins/include/amxmodx.inc b/plugins/include/amxmodx.inc index 940af59d..c4367f2a 100755 --- a/plugins/include/amxmodx.inc +++ b/plugins/include/amxmodx.inc @@ -20,6 +20,7 @@ #include #include #include +#include /* Function is called just after server activation. * Good place for configuration loading, commands and cvars registration. */ diff --git a/plugins/include/sorting.inc b/plugins/include/sorting.inc new file mode 100644 index 00000000..dc3907b2 --- /dev/null +++ b/plugins/include/sorting.inc @@ -0,0 +1,76 @@ +/* Sorting functions. + * + * by the AMX Mod X Development Team + * + * This file is provided as is (no warranties). + * + * All sort functions are based off the qsort() function from the + * C standard library, which uses the Quick Sort algorithm. + * For more info, see: http://linux.wku.edu/~lamonml/algor/sort/sort.html + */ + +#if defined _time_included + #endinput +#endif +#define _time_included + +enum SortMethod +{ + Sort_Ascending = 0, + Sort_Descending = 1, +}; + +/** + * Basic sorting functions below. + */ + +native SortIntegers(array[], array_size, SortMethod:order = Sort_Ascending); + +native SortFloats(Float:array[], array_size, SortMethod:order = Sort_Ascending); + +native SortStrings(array[][], num_strings, SortMethod:order = Sort_Ascending); + +/** + * Custom sorting functions below. + */ + +/** + * Sorts a custom 1D array. You must pass in a comparison function. + * The sorting algorithm then uses your comparison function to sort the data. + * The function is called in the following manner: + * + * public MySortFunc(elem1, elem2, const array[], const data[], data_size) + * + * elem1, elem2 - Current element pair being compared + * array[] - Array in its current mid-sorted state. + * 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 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. + */ +native SortCustom1D(array[], array_size, const comparefunc[], data[]="", data_size=0); + + +/** + * Sorts a custom 2D array. + * The sorting algorithm then uses your comparison function to sort the data. + * The function is called in the following manner: + * + * public MySortFunc(const elem1[], const elem2[], const array[], data[], data_size) + * + * elem1[], elem2[] - Current element array pairs being compared + * array[][] - Array in its currently being sorted state. + * 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 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. + */ +native SortCustom2D(array[][], array_size, const comparefunc[], data[]="", data_size=0); diff --git a/plugins/testsuite/sorttest.sma b/plugins/testsuite/sorttest.sma new file mode 100644 index 00000000..2b998fb4 --- /dev/null +++ b/plugins/testsuite/sorttest.sma @@ -0,0 +1,153 @@ +#include + +public plugin_init() +{ + register_plugin("Sort Test", "1.0", "BAILOPAN") + + register_srvcmd("test_sort_ints", "Command_TestSortInts") + register_srvcmd("test_sort_floats", "Command_TestSortFloats") + register_srvcmd("test_sort_strings", "Command_TestSortStrings") + register_srvcmd("test_sort_1d", "Command_TestSort1D") + register_srvcmd("test_sort_2d", "Command_TestSort2D") +} + +/***************** + * INTEGER TESTS * + *****************/ +// Note that integer comparison is just int1-int2 (or a variation therein) + +PrintIntegers(const array[], size) +{ + for (new i=0; i elem2) + { + return -1; + } else if (elem1 < elem2) { + return 1; + } + + return 0; +} + +public Command_TestSort1D() +{ + new Float:array[10] = {6.3, 7.6, 3.2, 2.1, 8.5, 5.2, 0.4, 1.7, 4.8, 8.2} + + SortCustom1D(_:array, 10, "Custom1DSort") + PrintFloats(array, 10) + + return PLUGIN_HANDLED +} + +/*************************** + * String comparison tests * + ***************************/ + +PrintStrings(const array[][], size) +{ + for (new i=0; i