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