diff --git a/core/logic/smn_adt_stack.cpp b/core/logic/smn_adt_stack.cpp index 085b4b0f..ef2ea737 100644 --- a/core/logic/smn_adt_stack.cpp +++ b/core/logic/smn_adt_stack.cpp @@ -1,8 +1,8 @@ /** -* vim: set ts=4 : +* vim: set ts=4 sw=4 tw=99 noet : * ============================================================================= * SourceMod -* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. +* Copyright (C) 2004-2014 AlliedModders LLC. All rights reserved. * ============================================================================= * * This program is free software; you can redistribute it and/or modify it under @@ -32,6 +32,7 @@ #include #include "common_logic.h" #include "CellArray.h" +#include "handle_helpers.h" HandleType_t htCellStack; @@ -293,6 +294,79 @@ static cell_t IsStackEmpty(IPluginContext *pContext, const cell_t *params) return 0; } +static cell_t ArrayStack_Pop(IPluginContext *pContext, const cell_t *params) +{ + OpenHandle array(pContext, params[1], htCellStack); + if (!array.Ok()) + return 0; + + if (array->size() == 0) + return pContext->ThrowNativeError("stack is empty"); + + cell_t *blk = array->at(array->size() - 1); + cell_t idx = (size_t)params[2]; + + cell_t rval; + if (params[3] == 0) { + if (idx >= array->blocksize()) + return pContext->ThrowNativeError("Invalid block %d (blocksize: %d)", idx, array->blocksize()); + rval = blk[idx]; + } else { + if (idx >= array->blocksize() * 4) + return pContext->ThrowNativeError("Invalid byte %d (blocksize: %d bytes)", idx, array->blocksize() * 4); + rval = (cell_t)*((char *)blk + idx); + } + + array->remove(array->size() - 1); + return rval; +} + +static cell_t ArrayStack_PopString(IPluginContext *pContext, const cell_t *params) +{ + OpenHandle array(pContext, params[1], htCellStack); + if (!array.Ok()) + return 0; + + if (array->size() == 0) + return pContext->ThrowNativeError("stack is empty"); + + size_t idx = array->size() - 1; + cell_t *blk = array->at(idx); + + cell_t *pWritten; + pContext->LocalToPhysAddr(params[4], &pWritten); + + size_t numWritten; + pContext->StringToLocalUTF8(params[2], params[3], (char *)blk, &numWritten); + *pWritten = (cell_t)numWritten; + array->remove(idx); + return 1; +} + +static cell_t ArrayStack_PopArray(IPluginContext *pContext, const cell_t *params) +{ + OpenHandle array(pContext, params[1], htCellStack); + if (!array.Ok()) + return 0; + + if (array->size() == 0) + return pContext->ThrowNativeError("stack is empty"); + + cell_t *addr; + pContext->LocalToPhysAddr(params[2], &addr); + + size_t idx = array->size() - 1; + cell_t *blk = array->at(idx); + size_t indexes = array->blocksize(); + + if (params[3] != -1 && (size_t)params[3] <= array->blocksize()) + indexes = params[3]; + + memcpy(addr, blk, sizeof(cell_t) * indexes); + array->remove(idx); + return 0; +} + REGISTER_NATIVES(cellStackNatives) { {"CreateStack", CreateStack}, @@ -303,5 +377,16 @@ REGISTER_NATIVES(cellStackNatives) {"PushStackArray", PushStackArray}, {"PushStackCell", PushStackCell}, {"PushStackString", PushStackString}, + + // Transitional syntax support. + {"ArrayStack.ArrayStack", CreateStack}, + {"ArrayStack.Pop", ArrayStack_Pop}, + {"ArrayStack.PopString", ArrayStack_PopString}, + {"ArrayStack.PopArray", ArrayStack_PopArray}, + {"ArrayStack.Push", PushStackCell}, + {"ArrayStack.PushString", PushStackString}, + {"ArrayStack.PushArray", PushStackArray}, + {"ArrayStack.Empty.get", IsStackEmpty}, + {NULL, NULL}, }; diff --git a/plugins/include/adt_stack.inc b/plugins/include/adt_stack.inc index b3102e75..f174696b 100644 --- a/plugins/include/adt_stack.inc +++ b/plugins/include/adt_stack.inc @@ -1,7 +1,7 @@ /** - * vim: set ts=4 : + * vim: set ts=4 sw=4 tw=99 noet : * ============================================================================= - * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved. + * SourceMod (C)2004-2014 AlliedModders LLC. All rights reserved. * ============================================================================= * * This file is part of the SourceMod/SourcePawn SDK. @@ -35,6 +35,82 @@ #endif #define _adt_stack_included +methodmap ArrayStack < Handle +{ + // 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] + public native ArrayStack(int 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 value Value to push. + public native void Push(any value); + + // Pushes a copy of a string onto the end of a stack, truncating it if it + // is too big. + // + // @param value String to push. + public native void PushString(const char[] value); + + // Pushes a copy of 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. + public native void PushArray(const any[] values, int size=-1); + + // Pops a cell value from a stack. + // + // @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 The stack is empty. + public native any Pop(int block=0, bool asChar=false); + + // Pops a string value from a stack. + // + // @param buffer Buffer to store string. + // @param maxlength Maximum size of the buffer. + // @oaram written Number of characters written to buffer, not including + // the null terminator. + // @error The stack is empty. + public native void PopString(char[] buffer, int maxlength, int &written = 0); + + // Pops an array of cells from a stack. + // + // @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. + // @error The stack is empty. + public native void PopArray(any[] buffer, int size=-1); + + // Returns true if the stack is empty, false otherwise. + property bool Empty { + public native get(); + } +}; + /** * 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. @@ -53,7 +129,7 @@ * new Array[X][32] * @return New stack Handle. */ -native Handle:CreateStack(blocksize=1); +native ArrayStack CreateStack(int blocksize=1); /** * Pushes a value onto the end of the stack, adding a new index. @@ -63,24 +139,22 @@ native Handle:CreateStack(blocksize=1); * * @param stack Stack Handle. * @param value Value to push. - * @noreturn * @error Invalid Handle or out of memory. */ -native PushStackCell(Handle:stack, any:value); +native void PushStackCell(Handle stack, any value); /** - * Pushes a string onto the end of a stack, truncating it if it is + * Pushes a copy of 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[]); +native void PushStackString(Handle stack, const char[] value); /** - * Pushes an array of cells onto the end of a stack. The cells + * Pushes a copy of 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. * @@ -89,10 +163,9 @@ native PushStackString(Handle:stack, const String:value[]); * @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 any:values[], size=-1); +native void PushStackArray(Handle stack, const any[] values, int size=-1); /** * Pops a cell value from a stack. @@ -105,7 +178,7 @@ native PushStackArray(Handle:stack, const any:values[], size=-1); * @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); +native bool PopStackCell(Handle stack, any &value, int block=0, bool asChar=false); /** * Pops a string value from a stack. @@ -116,7 +189,7 @@ native bool:PopStackCell(Handle:stack, &any:value, block=0, bool:asChar=false); * @return True on success, false if the stack is empty. * @error Invalid Handle. */ -native bool:PopStackString(Handle:stack, String:buffer[], maxlength, &written=0); +native bool PopStackString(Handle stack, char[] buffer, int maxlength, int &written=0); /** * Pops an array of cells from a stack. @@ -128,7 +201,7 @@ native bool:PopStackString(Handle:stack, String:buffer[], maxlength, &written=0) * @return True on success, false if the stack is empty. * @error Invalid Handle. */ -native bool:PopStackArray(Handle:stack, any:buffer[], size=-1); +native bool PopStackArray(Handle stack, any[] buffer, int size=-1); /** * Checks if a stack is empty. @@ -137,7 +210,7 @@ native bool:PopStackArray(Handle:stack, any:buffer[], size=-1); * @return True if empty, false if not empty. * @error Invalid Handle. */ -native bool:IsStackEmpty(Handle:stack); +native bool IsStackEmpty(Handle stack); /** * Pops a value off a stack, ignoring it completely. @@ -146,9 +219,8 @@ native bool:IsStackEmpty(Handle:stack); * @return True if something was popped, false otherwise. * @error Invalid Handle. */ -stock PopStack(Handle:stack) +stock bool PopStack(Handle stack) { new value; - return PopStackCell(stack, value); } diff --git a/plugins/testsuite/stacktest.sp b/plugins/testsuite/stacktest.sp index 65413cb5..71f7bc89 100644 --- a/plugins/testsuite/stacktest.sp +++ b/plugins/testsuite/stacktest.sp @@ -16,38 +16,36 @@ public OnPluginStart() public Action:Test_Stack(args) { - new Handle:stack; - new test[20] - decl String:buffer[42]; + int test[20]; + char 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); + ArrayStack stack = ArrayStack(30); + stack.Push(50); + stack.PushArray(test, 2); + stack.PushArray(test, 2); + stack.PushString("space craaab"); + stack.Push(12); - PrintToServer("empty? %d", IsStackEmpty(stack)); + PrintToServer("empty? %d", stack.Empty); - PopStack(stack); - PopStackString(stack, buffer, sizeof(buffer)); + stack.Pop(); + stack.PopString(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); + stack.PopArray(test, 2); PrintToServer("popped: %d, %d", test[0], test[1]); - PopStackCell(stack, test[0], 1); + test[0] = stack.Pop(1); PrintToServer("popped: x, %d", test[0]); - PopStackCell(stack, test[0]); + test[0] = stack.Pop(); PrintToServer("popped: %d", test[0]); - PrintToServer("empty? %d", IsStackEmpty(stack)); - - CloseHandle(stack); + PrintToServer("empty? %d", stack.Empty); + delete stack; return Plugin_Handled; }