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