core/smn_sorting.cpp
author David Anderson <dvander@alliedmods.net>
Thu Mar 15 04:45:33 2007 +0000 (2007-03-15)
changeset 617 dc7671b320e4
parent 603 a71f010463c4
child 653 ef60870067ff
permissions -rw-r--r--
fixed two spelling mistakes
     1 /**
     2  * ===============================================================
     3  * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     4  * ===============================================================
     5  *
     6  * This file is not open source and may not be copied without explicit
     7  * written permission of AlliedModders LLC.  This file may not be redistributed 
     8  * in whole or significant part.
     9  * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
    10  *
    11  * Version: $Id$
    12  */
    13 
    14 #include "sm_globals.h"
    15 #include <IHandleSys.h>
    16 #include <stdlib.h>
    17 #include <string.h>
    18 
    19 /***********************************
    20  *   About the double array hack   *
    21      ***************************   
    22 
    23  Double arrays in Pawn are vectors offset by the current offset.  For example:
    24 
    25  new array[2][2]
    26 
    27    In this array, index 0 contains the offset from the current offset which
    28  results in the final vector [2] (at [0][2]).  Meaning, to dereference [1][2], 
    29  it is equivalent to:
    30 
    31  address = &array[1] + array[1] + 2 * sizeof(cell)
    32 
    33    The fact that each offset is from the _current_ position rather than the _base_
    34  position is very important.  It means that if you to try to swap vector positions,
    35  the offsets will no longer match, because their current position has changed.  A 
    36  simple and ingenious way around this is to back up the positions in a separate array,
    37  then to overwrite each position in the old array with absolute indices.  Pseudo C++ code:
    38 
    39  cell *array; //assumed to be set to the 2+D array
    40  cell *old_offsets = new cell[2];
    41  for (int i=0; i<2; i++)
    42  {
    43     old_offsets = array[i];
    44 	array[i] = i;
    45  }
    46 
    47    Now, you can swap the array indices with no problem, and do a reverse-lookup to find the original addresses.
    48  After sorting/modification is done, you must relocate the new indices.  For example, if the two vectors in our
    49  demo array were swapped, array[0] would be 1 and array[1] would be 0.  This is invalid to the virtual machine.
    50  Luckily, this is also simple -- all the information is there.
    51 
    52  for (int i=0; i<2; i++)
    53  {
    54 	//get the # of the vector we want to relocate in
    55 	cell vector_index = array[i];			
    56 	//get the real address of this vector
    57 	char *real_address = (char *)array + (vector_index * sizeof(cell)) + old_offsets[vector_index];
    58 	//calc and store the new distance offset
    59 	array[i] = real_address - ( (char *)array + (vector_index + sizeof(cell)) )
    60  }
    61 
    62  Note that the inner expression can be heavily reduced; it is expanded for readability.
    63  **********************************/
    64 
    65 enum SortOrder
    66 {
    67 	Sort_Ascending = 0,
    68 	Sort_Descending = 1,
    69 };
    70 
    71 int sort_ints_asc(const void *int1, const void *int2)
    72 {
    73 	return (*(int *)int1) - (*(int *)int2);
    74 }
    75 
    76 int sort_ints_desc(const void *int1, const void *int2)
    77 {
    78 	return (*(int *)int2) - (*(int *)int1);
    79 }
    80 
    81 static cell_t sm_SortIntegers(IPluginContext *pContext, const cell_t *params)
    82 {
    83 	cell_t *array;
    84 	cell_t array_size = params[2];
    85 	cell_t type = params[3];
    86 
    87 	pContext->LocalToPhysAddr(params[1], &array);
    88 
    89 	if (type == Sort_Ascending)
    90 	{
    91 		qsort(array, array_size, sizeof(cell_t), sort_ints_asc);
    92 	} else {
    93 		qsort(array, array_size, sizeof(cell_t), sort_ints_desc);
    94 	}
    95 
    96 	return 1;
    97 }
    98 
    99 int sort_floats_asc(const void *float1, const void *float2)
   100 {
   101 	float r1 = *(float *)float1;
   102 	float r2 = *(float *)float2;
   103 	
   104 	if (r1 < r2)
   105 	{
   106 		return -1;
   107 	} else if (r2 < r1) {
   108 		return 1;
   109 	} else {
   110 		return 0;
   111 	}
   112 }
   113 
   114 int sort_floats_desc(const void *float1, const void *float2)
   115 {
   116 	float r1 = *(float *)float1;
   117 	float r2 = *(float *)float2;
   118 
   119 	if (r1 < r2)
   120 	{
   121 		return 1;
   122 	} else if (r2 < r1) {
   123 		return -1;
   124 	} else {
   125 		return 0;
   126 	}
   127 }
   128 
   129 static cell_t sm_SortFloats(IPluginContext *pContext, const cell_t *params)
   130 {
   131 	cell_t *array;
   132 	cell_t array_size = params[2];
   133 	cell_t type = params[3];
   134 
   135 	pContext->LocalToPhysAddr(params[1], &array);
   136 
   137 	if (type == Sort_Ascending)
   138 	{
   139 		qsort(array, array_size, sizeof(cell_t), sort_floats_asc);
   140 	} else {
   141 		qsort(array, array_size, sizeof(cell_t), sort_floats_desc);
   142 	}
   143 
   144 	return 1;
   145 }
   146 
   147 static cell_t *g_CurStringArray = NULL;
   148 static cell_t *g_CurRebaseMap = NULL;
   149 
   150 int sort_strings_asc(const void *blk1, const void *blk2)
   151 {
   152 	cell_t reloc1 = *(cell_t *)blk1;
   153 	cell_t reloc2 = *(cell_t *)blk2;
   154 	
   155 	char *str1 = ((char *)(&g_CurStringArray[reloc1]) + g_CurRebaseMap[reloc1]);
   156 	char *str2 = ((char *)(&g_CurStringArray[reloc2]) + g_CurRebaseMap[reloc2]);
   157 
   158 	return strcmp(str1, str2);
   159 }
   160 
   161 int sort_strings_desc(const void *blk1, const void *blk2)
   162 {
   163 	cell_t reloc1 = *(cell_t *)blk1;
   164 	cell_t reloc2 = *(cell_t *)blk2;
   165 
   166 	char *str1 = ((char *)(&g_CurStringArray[reloc1]) + g_CurRebaseMap[reloc1]);
   167 	char *str2 = ((char *)(&g_CurStringArray[reloc2]) + g_CurRebaseMap[reloc2]);
   168 
   169 	return strcmp(str2, str1);
   170 }
   171 
   172 static cell_t sm_SortStrings(IPluginContext *pContext, const cell_t *params)
   173 {
   174 	cell_t *array;
   175 	cell_t array_size = params[2];
   176 	cell_t type = params[3];
   177 
   178 	pContext->LocalToPhysAddr(params[1], &array);
   179 
   180 	/** HACKHACK - back up the old indices, replace the indices with something easier */
   181 	cell_t amx_addr, *phys_addr;
   182 	int err;
   183 	if ((err=pContext->HeapAlloc(array_size, &amx_addr, &phys_addr)) != SP_ERROR_NONE)
   184 	{
   185 		pContext->ThrowNativeErrorEx(err, "Ran out of memory to sort");
   186 	}
   187 
   188 	g_CurStringArray = array;
   189 	g_CurRebaseMap = phys_addr;
   190 
   191 	for (int i=0; i<array_size; i++)
   192 	{
   193 		phys_addr[i] = array[i];
   194 		array[i] = i;
   195 	}
   196 
   197 	if (type == Sort_Ascending)
   198 	{
   199 		qsort(array, array_size, sizeof(cell_t), sort_strings_asc);
   200 	} else {
   201 		qsort(array, array_size, sizeof(cell_t), sort_strings_desc);
   202 	}
   203 
   204 	/* END HACKHACK - restore what we damaged so Pawn doesn't throw up.
   205 	 * We'll browse through each index of the array and patch up the distance.
   206 	 */
   207 	for (int i=0; i<array_size; i++)
   208 	{
   209 		/* Compute the final address of the old array and subtract the new location.
   210 		 * This is the fixed up distance.
   211 		 */
   212 		array[i] = ((char *)&array[array[i]] + phys_addr[array[i]]) - (char *)&array[i];
   213 	}
   214 
   215 	pContext->HeapPop(amx_addr);
   216 
   217 	g_CurStringArray = NULL;
   218 	g_CurRebaseMap = NULL;
   219 
   220 	return 1;
   221 }
   222 
   223 struct sort_info
   224 {
   225 	IPluginFunction *pFunc;
   226 	Handle_t hndl;
   227 	cell_t array_addr;
   228 	cell_t *array_base;
   229 	cell_t *array_remap;
   230 };
   231 
   232 sort_info g_SortInfo;
   233 
   234 int sort1d_amx_custom(const void *elem1, const void *elem2)
   235 {
   236 	cell_t c1 = *(cell_t *)elem1;
   237 	cell_t c2 = *(cell_t *)elem2;
   238 
   239 	cell_t result = 0;
   240 	IPluginFunction *pf = g_SortInfo.pFunc;
   241 	pf->PushCell(c1);
   242 	pf->PushCell(c2);
   243 	pf->PushCell(g_SortInfo.array_addr);
   244 	pf->PushCell(g_SortInfo.hndl);
   245 	pf->Execute(&result);
   246 
   247 	return result;
   248 }
   249 
   250 static cell_t sm_SortCustom1D(IPluginContext *pContext, const cell_t *params)
   251 {
   252 	cell_t *array;
   253 	cell_t array_size = params[2];
   254 
   255 	IPluginFunction *pFunction = pContext->GetFunctionById(params[3]);
   256 	if (!pFunction)
   257 	{
   258 		return pContext->ThrowNativeError("Function %x is not a valid function", params[3]);
   259 	}
   260 
   261 	pContext->LocalToPhysAddr(params[1], &array);
   262 
   263 	sort_info oldinfo = g_SortInfo;
   264 
   265 	
   266 	g_SortInfo.hndl = params[4];
   267 	g_SortInfo.array_addr = params[1];
   268 	g_SortInfo.array_remap = NULL;
   269 	g_SortInfo.array_base = NULL;
   270 	g_SortInfo.pFunc = pFunction;
   271 
   272 	qsort(array, array_size, sizeof(cell_t), sort1d_amx_custom);
   273 
   274 	g_SortInfo = oldinfo;
   275 
   276 	return 1;
   277 }
   278 
   279 int sort2d_amx_custom(const void *elem1, const void *elem2)
   280 {
   281 	cell_t c1 = *(cell_t *)elem1;
   282 	cell_t c2 = *(cell_t *)elem2;
   283 
   284 	cell_t c1_addr = g_SortInfo.array_addr + (c1 * sizeof(cell_t)) + g_SortInfo.array_remap[c1];
   285 	cell_t c2_addr = g_SortInfo.array_addr + (c2 * sizeof(cell_t)) + g_SortInfo.array_remap[c2];
   286 
   287 	IPluginContext *pContext = g_SortInfo.pFunc->GetParentContext();
   288 	cell_t *c1_r, *c2_r;
   289 	pContext->LocalToPhysAddr(c1_addr, &c1_r);
   290 	pContext->LocalToPhysAddr(c2_addr, &c2_r);
   291 
   292 	cell_t result = 0;
   293 	g_SortInfo.pFunc->PushCell(c1_addr);
   294 	g_SortInfo.pFunc->PushCell(c2_addr);
   295 	g_SortInfo.pFunc->PushCell(g_SortInfo.array_addr);
   296 	g_SortInfo.pFunc->PushCell(g_SortInfo.hndl);
   297 	g_SortInfo.pFunc->Execute(&result);
   298 
   299 	return result;
   300 }
   301 
   302 static cell_t sm_SortCustom2D(IPluginContext *pContext, const cell_t *params)
   303 {
   304 	cell_t *array;
   305 	cell_t array_size = params[2];
   306 	IPluginFunction *pFunction;
   307 
   308 	pContext->LocalToPhysAddr(params[1], &array);
   309 
   310 	if ((pFunction=pContext->GetFunctionById(params[3])) == NULL)
   311 	{
   312 		return pContext->ThrowNativeError("Function %x is not a valid function", params[3]);
   313 	}
   314 
   315 	/** back up the old indices, replace the indices with something easier */
   316 	cell_t amx_addr, *phys_addr;
   317 	int err;
   318 	if ((err=pContext->HeapAlloc(array_size, &amx_addr, &phys_addr)) != SP_ERROR_NONE)
   319 	{
   320 		pContext->ThrowNativeErrorEx(err, "Ran out of memory to sort");
   321 		return 0;
   322 	}
   323 
   324 	sort_info oldinfo = g_SortInfo;
   325 
   326 	g_SortInfo.pFunc = pFunction;
   327 	g_SortInfo.hndl = params[4];
   328 	g_SortInfo.array_addr = params[1];
   329 	
   330 	/** Same process as in strings, back up the old indices for later fixup */
   331 	g_SortInfo.array_base = array;
   332 	g_SortInfo.array_remap = phys_addr;
   333 	
   334 	for (int i=0; i<array_size; i++)
   335 	{
   336 		phys_addr[i] = array[i];
   337 		array[i] = i;
   338 	}
   339 
   340 	qsort(array, array_size, sizeof(cell_t), sort2d_amx_custom);
   341 
   342 	/** Fixup process! */
   343 	for (int i=0; i<array_size; i++)
   344 	{
   345 		/* Compute the final address of the old array and subtract the new location.
   346 		 * This is the fixed up distance.
   347 		 */
   348 		array[i] = ((char *)&array[array[i]] + phys_addr[array[i]]) - (char *)&array[i];
   349 	}
   350 
   351 	pContext->HeapPop(amx_addr);
   352 
   353 	g_SortInfo = oldinfo;
   354 	
   355 	return 1;
   356 }
   357 
   358 REGISTER_NATIVES(sortNatives)
   359 {
   360 	{"SortIntegers",			sm_SortIntegers},
   361 	{"SortFloats",				sm_SortFloats},
   362 	{"SortStrings",				sm_SortStrings},
   363 	{"SortCustom1D",			sm_SortCustom1D},
   364 	{"SortCustom2D",			sm_SortCustom2D},
   365 	{NULL,						NULL},
   366 };