core/smn_sorting.cpp
author Scott Ehlert <ds@alliedmods.net>
Wed Aug 15 06:19:30 2007 +0000 (2007-08-15)
changeset 1330 ab94316bb52b
parent 1231 17badaf66ce4
child 1701 9cf7f48da853
child 1715 f9ecab642e25
permissions -rw-r--r--
Updated license headers on virtually all files with extremely minor changes in the name of some sort of strange consistency.
All plugin and include file headers also have been changed to say about GPL3 instead of GPL2.

(This day shall henceforth be known as the Eighty Column Massacre of '07)
     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 };