core/smn_sorting.cpp
author David Anderson <dvander@alliedmods.net>
Tue Dec 04 15:58:07 2007 +0000 (2007-12-04)
changeset 1764 e4f24e9789fd
parent 1704 796db70f03c7
child 1917 3b9449e1cb98
permissions -rw-r--r--
- removed ancient bcompat shim for translations/core.cfg file
- fixed msvc8 whining about BfReadString
     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 #include "HandleSys.h"
    37 #include "CellArray.h"
    38 
    39 /***********************************
    40  *   About the double array hack   *
    41      ***************************   
    42 
    43  Double arrays in Pawn are vectors offset by the current offset.  For example:
    44 
    45  new array[2][2]
    46 
    47    In this array, index 0 contains the offset from the current offset which
    48  results in the final vector [2] (at [0][2]).  Meaning, to dereference [1][2], 
    49  it is equivalent to:
    50 
    51  address = &array[1] + array[1] + 2 * sizeof(cell)
    52 
    53    The fact that each offset is from the _current_ position rather than the _base_
    54  position is very important.  It means that if you to try to swap vector positions,
    55  the offsets will no longer match, because their current position has changed.  A 
    56  simple and ingenious way around this is to back up the positions in a separate array,
    57  then to overwrite each position in the old array with absolute indices.  Pseudo C++ code:
    58 
    59  cell *array; //assumed to be set to the 2+D array
    60  cell *old_offsets = new cell[2];
    61  for (int i=0; i<2; i++)
    62  {
    63     old_offsets = array[i];
    64 	array[i] = i;
    65  }
    66 
    67    Now, you can swap the array indices with no problem, and do a reverse-lookup to find the original addresses.
    68  After sorting/modification is done, you must relocate the new indices.  For example, if the two vectors in our
    69  demo array were swapped, array[0] would be 1 and array[1] would be 0.  This is invalid to the virtual machine.
    70  Luckily, this is also simple -- all the information is there.
    71 
    72  for (int i=0; i<2; i++)
    73  {
    74 	//get the # of the vector we want to relocate in
    75 	cell vector_index = array[i];			
    76 	//get the real address of this vector
    77 	char *real_address = (char *)array + (vector_index * sizeof(cell)) + old_offsets[vector_index];
    78 	//calc and store the new distance offset
    79 	array[i] = real_address - ( (char *)array + (vector_index + sizeof(cell)) )
    80  }
    81 
    82  Note that the inner expression can be heavily reduced; it is expanded for readability.
    83  **********************************/
    84 
    85 enum SortOrder
    86 {
    87 	Sort_Ascending = 0,
    88 	Sort_Descending = 1,
    89 };
    90 
    91 int sort_ints_asc(const void *int1, const void *int2)
    92 {
    93 	return (*(int *)int1) - (*(int *)int2);
    94 }
    95 
    96 int sort_ints_desc(const void *int1, const void *int2)
    97 {
    98 	return (*(int *)int2) - (*(int *)int1);
    99 }
   100 
   101 static cell_t sm_SortIntegers(IPluginContext *pContext, const cell_t *params)
   102 {
   103 	cell_t *array;
   104 	cell_t array_size = params[2];
   105 	cell_t type = params[3];
   106 
   107 	pContext->LocalToPhysAddr(params[1], &array);
   108 
   109 	if (type == Sort_Ascending)
   110 	{
   111 		qsort(array, array_size, sizeof(cell_t), sort_ints_asc);
   112 	} else {
   113 		qsort(array, array_size, sizeof(cell_t), sort_ints_desc);
   114 	}
   115 
   116 	return 1;
   117 }
   118 
   119 int sort_floats_asc(const void *float1, const void *float2)
   120 {
   121 	float r1 = *(float *)float1;
   122 	float r2 = *(float *)float2;
   123 	
   124 	if (r1 < r2)
   125 	{
   126 		return -1;
   127 	} else if (r2 < r1) {
   128 		return 1;
   129 	} else {
   130 		return 0;
   131 	}
   132 }
   133 
   134 int sort_floats_desc(const void *float1, const void *float2)
   135 {
   136 	float r1 = *(float *)float1;
   137 	float r2 = *(float *)float2;
   138 
   139 	if (r1 < r2)
   140 	{
   141 		return 1;
   142 	} else if (r2 < r1) {
   143 		return -1;
   144 	} else {
   145 		return 0;
   146 	}
   147 }
   148 
   149 static cell_t sm_SortFloats(IPluginContext *pContext, const cell_t *params)
   150 {
   151 	cell_t *array;
   152 	cell_t array_size = params[2];
   153 	cell_t type = params[3];
   154 
   155 	pContext->LocalToPhysAddr(params[1], &array);
   156 
   157 	if (type == Sort_Ascending)
   158 	{
   159 		qsort(array, array_size, sizeof(cell_t), sort_floats_asc);
   160 	} else {
   161 		qsort(array, array_size, sizeof(cell_t), sort_floats_desc);
   162 	}
   163 
   164 	return 1;
   165 }
   166 
   167 static cell_t *g_CurStringArray = NULL;
   168 static cell_t *g_CurRebaseMap = NULL;
   169 
   170 int sort_strings_asc(const void *blk1, const void *blk2)
   171 {
   172 	cell_t reloc1 = *(cell_t *)blk1;
   173 	cell_t reloc2 = *(cell_t *)blk2;
   174 	
   175 	char *str1 = ((char *)(&g_CurStringArray[reloc1]) + g_CurRebaseMap[reloc1]);
   176 	char *str2 = ((char *)(&g_CurStringArray[reloc2]) + g_CurRebaseMap[reloc2]);
   177 
   178 	return strcmp(str1, str2);
   179 }
   180 
   181 int sort_strings_desc(const void *blk1, const void *blk2)
   182 {
   183 	cell_t reloc1 = *(cell_t *)blk1;
   184 	cell_t reloc2 = *(cell_t *)blk2;
   185 
   186 	char *str1 = ((char *)(&g_CurStringArray[reloc1]) + g_CurRebaseMap[reloc1]);
   187 	char *str2 = ((char *)(&g_CurStringArray[reloc2]) + g_CurRebaseMap[reloc2]);
   188 
   189 	return strcmp(str2, str1);
   190 }
   191 
   192 static cell_t sm_SortStrings(IPluginContext *pContext, const cell_t *params)
   193 {
   194 	cell_t *array;
   195 	cell_t array_size = params[2];
   196 	cell_t type = params[3];
   197 
   198 	pContext->LocalToPhysAddr(params[1], &array);
   199 
   200 	/** HACKHACK - back up the old indices, replace the indices with something easier */
   201 	cell_t amx_addr, *phys_addr;
   202 	int err;
   203 	if ((err=pContext->HeapAlloc(array_size, &amx_addr, &phys_addr)) != SP_ERROR_NONE)
   204 	{
   205 		pContext->ThrowNativeErrorEx(err, "Ran out of memory to sort");
   206 	}
   207 
   208 	g_CurStringArray = array;
   209 	g_CurRebaseMap = phys_addr;
   210 
   211 	for (int i=0; i<array_size; i++)
   212 	{
   213 		phys_addr[i] = array[i];
   214 		array[i] = i;
   215 	}
   216 
   217 	if (type == Sort_Ascending)
   218 	{
   219 		qsort(array, array_size, sizeof(cell_t), sort_strings_asc);
   220 	} else {
   221 		qsort(array, array_size, sizeof(cell_t), sort_strings_desc);
   222 	}
   223 
   224 	/* END HACKHACK - restore what we damaged so Pawn doesn't throw up.
   225 	 * We'll browse through each index of the array and patch up the distance.
   226 	 */
   227 	for (int i=0; i<array_size; i++)
   228 	{
   229 		/* Compute the final address of the old array and subtract the new location.
   230 		 * This is the fixed up distance.
   231 		 */
   232 		array[i] = ((char *)&array[array[i]] + phys_addr[array[i]]) - (char *)&array[i];
   233 	}
   234 
   235 	pContext->HeapPop(amx_addr);
   236 
   237 	g_CurStringArray = NULL;
   238 	g_CurRebaseMap = NULL;
   239 
   240 	return 1;
   241 }
   242 
   243 struct sort_info
   244 {
   245 	IPluginFunction *pFunc;
   246 	Handle_t hndl;
   247 	cell_t array_addr;
   248 	cell_t *array_base;
   249 	cell_t *array_remap;
   250 };
   251 
   252 sort_info g_SortInfo;
   253 
   254 int sort1d_amx_custom(const void *elem1, const void *elem2)
   255 {
   256 	cell_t c1 = *(cell_t *)elem1;
   257 	cell_t c2 = *(cell_t *)elem2;
   258 
   259 	cell_t result = 0;
   260 	IPluginFunction *pf = g_SortInfo.pFunc;
   261 	pf->PushCell(c1);
   262 	pf->PushCell(c2);
   263 	pf->PushCell(g_SortInfo.array_addr);
   264 	pf->PushCell(g_SortInfo.hndl);
   265 	pf->Execute(&result);
   266 
   267 	return result;
   268 }
   269 
   270 static cell_t sm_SortCustom1D(IPluginContext *pContext, const cell_t *params)
   271 {
   272 	cell_t *array;
   273 	cell_t array_size = params[2];
   274 
   275 	IPluginFunction *pFunction = pContext->GetFunctionById(params[3]);
   276 	if (!pFunction)
   277 	{
   278 		return pContext->ThrowNativeError("Function %x is not a valid function", params[3]);
   279 	}
   280 
   281 	pContext->LocalToPhysAddr(params[1], &array);
   282 
   283 	sort_info oldinfo = g_SortInfo;
   284 
   285 	
   286 	g_SortInfo.hndl = params[4];
   287 	g_SortInfo.array_addr = params[1];
   288 	g_SortInfo.array_remap = NULL;
   289 	g_SortInfo.array_base = NULL;
   290 	g_SortInfo.pFunc = pFunction;
   291 
   292 	qsort(array, array_size, sizeof(cell_t), sort1d_amx_custom);
   293 
   294 	g_SortInfo = oldinfo;
   295 
   296 	return 1;
   297 }
   298 
   299 int sort2d_amx_custom(const void *elem1, const void *elem2)
   300 {
   301 	cell_t c1 = *(cell_t *)elem1;
   302 	cell_t c2 = *(cell_t *)elem2;
   303 
   304 	cell_t c1_addr = g_SortInfo.array_addr + (c1 * sizeof(cell_t)) + g_SortInfo.array_remap[c1];
   305 	cell_t c2_addr = g_SortInfo.array_addr + (c2 * sizeof(cell_t)) + g_SortInfo.array_remap[c2];
   306 
   307 	IPluginContext *pContext = g_SortInfo.pFunc->GetParentContext();
   308 	cell_t *c1_r, *c2_r;
   309 	pContext->LocalToPhysAddr(c1_addr, &c1_r);
   310 	pContext->LocalToPhysAddr(c2_addr, &c2_r);
   311 
   312 	cell_t result = 0;
   313 	g_SortInfo.pFunc->PushCell(c1_addr);
   314 	g_SortInfo.pFunc->PushCell(c2_addr);
   315 	g_SortInfo.pFunc->PushCell(g_SortInfo.array_addr);
   316 	g_SortInfo.pFunc->PushCell(g_SortInfo.hndl);
   317 	g_SortInfo.pFunc->Execute(&result);
   318 
   319 	return result;
   320 }
   321 
   322 static cell_t sm_SortCustom2D(IPluginContext *pContext, const cell_t *params)
   323 {
   324 	cell_t *array;
   325 	cell_t array_size = params[2];
   326 	IPluginFunction *pFunction;
   327 
   328 	pContext->LocalToPhysAddr(params[1], &array);
   329 
   330 	if ((pFunction=pContext->GetFunctionById(params[3])) == NULL)
   331 	{
   332 		return pContext->ThrowNativeError("Function %x is not a valid function", params[3]);
   333 	}
   334 
   335 	/** back up the old indices, replace the indices with something easier */
   336 	cell_t amx_addr, *phys_addr;
   337 	int err;
   338 	if ((err=pContext->HeapAlloc(array_size, &amx_addr, &phys_addr)) != SP_ERROR_NONE)
   339 	{
   340 		pContext->ThrowNativeErrorEx(err, "Ran out of memory to sort");
   341 		return 0;
   342 	}
   343 
   344 	sort_info oldinfo = g_SortInfo;
   345 
   346 	g_SortInfo.pFunc = pFunction;
   347 	g_SortInfo.hndl = params[4];
   348 	g_SortInfo.array_addr = params[1];
   349 	
   350 	/** Same process as in strings, back up the old indices for later fixup */
   351 	g_SortInfo.array_base = array;
   352 	g_SortInfo.array_remap = phys_addr;
   353 	
   354 	for (int i=0; i<array_size; i++)
   355 	{
   356 		phys_addr[i] = array[i];
   357 		array[i] = i;
   358 	}
   359 
   360 	qsort(array, array_size, sizeof(cell_t), sort2d_amx_custom);
   361 
   362 	/** Fixup process! */
   363 	for (int i=0; i<array_size; i++)
   364 	{
   365 		/* Compute the final address of the old array and subtract the new location.
   366 		 * This is the fixed up distance.
   367 		 */
   368 		array[i] = ((char *)&array[array[i]] + phys_addr[array[i]]) - (char *)&array[i];
   369 	}
   370 
   371 	pContext->HeapPop(amx_addr);
   372 
   373 	g_SortInfo = oldinfo;
   374 	
   375 	return 1;
   376 }
   377 
   378 enum SortType
   379 {
   380 	Sort_Integer = 0,
   381 	Sort_Float,
   382 	Sort_String,
   383 };
   384 
   385 int sort_adtarray_strings_asc(const void *str1, const void *str2)
   386 {
   387 	return strcmp((char *) str1, (char *) str2);
   388 }
   389 
   390 int sort_adtarray_strings_desc(const void *str1, const void *str2)
   391 {
   392 	return strcmp((char *) str2, (char *) str1);
   393 }
   394 
   395 static cell_t sm_SortADTArray(IPluginContext *pContext, const cell_t *params)
   396 {
   397 	CellArray *cArray;
   398 	HandleError err;
   399 	HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
   400 
   401 	if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&cArray)) 
   402 		!= HandleError_None)
   403 	{
   404 		return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
   405 	}
   406 
   407 	cell_t order = params[2];
   408 	cell_t type = params[3];
   409 	size_t arraysize = cArray->size();
   410 	size_t blocksize = cArray->blocksize();
   411 	cell_t *array = cArray->base();
   412 
   413 	if (type == Sort_Integer)
   414 	{
   415 		if (order == Sort_Ascending)
   416 		{
   417 			qsort(array, arraysize, blocksize * sizeof(cell_t), sort_ints_asc);
   418 		}
   419 		else
   420 		{
   421 			qsort(array, arraysize, blocksize * sizeof(cell_t), sort_ints_desc);
   422 		}
   423 	}
   424 	else if (type == Sort_Float)
   425 	{
   426 		if (order == Sort_Ascending)
   427 		{
   428 			qsort(array, arraysize, blocksize * sizeof(cell_t), sort_floats_asc);
   429 		}
   430 		else 
   431 		{
   432 			qsort(array, arraysize, blocksize * sizeof(cell_t), sort_floats_desc);
   433 		}
   434 	}
   435 	else if (type == Sort_String)
   436 	{
   437 		if (order == Sort_Ascending)
   438 		{
   439 			qsort(array, arraysize, blocksize * sizeof(cell_t), sort_adtarray_strings_asc);
   440 		}
   441 		else 
   442 		{
   443 			qsort(array, arraysize, blocksize * sizeof(cell_t), sort_adtarray_strings_desc);
   444 		}
   445 	}
   446 
   447 	return 1;
   448 }
   449 
   450 struct sort_infoADT
   451 {
   452 	IPluginFunction *pFunc;
   453 	cell_t *array_base;
   454 	cell_t array_bsize;
   455 	Handle_t array_hndl;
   456 	Handle_t hndl;
   457 };
   458 
   459 sort_infoADT g_SortInfoADT;
   460 
   461 int sort_adtarray_custom(const void *elem1, const void *elem2)
   462 {
   463 	cell_t result = 0;
   464 	IPluginFunction *pf = g_SortInfoADT.pFunc;
   465 	pf->PushCell(((cell_t) ((cell_t *) elem1 - g_SortInfoADT.array_base)) / g_SortInfoADT.array_bsize);
   466 	pf->PushCell(((cell_t) ((cell_t *) elem2 - g_SortInfoADT.array_base)) / g_SortInfoADT.array_bsize);
   467 	pf->PushCell(g_SortInfoADT.array_hndl);
   468 	pf->PushCell(g_SortInfoADT.hndl);
   469 	pf->Execute(&result);
   470 
   471 	return result;
   472 }
   473 
   474 static cell_t sm_SortADTArrayCustom(IPluginContext *pContext, const cell_t *params)
   475 {
   476 	CellArray *cArray;
   477 	HandleError err;
   478 	HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
   479 
   480 	if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&cArray)) 
   481 		!= HandleError_None)
   482 	{
   483 		return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
   484 	}
   485 
   486 	IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
   487 	if (!pFunction)
   488 	{
   489 		return pContext->ThrowNativeError("Function %x is not a valid function", params[2]);
   490 	}
   491 
   492 	size_t arraysize = cArray->size();
   493 	size_t blocksize = cArray->blocksize();
   494 	cell_t *array = cArray->base();
   495 
   496 	sort_infoADT oldinfo = g_SortInfoADT;
   497 
   498 	g_SortInfoADT.pFunc = pFunction;
   499 	g_SortInfoADT.array_base = array;
   500 	g_SortInfoADT.array_bsize = (cell_t) blocksize;
   501 	g_SortInfoADT.array_hndl = params[1];
   502 	g_SortInfoADT.hndl = params[3];
   503 	
   504 	qsort(array, arraysize, blocksize * sizeof(cell_t), sort_adtarray_custom);
   505 
   506 	g_SortInfoADT = oldinfo;
   507 
   508 	return 1;
   509 }
   510 
   511 REGISTER_NATIVES(sortNatives)
   512 {
   513 	{"SortIntegers",			sm_SortIntegers},
   514 	{"SortFloats",				sm_SortFloats},
   515 	{"SortStrings",				sm_SortStrings},
   516 	{"SortCustom1D",			sm_SortCustom1D},
   517 	{"SortCustom2D",			sm_SortCustom2D},
   518 	{"SortADTArray",			sm_SortADTArray},
   519 	{"SortADTArrayCustom",		sm_SortADTArrayCustom},
   520 	{NULL,						NULL},
   521 };