core/smn_sorting.cpp
author Matt Woodrow <pred@alliedmods.net>
Thu May 01 23:48:27 2008 +0000 (2008-05-01)
changeset 2035 941abff6deed
parent 1952 b7aa2b626807
child 2289 44045ead61cd
permissions -rw-r--r--
Added msvc9 Project files to sdktools. Badly.
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 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 	Sort_Random = 2,
    90 };
    91 
    92 void sort_random(cell_t *array, cell_t size)
    93 {
    94 	srand((unsigned int)time(NULL));
    95 
    96 	for (int i = size-1; i > 0; i--)
    97 	{
    98         int n = (rand() % i) + 1;
    99 
   100 		if (array[i] != array[n]) 
   101 		{
   102 			array[i] ^= array[n];
   103 			array[n] ^= array[i];
   104 			array[i] ^= array[n];
   105 		}
   106 	}
   107 }
   108 
   109 int sort_ints_asc(const void *int1, const void *int2)
   110 {
   111 	return (*(int *)int1) - (*(int *)int2);
   112 }
   113 
   114 int sort_ints_desc(const void *int1, const void *int2)
   115 {
   116 	return (*(int *)int2) - (*(int *)int1);
   117 }
   118 
   119 static cell_t sm_SortIntegers(IPluginContext *pContext, const cell_t *params)
   120 {
   121 	cell_t *array;
   122 	cell_t array_size = params[2];
   123 	cell_t type = params[3];
   124 
   125 	pContext->LocalToPhysAddr(params[1], &array);
   126 
   127 	if (type == Sort_Ascending)
   128 	{
   129 		qsort(array, array_size, sizeof(cell_t), sort_ints_asc);
   130 	} 
   131 	else if (type == Sort_Descending)
   132 	{
   133 		qsort(array, array_size, sizeof(cell_t), sort_ints_desc);
   134 	}
   135 	else
   136 	{
   137 		sort_random(array, array_size);
   138 	}
   139 
   140 	return 1;
   141 }
   142 
   143 int sort_floats_asc(const void *float1, const void *float2)
   144 {
   145 	float r1 = *(float *)float1;
   146 	float r2 = *(float *)float2;
   147 	
   148 	if (r1 < r2)
   149 	{
   150 		return -1;
   151 	} else if (r2 < r1) {
   152 		return 1;
   153 	} else {
   154 		return 0;
   155 	}
   156 }
   157 
   158 int sort_floats_desc(const void *float1, const void *float2)
   159 {
   160 	float r1 = *(float *)float1;
   161 	float r2 = *(float *)float2;
   162 
   163 	if (r1 < r2)
   164 	{
   165 		return 1;
   166 	} else if (r2 < r1) {
   167 		return -1;
   168 	} else {
   169 		return 0;
   170 	}
   171 }
   172 
   173 static cell_t sm_SortFloats(IPluginContext *pContext, const cell_t *params)
   174 {
   175 	cell_t *array;
   176 	cell_t array_size = params[2];
   177 	cell_t type = params[3];
   178 
   179 	pContext->LocalToPhysAddr(params[1], &array);
   180 
   181 	if (type == Sort_Ascending)
   182 	{
   183 		qsort(array, array_size, sizeof(cell_t), sort_floats_asc);
   184 	}
   185 	else if (type == Sort_Descending)
   186 	{
   187 		qsort(array, array_size, sizeof(cell_t), sort_floats_desc);
   188 	}
   189 	else
   190 	{
   191 		sort_random(array, array_size);
   192 	}
   193 
   194 	return 1;
   195 }
   196 
   197 static cell_t *g_CurStringArray = NULL;
   198 static cell_t *g_CurRebaseMap = NULL;
   199 
   200 int sort_strings_asc(const void *blk1, const void *blk2)
   201 {
   202 	cell_t reloc1 = *(cell_t *)blk1;
   203 	cell_t reloc2 = *(cell_t *)blk2;
   204 	
   205 	char *str1 = ((char *)(&g_CurStringArray[reloc1]) + g_CurRebaseMap[reloc1]);
   206 	char *str2 = ((char *)(&g_CurStringArray[reloc2]) + g_CurRebaseMap[reloc2]);
   207 
   208 	return strcmp(str1, str2);
   209 }
   210 
   211 int sort_strings_desc(const void *blk1, const void *blk2)
   212 {
   213 	cell_t reloc1 = *(cell_t *)blk1;
   214 	cell_t reloc2 = *(cell_t *)blk2;
   215 
   216 	char *str1 = ((char *)(&g_CurStringArray[reloc1]) + g_CurRebaseMap[reloc1]);
   217 	char *str2 = ((char *)(&g_CurStringArray[reloc2]) + g_CurRebaseMap[reloc2]);
   218 
   219 	return strcmp(str2, str1);
   220 }
   221 
   222 static cell_t sm_SortStrings(IPluginContext *pContext, const cell_t *params)
   223 {
   224 	cell_t *array;
   225 	cell_t array_size = params[2];
   226 	cell_t type = params[3];
   227 
   228 	pContext->LocalToPhysAddr(params[1], &array);
   229 
   230 	/** HACKHACK - back up the old indices, replace the indices with something easier */
   231 	cell_t amx_addr, *phys_addr;
   232 	int err;
   233 	if ((err=pContext->HeapAlloc(array_size, &amx_addr, &phys_addr)) != SP_ERROR_NONE)
   234 	{
   235 		pContext->ThrowNativeErrorEx(err, "Ran out of memory to sort");
   236 	}
   237 
   238 	g_CurStringArray = array;
   239 	g_CurRebaseMap = phys_addr;
   240 
   241 	for (int i=0; i<array_size; i++)
   242 	{
   243 		phys_addr[i] = array[i];
   244 		array[i] = i;
   245 	}
   246 
   247 	if (type == Sort_Ascending)
   248 	{
   249 		qsort(array, array_size, sizeof(cell_t), sort_strings_asc);
   250 	} 
   251 	else if (type == Sort_Descending)
   252 	{
   253 		qsort(array, array_size, sizeof(cell_t), sort_strings_desc);
   254 	}
   255 	else
   256 	{
   257 		sort_random(array, array_size);
   258 	}
   259 
   260 	/* END HACKHACK - restore what we damaged so Pawn doesn't throw up.
   261 	 * We'll browse through each index of the array and patch up the distance.
   262 	 */
   263 	for (int i=0; i<array_size; i++)
   264 	{
   265 		/* Compute the final address of the old array and subtract the new location.
   266 		 * This is the fixed up distance.
   267 		 */
   268 		array[i] = ((char *)&array[array[i]] + phys_addr[array[i]]) - (char *)&array[i];
   269 	}
   270 
   271 	pContext->HeapPop(amx_addr);
   272 
   273 	g_CurStringArray = NULL;
   274 	g_CurRebaseMap = NULL;
   275 
   276 	return 1;
   277 }
   278 
   279 struct sort_info
   280 {
   281 	IPluginFunction *pFunc;
   282 	Handle_t hndl;
   283 	cell_t array_addr;
   284 	cell_t *array_base;
   285 	cell_t *array_remap;
   286 };
   287 
   288 sort_info g_SortInfo;
   289 
   290 int sort1d_amx_custom(const void *elem1, const void *elem2)
   291 {
   292 	cell_t c1 = *(cell_t *)elem1;
   293 	cell_t c2 = *(cell_t *)elem2;
   294 
   295 	cell_t result = 0;
   296 	IPluginFunction *pf = g_SortInfo.pFunc;
   297 	pf->PushCell(c1);
   298 	pf->PushCell(c2);
   299 	pf->PushCell(g_SortInfo.array_addr);
   300 	pf->PushCell(g_SortInfo.hndl);
   301 	pf->Execute(&result);
   302 
   303 	return result;
   304 }
   305 
   306 static cell_t sm_SortCustom1D(IPluginContext *pContext, const cell_t *params)
   307 {
   308 	cell_t *array;
   309 	cell_t array_size = params[2];
   310 
   311 	IPluginFunction *pFunction = pContext->GetFunctionById(params[3]);
   312 	if (!pFunction)
   313 	{
   314 		return pContext->ThrowNativeError("Function %x is not a valid function", params[3]);
   315 	}
   316 
   317 	pContext->LocalToPhysAddr(params[1], &array);
   318 
   319 	sort_info oldinfo = g_SortInfo;
   320 
   321 	
   322 	g_SortInfo.hndl = params[4];
   323 	g_SortInfo.array_addr = params[1];
   324 	g_SortInfo.array_remap = NULL;
   325 	g_SortInfo.array_base = NULL;
   326 	g_SortInfo.pFunc = pFunction;
   327 
   328 	qsort(array, array_size, sizeof(cell_t), sort1d_amx_custom);
   329 
   330 	g_SortInfo = oldinfo;
   331 
   332 	return 1;
   333 }
   334 
   335 int sort2d_amx_custom(const void *elem1, const void *elem2)
   336 {
   337 	cell_t c1 = *(cell_t *)elem1;
   338 	cell_t c2 = *(cell_t *)elem2;
   339 
   340 	cell_t c1_addr = g_SortInfo.array_addr + (c1 * sizeof(cell_t)) + g_SortInfo.array_remap[c1];
   341 	cell_t c2_addr = g_SortInfo.array_addr + (c2 * sizeof(cell_t)) + g_SortInfo.array_remap[c2];
   342 
   343 	IPluginContext *pContext = g_SortInfo.pFunc->GetParentContext();
   344 	cell_t *c1_r, *c2_r;
   345 	pContext->LocalToPhysAddr(c1_addr, &c1_r);
   346 	pContext->LocalToPhysAddr(c2_addr, &c2_r);
   347 
   348 	cell_t result = 0;
   349 	g_SortInfo.pFunc->PushCell(c1_addr);
   350 	g_SortInfo.pFunc->PushCell(c2_addr);
   351 	g_SortInfo.pFunc->PushCell(g_SortInfo.array_addr);
   352 	g_SortInfo.pFunc->PushCell(g_SortInfo.hndl);
   353 	g_SortInfo.pFunc->Execute(&result);
   354 
   355 	return result;
   356 }
   357 
   358 static cell_t sm_SortCustom2D(IPluginContext *pContext, const cell_t *params)
   359 {
   360 	cell_t *array;
   361 	cell_t array_size = params[2];
   362 	IPluginFunction *pFunction;
   363 
   364 	pContext->LocalToPhysAddr(params[1], &array);
   365 
   366 	if ((pFunction=pContext->GetFunctionById(params[3])) == NULL)
   367 	{
   368 		return pContext->ThrowNativeError("Function %x is not a valid function", params[3]);
   369 	}
   370 
   371 	/** back up the old indices, replace the indices with something easier */
   372 	cell_t amx_addr, *phys_addr;
   373 	int err;
   374 	if ((err=pContext->HeapAlloc(array_size, &amx_addr, &phys_addr)) != SP_ERROR_NONE)
   375 	{
   376 		pContext->ThrowNativeErrorEx(err, "Ran out of memory to sort");
   377 		return 0;
   378 	}
   379 
   380 	sort_info oldinfo = g_SortInfo;
   381 
   382 	g_SortInfo.pFunc = pFunction;
   383 	g_SortInfo.hndl = params[4];
   384 	g_SortInfo.array_addr = params[1];
   385 	
   386 	/** Same process as in strings, back up the old indices for later fixup */
   387 	g_SortInfo.array_base = array;
   388 	g_SortInfo.array_remap = phys_addr;
   389 	
   390 	for (int i=0; i<array_size; i++)
   391 	{
   392 		phys_addr[i] = array[i];
   393 		array[i] = i;
   394 	}
   395 
   396 	qsort(array, array_size, sizeof(cell_t), sort2d_amx_custom);
   397 
   398 	/** Fixup process! */
   399 	for (int i=0; i<array_size; i++)
   400 	{
   401 		/* Compute the final address of the old array and subtract the new location.
   402 		 * This is the fixed up distance.
   403 		 */
   404 		array[i] = ((char *)&array[array[i]] + phys_addr[array[i]]) - (char *)&array[i];
   405 	}
   406 
   407 	pContext->HeapPop(amx_addr);
   408 
   409 	g_SortInfo = oldinfo;
   410 	
   411 	return 1;
   412 }
   413 
   414 enum SortType
   415 {
   416 	Sort_Integer = 0,
   417 	Sort_Float,
   418 	Sort_String,
   419 };
   420 
   421 int sort_adtarray_strings_asc(const void *str1, const void *str2)
   422 {
   423 	return strcmp((char *) str1, (char *) str2);
   424 }
   425 
   426 int sort_adtarray_strings_desc(const void *str1, const void *str2)
   427 {
   428 	return strcmp((char *) str2, (char *) str1);
   429 }
   430 
   431 void sort_adt_random(CellArray *cArray)
   432 {
   433 	size_t arraysize = cArray->size();
   434 
   435 	srand((unsigned int)time(NULL));
   436 
   437 	for (int i = arraysize-1; i > 0; i--)
   438 	{
   439         int n = (rand() % i) + 1;
   440 
   441 		cArray->swap(i, n);
   442 	}
   443 }
   444 
   445 static cell_t sm_SortADTArray(IPluginContext *pContext, const cell_t *params)
   446 {
   447 	CellArray *cArray;
   448 	HandleError err;
   449 	HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
   450 
   451 	if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&cArray)) 
   452 		!= HandleError_None)
   453 	{
   454 		return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
   455 	}
   456 
   457 	cell_t order = params[2];
   458 	
   459 	if (order == Sort_Random)
   460 	{
   461 		sort_adt_random(cArray);
   462 
   463 		return 1;
   464 	}
   465 
   466 	cell_t type = params[3];
   467 	size_t arraysize = cArray->size();
   468 	size_t blocksize = cArray->blocksize();
   469 	cell_t *array = cArray->base();
   470 
   471 	if (type == Sort_Integer)
   472 	{
   473 		if (order == Sort_Ascending)
   474 		{
   475 			qsort(array, arraysize, blocksize * sizeof(cell_t), sort_ints_asc);
   476 		}
   477 		else
   478 		{
   479 			qsort(array, arraysize, blocksize * sizeof(cell_t), sort_ints_desc);
   480 		}
   481 	}
   482 	else if (type == Sort_Float)
   483 	{
   484 		if (order == Sort_Ascending)
   485 		{
   486 			qsort(array, arraysize, blocksize * sizeof(cell_t), sort_floats_asc);
   487 		}
   488 		else 
   489 		{
   490 			qsort(array, arraysize, blocksize * sizeof(cell_t), sort_floats_desc);
   491 		}
   492 	}
   493 	else if (type == Sort_String)
   494 	{
   495 		if (order == Sort_Ascending)
   496 		{
   497 			qsort(array, arraysize, blocksize * sizeof(cell_t), sort_adtarray_strings_asc);
   498 		}
   499 		else 
   500 		{
   501 			qsort(array, arraysize, blocksize * sizeof(cell_t), sort_adtarray_strings_desc);
   502 		}
   503 	}
   504 
   505 	return 1;
   506 }
   507 
   508 struct sort_infoADT
   509 {
   510 	IPluginFunction *pFunc;
   511 	cell_t *array_base;
   512 	cell_t array_bsize;
   513 	Handle_t array_hndl;
   514 	Handle_t hndl;
   515 };
   516 
   517 sort_infoADT g_SortInfoADT;
   518 
   519 int sort_adtarray_custom(const void *elem1, const void *elem2)
   520 {
   521 	cell_t result = 0;
   522 	IPluginFunction *pf = g_SortInfoADT.pFunc;
   523 	pf->PushCell(((cell_t) ((cell_t *) elem1 - g_SortInfoADT.array_base)) / g_SortInfoADT.array_bsize);
   524 	pf->PushCell(((cell_t) ((cell_t *) elem2 - g_SortInfoADT.array_base)) / g_SortInfoADT.array_bsize);
   525 	pf->PushCell(g_SortInfoADT.array_hndl);
   526 	pf->PushCell(g_SortInfoADT.hndl);
   527 	pf->Execute(&result);
   528 
   529 	return result;
   530 }
   531 
   532 static cell_t sm_SortADTArrayCustom(IPluginContext *pContext, const cell_t *params)
   533 {
   534 	CellArray *cArray;
   535 	HandleError err;
   536 	HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
   537 
   538 	if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&cArray)) 
   539 		!= HandleError_None)
   540 	{
   541 		return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
   542 	}
   543 
   544 	IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
   545 	if (!pFunction)
   546 	{
   547 		return pContext->ThrowNativeError("Function %x is not a valid function", params[2]);
   548 	}
   549 
   550 	size_t arraysize = cArray->size();
   551 	size_t blocksize = cArray->blocksize();
   552 	cell_t *array = cArray->base();
   553 
   554 	sort_infoADT oldinfo = g_SortInfoADT;
   555 
   556 	g_SortInfoADT.pFunc = pFunction;
   557 	g_SortInfoADT.array_base = array;
   558 	g_SortInfoADT.array_bsize = (cell_t) blocksize;
   559 	g_SortInfoADT.array_hndl = params[1];
   560 	g_SortInfoADT.hndl = params[3];
   561 	
   562 	qsort(array, arraysize, blocksize * sizeof(cell_t), sort_adtarray_custom);
   563 
   564 	g_SortInfoADT = oldinfo;
   565 
   566 	return 1;
   567 }
   568 
   569 REGISTER_NATIVES(sortNatives)
   570 {
   571 	{"SortIntegers",			sm_SortIntegers},
   572 	{"SortFloats",				sm_SortFloats},
   573 	{"SortStrings",				sm_SortStrings},
   574 	{"SortCustom1D",			sm_SortCustom1D},
   575 	{"SortCustom2D",			sm_SortCustom2D},
   576 	{"SortADTArray",			sm_SortADTArray},
   577 	{"SortADTArrayCustom",		sm_SortADTArrayCustom},
   578 	{NULL,						NULL},
   579 };