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