diff --git a/core/CellArray.h b/core/CellArray.h index 42f8b452..c5ec78b0 100644 --- a/core/CellArray.h +++ b/core/CellArray.h @@ -40,14 +40,17 @@ public: CellArray(size_t blocksize) : m_Data(NULL), m_BlockSize(blocksize), m_AllocSize(0), m_Size(0) { } + ~CellArray() { free(m_Data); } + size_t size() const { return m_Size; } + cell_t *push() { if (!GrowIfNeeded(1)) @@ -58,18 +61,22 @@ public: m_Size++; return arr; } + cell_t *at(size_t b) const { return &m_Data[b * m_BlockSize]; } + size_t blocksize() const { return m_BlockSize; } + void clear() { m_Size = 0; } + bool swap(size_t item1, size_t item2) { /* Make sure there is extra space available */ @@ -90,6 +97,7 @@ public: return true; } + void remove(size_t index) { /* If we're at the end, take the easy way out */ @@ -107,6 +115,7 @@ public: m_Size--; } + cell_t *insert_at(size_t index) { /* Make sure it'll fit */ @@ -124,6 +133,7 @@ public: return src; } + bool resize(size_t count) { if (count <= m_Size) @@ -132,7 +142,7 @@ public: return true; } - if(!GrowIfNeeded(count - m_Size)) + if (!GrowIfNeeded(count - m_Size)) { return false; } diff --git a/core/Makefile b/core/Makefile index 3fcae6a3..ebebe82b 100644 --- a/core/Makefile +++ b/core/Makefile @@ -25,7 +25,7 @@ OBJECTS += smn_admin.cpp smn_bitbuffer.cpp smn_console.cpp smn_core.cpp \ smn_handles.cpp smn_keyvalues.cpp smn_banning.cpp smn_maplists.cpp \ smn_lang.cpp smn_player.cpp smn_string.cpp smn_sorting.cpp smn_textparse.cpp smn_timers.cpp \ smn_usermsgs.cpp smn_menus.cpp smn_database.cpp smn_vector.cpp smn_adt_array.cpp \ - smn_adt_trie.cpp smn_hudtext.cpp + smn_adt_trie.cpp smn_hudtext.cpp smn_adt_stack.cpp OBJECTS += systems/ExtensionSys.cpp systems/ForwardSys.cpp systems/HandleSys.cpp \ systems/LibrarySys.cpp systems/PluginInfoDatabase.cpp systems/PluginSys.cpp \ systems/ShareSys.cpp vm/sp_vm_basecontext.cpp vm/sp_vm_engine.cpp \ diff --git a/core/msvc8/sourcemod_mm.vcproj b/core/msvc8/sourcemod_mm.vcproj index 42b3a6c8..b2b47154 100644 --- a/core/msvc8/sourcemod_mm.vcproj +++ b/core/msvc8/sourcemod_mm.vcproj @@ -1347,6 +1347,10 @@ RelativePath="..\smn_adt_array.cpp" > + + diff --git a/core/smn_adt_stack.cpp b/core/smn_adt_stack.cpp new file mode 100644 index 00000000..0bf4cb68 --- /dev/null +++ b/core/smn_adt_stack.cpp @@ -0,0 +1,309 @@ +/** +* vim: set ts=4 : +* ============================================================================= +* SourceMod +* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. +* ============================================================================= +* +* This program is free software; you can redistribute it and/or modify it under +* the terms of the GNU General Public License, version 3.0, as published by the +* Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +* details. +* +* You should have received a copy of the GNU General Public License along with +* this program. If not, see . +* +* As a special exception, AlliedModders LLC gives you permission to link the +* code of this program (as well as its derivative works) to "Half-Life 2," the +* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software +* by the Valve Corporation. You must obey the GNU General Public License in +* all respects for all other code used. Additionally, AlliedModders LLC grants +* this exception to all derivative works. AlliedModders LLC defines further +* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), +* or . +* +* Version: $Id$ +*/ + +#include +#include "sm_globals.h" +#include "sm_stringutil.h" +#include "CellArray.h" +#include "HandleSys.h" + +HandleType_t htCellStack; + +class CellStackHelpers : + public SMGlobalClass, + public IHandleTypeDispatch +{ +public: //SMGlobalClass + void OnSourceModAllInitialized() + { + htCellStack = g_HandleSys.CreateType("CellStack", this, 0, NULL, NULL, g_pCoreIdent, NULL); + } + void OnSourceModShutdown() + { + g_HandleSys.RemoveType(htCellStack, g_pCoreIdent); + } +public: //IHandleTypeDispatch + void OnHandleDestroy(HandleType_t type, void *object) + { + CellArray *array = (CellArray *)object; + delete array; + } + bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize) + { + CellArray *pArray = (CellArray *)object; + *pSize = sizeof(CellArray) + pArray->mem_usage(); + return true; + } +} s_CellStackHelpers; + +static cell_t CreateStack(IPluginContext *pContext, const cell_t *params) +{ + if (params[1] < 1) + { + return pContext->ThrowNativeError("Invalid block size (must be > 0)"); + } + + CellArray *array = new CellArray(params[1]); + + Handle_t hndl = g_HandleSys.CreateHandle(htCellStack, array, pContext->GetIdentity(), g_pCoreIdent, NULL); + if (!hndl) + { + delete array; + } + + return hndl; +} + +static cell_t PushStackCell(IPluginContext *pContext, const cell_t *params) +{ + CellArray *array; + HandleError err; + HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent); + + if ((err = g_HandleSys.ReadHandle(params[1], htCellStack, &sec, (void **)&array)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err); + } + + cell_t *blk = array->push(); + if (!blk) + { + return pContext->ThrowNativeError("Failed to grow stack"); + } + + *blk = params[2]; + + return 1; +} + +static cell_t PushStackString(IPluginContext *pContext, const cell_t *params) +{ + CellArray *array; + HandleError err; + HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent); + + if ((err = g_HandleSys.ReadHandle(params[1], htCellStack, &sec, (void **)&array)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err); + } + + cell_t *blk = array->push(); + if (!blk) + { + return pContext->ThrowNativeError("Failed to grow stack"); + } + + char *str; + pContext->LocalToString(params[2], &str); + + strncopy((char *)blk, str, array->blocksize() * sizeof(cell_t)); + + return 1; +} + +static cell_t PushStackArray(IPluginContext *pContext, const cell_t *params) +{ + CellArray *array; + HandleError err; + HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent); + + if ((err = g_HandleSys.ReadHandle(params[1], htCellStack, &sec, (void **)&array)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err); + } + + cell_t *blk = array->push(); + if (!blk) + { + return pContext->ThrowNativeError("Failed to grow array"); + } + + cell_t *addr; + pContext->LocalToPhysAddr(params[2], &addr); + + size_t indexes = array->blocksize(); + if (params[3] != -1 && (size_t)params[3] <= array->blocksize()) + { + indexes = params[3]; + } + + memcpy(blk, addr, sizeof(cell_t) * indexes); + + return 1; +} + +static cell_t PopStackCell(IPluginContext *pContext, const cell_t *params) +{ + size_t idx; + HandleError err; + CellArray *array; + cell_t *blk, *buffer; + HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent); + + if ((err = g_HandleSys.ReadHandle(params[1], htCellStack, &sec, (void **)&array)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err); + } + + if (array->size() == 0) + { + return 0; + } + + pContext->LocalToPhysAddr(params[2], &buffer); + + blk = array->at(array->size() - 1); + idx = (size_t)params[3]; + + if (params[4] == 0) + { + if (idx >= array->blocksize()) + { + return pContext->ThrowNativeError("Invalid block %d (blocksize: %d)", idx, array->blocksize()); + } + *buffer = blk[idx]; + } + else + { + if (idx >= array->blocksize() * 4) + { + return pContext->ThrowNativeError("Invalid byte %d (blocksize: %d bytes)", idx, array->blocksize() * 4); + } + *buffer = (cell_t)*((char *)blk + idx); + } + + array->remove(array->size() - 1); + + return 1; +} + +static cell_t PopStackString(IPluginContext *pContext, const cell_t *params) +{ + HandleError err; + CellArray *array; + size_t idx, numWritten; + cell_t *blk, *pWritten; + HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent); + + if ((err = g_HandleSys.ReadHandle(params[1], htCellStack, &sec, (void **)&array)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err); + } + + if (array->size() == 0) + { + return 0; + } + + idx = array->size() - 1; + blk = array->at(idx); + pContext->StringToLocalUTF8(params[2], params[3], (char *)blk, &numWritten); + pContext->LocalToPhysAddr(params[4], &pWritten); + *pWritten = (cell_t)numWritten; + array->remove(idx); + + return 1; +} + +static cell_t PopStackArray(IPluginContext *pContext, const cell_t *params) +{ + size_t idx; + cell_t *blk; + cell_t *addr; + size_t indexes; + HandleError err; + CellArray *array; + HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent); + + if ((err = g_HandleSys.ReadHandle(params[1], htCellStack, &sec, (void **)&array)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err); + } + + if (array->size() == 0) + { + return 0; + } + + idx = array->size() - 1; + blk = array->at(idx); + indexes = array->blocksize(); + + if (params[3] != -1 && (size_t)params[3] <= array->blocksize()) + { + indexes = params[3]; + } + + pContext->LocalToPhysAddr(params[2], &addr); + memcpy(addr, blk, sizeof(cell_t) * indexes); + array->remove(idx); + + return indexes; +} + +static cell_t IsStackEmpty(IPluginContext *pContext, const cell_t *params) +{ + HandleError err; + CellArray *array; + HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent); + + if ((err = g_HandleSys.ReadHandle(params[1], htCellStack, &sec, (void **)&array)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err); + } + + if (array->size() == 0) + { + return 1; + } + + return 0; +} + +REGISTER_NATIVES(cellStackNatives) +{ + {"CreateStack", CreateStack}, + {"IsStackEmpty", IsStackEmpty}, + {"PopStackArray", PopStackArray}, + {"PopStackCell", PopStackCell}, + {"PopStackString", PopStackString}, + {"PushStackArray", PushStackArray}, + {"PushStackCell", PushStackCell}, + {"PushStackString", PushStackString}, + {NULL, NULL}, +}; diff --git a/plugins/include/adt.inc b/plugins/include/adt.inc index 62097783..1727fc93 100644 --- a/plugins/include/adt.inc +++ b/plugins/include/adt.inc @@ -37,3 +37,4 @@ #include #include +#include diff --git a/plugins/include/adt_stack.inc b/plugins/include/adt_stack.inc new file mode 100644 index 00000000..f47b1f3e --- /dev/null +++ b/plugins/include/adt_stack.inc @@ -0,0 +1,154 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#if defined _adt_stack_included + #endinput +#endif +#define _adt_stack_included + +/** + * Creates a stack structure. A stack is a LIFO (last in, first out) + * vector (array) of items. It has O(1) insertion and O(1) removal. + * + * Stacks have two operations: Push (adding an item) and Pop (removes + * items in reverse-push order). + * + * The contents of the stack are uniform; i.e. storing a string and then + * retrieving it as an integer is NOT the same as StringToInt()! + * + * The "blocksize" determines how many cells each slot has; it cannot + * be changed after creation. + * + * @param blocksize The number of cells each entry in the stack can + * hold. For example, 32 cells is equivalent to: + * new Array[X][32] + * @return New stack Handle. + */ +native Handle:CreateStack(blocksize=1); + +/** + * Pushes a value onto the end of the stack , adding a new index. + * + * This may safely be used even if the stack has a blocksize + * greater than 1. + * + * @param stack Stack Handle. + * @param value Value to push. + * @noreturn + * @error Invalid Handle or out of memory. + */ +native PushStackCell(Handle:stack, any:value); + +/** + * Pushes a string onto the end of a stack, truncating it if it is + * too big. + * + * @param stack Stack Handle. + * @param value String to push. + * @noreturn + * @error Invalid Handle or out of memory. + */ +native PushStackString(Handle:stack, const String:value[]); + +/** + * Pushes an array of cells onto the end of a stack. The cells + * are pushed as a block (i.e. the entire array takes up one stack slot), + * rather than pushing each cell individually. + * + * @param stack Stack Handle. + * @param values Block of values to copy. + * @param size If not set, the number of elements copied from the array + * will be equal to the blocksize. If set higher than the + * blocksize, the operation will be truncated. + * @noreturn + * @error Invalid Handle or out of memory. + */ +native PushStackArray(Handle:stack, const values[], size=-1); + +/** + * Pops a cell value from a stack. + * + * @param stack Stack Handle. + * @param value Variable to store the value. + * @param block Optionally specify which block to read from + * (useful if the blocksize > 0). + * @param asChar Optionally read as a byte instead of a cell. + * @return True on success, false if the stack is empty. + * @error Invalid Handle. + */ +native bool:PopStackCell(Handle:stack, &any:value, block=0, bool:asChar=false); + +/** + * Pops a string value from a stack. + * + * @param stack Stack Handle. + * @param buffer Buffer to store string. + * @param maxlength Maximum size of the buffer. + * @return True on success, false if the stack is empty. + * @error Invalid Handle. + */ +native bool:PopStackString(Handle:stack, String:buffer[], maxlength, &written=0); + +/** + * Pops an array of cells from a stack. + * + * @param stack Stack Handle. + * @param buffer Buffer to store the array in. + * @param size If not set, assumes the buffer size is equal to the + * blocksize. Otherwise, the size passed is used. + * @return True on success, false if the stack is empty. + * @error Invalid Handle. + */ +native bool:PopStackArray(Handle:stack, buffer[], size=-1); + +/** + * Checks if a stack is empty. + * + * @param stack Stack Handle. + * @return True if empty, false if not empty. + * @error Invalid Handle. + */ +native bool:IsStackEmpty(Handle:stack); + +/** + * Pops a value off a stack, ignoring it completely. + * + * @param stack Stack Handle. + * @return True if something was popped, false otherwise. + * @error Invalid Handle. + */ +stock PopStack(Handle:stack) +{ + new value; + + return PopStackCell(stack, value); +} diff --git a/plugins/testsuite/stacktest.sp b/plugins/testsuite/stacktest.sp new file mode 100644 index 00000000..65413cb5 --- /dev/null +++ b/plugins/testsuite/stacktest.sp @@ -0,0 +1,53 @@ +#include + +public Plugin:myinfo = +{ + name = "Stack Tests", + author = "AlliedModders LLC", + description = "Tests stack functions", + version = "1.0.0.0", + url = "http://www.sourcemod.net/" +}; + +public OnPluginStart() +{ + RegServerCmd("test_stack", Test_Stack); +} + +public Action:Test_Stack(args) +{ + new Handle:stack; + new test[20] + decl String:buffer[42]; + + test[0] = 5 + test[1] = 7 + + stack = CreateStack(30); + PushStackCell(stack, 50); + PushStackArray(stack, test, 2); + PushStackArray(stack, test, 2); + PushStackString(stack, "space craaab"); + PushStackCell(stack, 12); + + PrintToServer("empty? %d", IsStackEmpty(stack)); + + PopStack(stack); + PopStackString(stack, buffer, sizeof(buffer)); + PrintToServer("popped: \"%s\"", buffer); + test[0] = 0 + test[1] = 0 + PrintToServer("values: %d, %d", test[0], test[1]); + PopStackArray(stack, test, 2); + PrintToServer("popped: %d, %d", test[0], test[1]); + PopStackCell(stack, test[0], 1); + PrintToServer("popped: x, %d", test[0]); + PopStackCell(stack, test[0]); + PrintToServer("popped: %d", test[0]); + + PrintToServer("empty? %d", IsStackEmpty(stack)); + + CloseHandle(stack); + + return Plugin_Handled; +}