core/logic/smn_sorting.cpp
author systematicmania <systematicmania@gmail.com>
Sat Aug 24 00:12:21 2013 -0400 (2013-08-24)
changeset 4045 2891bbc5932b
parent 3752 619f3ef7f5fc
permissions -rw-r--r--
Fixed optional dependencies logging errors (again) (bug 5739, r=dvander).

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