rewrote forward API to use cached parameter pushing instead of immediate pushing

removed copy back cruft since it's no longer needed
removed PushCells() from API requirements, not needed
adjusted documentation and added TODO list to ForwardSys.cpp
various internal improvements

--HG--
extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40188
This commit is contained in:
David Anderson 2006-11-12 09:51:39 +00:00
parent 442806dd45
commit bad69571b6
9 changed files with 181 additions and 433 deletions

View File

@ -142,7 +142,7 @@ namespace SourceMod
virtual int PushArray(cell_t *inarray,
unsigned int cells,
cell_t **phys_addr,
int flags=SMFUNC_COPYBACK_NONE) =0;
int flags=0) =0;
};
@ -307,48 +307,33 @@ namespace SourceMod
*
* Note that while #2,3,4 could be added to AMX Mod X, the real binding property is #1, which makes the system
* object oriented, rather than AMX Mod X, which hides the objects behind static functions. It is entirely a design
* issue, rather than a usability one. The interesting part is when it gets to implementation, which is when the
* problems for SourceMod arise. Specifically, the implementation is easier in GENERAL -- the tough part is the oddball
* cases.
* issue, rather than a usability one. The interesting part is when it gets to implementation, which has to cache
* parameter pushing until execution. Without this, multiple function calls can be started across one plugin, which
* will result in heap corruption given SourcePawn's implementation.
*
* Observe the calling process:
* - Each parameter is pushed using the ICallable interface. For each parameter:
* - For each function in the collection, the parameter is processed and pushed.
* Observe the new calling process:
* - Each parameter is pushed into a local cache using the ICallable interface.
* - For each function in the collection:
* - Each parameter is decoded and -pushed into the function.
* - The call is made.
* - Copy backs are performed.
* - Return
*
* Astute readers will note the problems - the parameters are processed individually for each push,
* rather than for each call. This means:
* Astute readers will note the (minor) problems:
* 1) More memory is used. Specifically, rather than N params of memory, you now have N params * M plugins.
* This is because, again, parameters are processed per function object, per-push, before the call.
* This is because, again, parameters are cached both per-function and per-forward.
* 2) There are slightly more calls going around: one extra call for each parameter, since each push is manual.
* 3) Copybacks won't work as expected.
*
* Number 3 is hard to see. For example, say a forward has two functions, and an array is pushed with copyback.
* The array gets pushed and copied into each plugin. When the call is made, each plugin now has separate copies of
* the array. When the copyback is performed, it gets mirrored to the originall address, but not to the next plugin!
* Implementing this is "difficult." To be re-entrant, an IPluginFunction can't tell you anything
* about its copybacks after it has returned, because its internal states were reset long ago.
*
* In order to implement this feature, IPluginFunction::Execute function now lets you pass a listener in.
* This listener is notified each time an array parameter is about to be copied back. Specifically, the forward
* uses this to detect when it needs to push a new parameter out to the next plugin. When the forward gets the
* call back, it detects whether there is another plugin in the queue. If there is, it grabs the address it will
* be using for the same arrays, and specifies it as the new copy-back point. If no plugins are left, it allows
* the copyback to chain up to the original address.
*
* This wonderful little hack is somewhat reminiscent of how SourceHook parameter rewrite chains work. It seems
* ugly at first, but it is actually the correct design pattern to apply to an otherwise awful problem.
*
* Note that there are other solutions to this that aren't based on a visitor-like pattern. For example, the Function
* class could expose a "set new original address" function. Then after arrays are pushed, the arrays are re-browsed,
* and function #1 gets function #2's original address, function #2 gets function #3's original address, et cetera.
* This extra browse step is a bit less efficient though, since the "visitor" method implies only taking action when
* necessary. Furthermore, it would require exposing such a "set address" function, which should fire a red flag
* that the API is doing something it shouldn't (namely, exposing the direct setting of very internal properties).
* HISTORICAL NOTES:
* There used to be a # about copy backs.
* Note that originally, the Forward implementation was a thin wrapper around IForwards. It did not cache pushes,
* and instead immediately fired them to each internal plugin. This was to allow users to know that pointers would
* be immediately resolved. Unfortunately, this became extremely burdensome on the API and exposed many problems,
* the major (and breaking) one was that two separate Function objects cannot be in a calling process on the same
* plugin at once. (:TODO: perhaps prevent that in the IPlugin object?) This is because heap functions lose their order
* and become impossible to re-arrange without some global heap tracking mechanis. It also made iterative copy backs
* for arrays/references overwhelmingly complex, since each plugin had to have its memory back-patched for each copy.
* Therefore, this was scrapped for cached parameters (current implementation), which is the implementation AMX Mod X
* uses. It is both faster and works better.
*/
#endif //_INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_

View File

@ -5,10 +5,7 @@
namespace SourceMod
{
#define SMFUNC_COPYBACK_NONE (0) /* Never copy an array back */
#define SMFUNC_COPYBACK_ONCE (1<<0) /* Copy an array back after call */
#define SMFUNC_COPYBACK_ALWAYS (1<<1) /* Copy an array back after subsequent calls (forwards) */
#define SMFUNC_ARRAY_NOINIT (1<<2) /* The array is not copied at first, but copyback is performed */
#define SM_FUNCFLAG_COPYBACK (1<<0) /* Copy an array/reference back after call */
/**
* @brief Represents what a function needs to implement in order to be callable.
@ -27,6 +24,9 @@ namespace SourceMod
/**
* @brief Pushes a cell by reference onto the current call.
* NOTE: On Execute, the pointer passed will be modified if copyback is enabled.
* NOTE: By reference parameters are cached and thus are not read until execution.
* This means you cannot push a pointer, change it, and push it again and expect
* two different values to come out.
*
* @param cell Address containing parameter value to push.
* @param flags Copy-back flags.
@ -45,6 +45,9 @@ namespace SourceMod
/**
* @brief Pushes a float onto the current call by reference.
* NOTE: On Execute, the pointer passed will be modified if copyback is enabled.
* NOTE: By reference parameters are cached and thus are not read until execution.
* This means you cannot push a pointer, change it, and push it again and expect
* two different values to come out.
*
* @param float Parameter value to push.
& @param flags Copy-back flags.
@ -52,21 +55,13 @@ namespace SourceMod
*/
virtual int PushFloatByRef(float *number, int flags) =0;
/**
* @brief Pushes an array of cells onto the current call, each cell individually.
* NOTE: This is essentially a crippled version of PushArray().
*
* @param array Array of cells.
* @param numcells Number of cells in array.
* @param each Whether or not to push as an array or individual cells.
* @return Error code, if any.
*/
virtual int PushCells(cell_t array[], unsigned int numcells, bool each) =0;
/**
* @brief Pushes an array of cells onto the current call.
* NOTE: On Execute, the pointer passed will be modified if non-NULL and copy-back
* is enabled.
* NOTE: By reference parameters are cached and thus are not read until execution.
* This means you cannot push a pointer, change it, and push it again and expect
* two different values to come out.
*
* @param array Array to copy, NULL if no initial array should be copied.
* @param cells Number of cells to allocate and optionally read from the input array.
@ -77,7 +72,7 @@ namespace SourceMod
virtual int PushArray(cell_t *inarray,
unsigned int cells,
cell_t **phys_addr,
int flags=SMFUNC_COPYBACK_NONE) =0;
int flags=0) =0;
/**
* @brief Pushes a string onto the current call.
@ -95,7 +90,7 @@ namespace SourceMod
* @param flags Copy-back flags.
* @return Error code, if any.
*/
virtual int PushStringByRef(char *string, int flags) =0;
virtual int PushStringEx(char *string, int flags) =0;
/**
* @brief Cancels a function call that is being pushed but not yet executed.
@ -104,30 +99,6 @@ namespace SourceMod
virtual void Cancel() =0;
};
/**
* @brief Used for copy-back notification
*/
class IFunctionCopybackReader
{
public:
/**
* @brief Called before an array is copied back.
*
* @param param Parameter index.
* @param cells Number of cells in the array.
* @param source_addr Source address in the plugin that will be copied.
* @param orig_addr Destination addres in plugin that will be overwritten.
* @param flags Copy flags.
* @return True to copy back, false otherwise.
*/
virtual bool OnCopybackArray(unsigned int param,
unsigned int cells,
cell_t *source_addr,
cell_t *orig_addr,
int flags) =0;
};
/**
* @brief Encapsulates a function call in a plugin.
* NOTE: Function calls must be atomic to one execution context.
@ -143,7 +114,7 @@ namespace SourceMod
* @param reader Copy-back listener. NULL to specify
* @return Error code, if any.
*/
virtual int Execute(cell_t *result, IFunctionCopybackReader *reader) =0;
virtual int Execute(cell_t *result) =0;
/**
* @brief Executes the function with the given parameter array.
@ -172,28 +143,6 @@ namespace SourceMod
* @return Address, or NULL if invalid parameter specified.
*/
virtual cell_t *GetAddressOfPushedParam(unsigned int param) =0;
#if 0
/**
* @brief Clones the function object. This can be used to maintain a private copy.
* The copy retains no push states and must be freeed with ReleaseCopy().
*/
virtual IPluginFunction *Copy() =0;
/**
* @brief If this object is a clone, this function must be called to destroy it.
*/
virtual void ReleaseCopy() =0;
/**
* @brief Returns original function pointer, if any.
* Note: IsCopyOf() will return the original non-copy, even if used
* from a copy.
*
* @return The original object that this was sourced from.
*/
virtual IPluginFunction *IsCopyOf() =0;
#endif
};
};

View File

@ -40,7 +40,7 @@
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\interfaces;..\;..\systems"
AdditionalIncludeDirectories="..\interfaces;..\;..\systems;..\..\sourcepawn\include"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;SOURCEMOD_MM_EXPORTS;_CRT_SECURE_NO_DEPRECATE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
@ -301,26 +301,6 @@
</File>
</Filter>
</Filter>
<Filter
Name="SDK"
>
<File
RelativePath="..\..\sourcepawn\include\sp_file_headers.h"
>
</File>
<File
RelativePath="..\..\sourcepawn\include\sp_vm_api.h"
>
</File>
<File
RelativePath="..\..\sourcepawn\include\sp_vm_base.h"
>
</File>
<File
RelativePath="..\..\sourcepawn\include\sp_vm_types.h"
>
</File>
</Filter>
<Filter
Name="zlib"
>
@ -449,10 +429,24 @@
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
Name="SDK"
>
<File
RelativePath="..\..\sourcepawn\include\sp_file_headers.h"
>
</File>
<File
RelativePath="..\..\sourcepawn\include\sp_vm_api.h"
>
</File>
<File
RelativePath="..\..\sourcepawn\include\sp_vm_base.h"
>
</File>
<File
RelativePath="..\..\sourcepawn\include\sp_vm_types.h"
>
</File>
</Filter>
</Filter>
</Files>

View File

@ -124,7 +124,7 @@ bool SourceModBase::InitializeSourceMod(char *error, size_t err_max, bool late)
fwd->AddFunction(func);
fwd->AddFunction(func2);
fwd->PushCell(1);
fwd->PushCellByRef(&val, SMFUNC_COPYBACK_ALWAYS);
fwd->PushCellByRef(&val, 0);
fwd->Execute(&result, NULL);
g_PluginMngr.UnloadPlugin(pPlugin);

View File

@ -76,7 +76,7 @@ int CFunction::PushCells(cell_t array[], unsigned int numcells, bool each)
{
if (!each)
{
return PushArray(array, numcells, NULL, SMFUNC_COPYBACK_NONE);
return PushArray(array, numcells, NULL, 0);
} else {
int err;
for (unsigned int i=0; i<numcells; i++)
@ -106,7 +106,7 @@ int CFunction::PushArray(cell_t *inarray, unsigned int cells, cell_t **phys_addr
return SetError(err);
}
info->flags = (inarray || (copyback & SMFUNC_COPYBACK_ALWAYS)) ? copyback : SMFUNC_COPYBACK_NONE;
info->flags = inarray ? copyback : 0;
info->marked = true;
info->size = cells;
m_params[m_curparam] = info->local_addr;
@ -114,10 +114,7 @@ int CFunction::PushArray(cell_t *inarray, unsigned int cells, cell_t **phys_addr
if (inarray)
{
if (!(copyback & SMFUNC_ARRAY_NOINIT))
{
memcpy(info->phys_addr, inarray, sizeof(cell_t) * cells);
}
memcpy(info->phys_addr, inarray, sizeof(cell_t) * cells);
info->orig_addr = inarray;
} else {
info->orig_addr = info->phys_addr;
@ -133,10 +130,10 @@ int CFunction::PushArray(cell_t *inarray, unsigned int cells, cell_t **phys_addr
int CFunction::PushString(const char *string)
{
return _PushString(string, SMFUNC_COPYBACK_NONE);
return _PushString(string, 0);
}
int CFunction::PushStringByRef(char *string, int flags)
int CFunction::PushStringEx(char *string, int flags)
{
return _PushString(string, flags);
}
@ -163,8 +160,7 @@ int CFunction::_PushString(const char *string, int flags)
m_params[m_curparam] = info->local_addr;
m_curparam++; /* Prevent a leak */
//:TODO: Use UTF-8 version
if ((err=base->StringToLocal(info->local_addr, len, string)) != SP_ERROR_NONE)
if ((err=base->StringToLocalUTF8(info->local_addr, len, string, NULL)) != SP_ERROR_NONE)
{
return SetError(err);
}
@ -197,7 +193,7 @@ void CFunction::Cancel()
m_errorstate = SP_ERROR_NONE;
}
int CFunction::Execute(cell_t *result, IFunctionCopybackReader *reader)
int CFunction::Execute(cell_t *result)
{
int err;
if (m_errorstate != SP_ERROR_NONE)
@ -236,17 +232,6 @@ int CFunction::Execute(cell_t *result, IFunctionCopybackReader *reader)
}
if (docopies && temp_info[numparams].flags)
{
if (reader)
{
if (!reader->OnCopybackArray(numparams,
temp_info[numparams].size,
temp_info[numparams].phys_addr,
temp_info[numparams].orig_addr,
temp_info[numparams].flags))
{
goto _skipcopy;
}
}
if (temp_info[numparams].orig_addr)
{
if (temp_info[numparams].size == 1)
@ -259,7 +244,6 @@ int CFunction::Execute(cell_t *result, IFunctionCopybackReader *reader)
}
}
}
_skipcopy:
base->HeapPop(temp_info[numparams].local_addr);
temp_info[numparams].marked = false;
}

View File

@ -28,9 +28,9 @@ public:
virtual int PushCells(cell_t array[], unsigned int numcells, bool each);
virtual int PushArray(cell_t *inarray, unsigned int cells, cell_t **phys_addr, int copyback);
virtual int PushString(const char *string);
virtual int PushStringByRef(char *string, int flags);
virtual int PushStringEx(char *string, int flags);
virtual cell_t *GetAddressOfPushedParam(unsigned int param);
virtual int Execute(cell_t *result, IFunctionCopybackReader *reader);
virtual int Execute(cell_t *result);
virtual void Cancel();
virtual int CallFunction(const cell_t *params, unsigned int num_params, cell_t *result);
virtual IPlugin *GetParentPlugin();

View File

@ -2,6 +2,30 @@
#include "ForwardSys.h"
#include "PluginSys.h"
/**
* Gensis turns to its source, reduction occurs stepwise although the essence is all one.
* End of line. FTL system check.
*
* :TODO: Implement the manager. ho ho ho
*
* :TODO: WHAT NEEDS TO BE TESTED IN THIS BEAST (X=done, -=TODO)
* NORMAL FUNCTIONS:
* X Push cells
* X Push cells byref (copyback tested = yes)
* - Push floats (copyback tested = ??)
* - Push floats byref (copyback tested = ??)
* - Push arrays (copyback tested = ??)
* - Push strings (copyback tested = ??)
* VARARG FUNCTIONS:
* - Pushing no varargs
* - Push vararg cells (copyback should be verified to not happen = ??)
* - Push vararg cells byref (copyback tested = ??)
* - Push vararg floats (copyback should be verified to not happen = ??)
* - Push vararg floats byref (copyback tested = ??)
* - Push vararg arrays (copyback tested = ??)
* - Push vararg strings (copyback tested = ??)
*/
CForward *CForward::CreateForward(const char *name, ExecType et, unsigned int num_params, ParamType *types, va_list ap)
{
ParamType _types[SP_MAX_EXEC_PARAMS];
@ -50,48 +74,17 @@ CForward *CForward::CreateForward(const char *name, ExecType et, unsigned int nu
if (num_params && types[num_params-1] == Param_VarArgs)
{
pForward->m_varargs = true;
pForward->m_varargs = num_params--;
} else {
pForward->m_varargs = false;
}
pForward->m_numparams = num_params;
pForward->m_errstate = SP_ERROR_NONE;
pForward->m_CopyBacks.numrecopy = 0;
return pForward;
}
bool CForward::OnCopybackArray(unsigned int param,
unsigned int cells,
cell_t *source_addr,
cell_t *orig_addr,
int flags)
{
/* Check if the stack is empty, this should be an assertion */
if (m_NextStack.empty())
{
/* This should never happen! */
assert(!m_NextStack.empty());
return true;
}
/* Check if we even want to copy to the next plugin */
if (!(flags & SMFUNC_COPYBACK_ALWAYS))
{
return true;
}
/* Keep track of the copy back and save the info */
NextCallInfo &info = m_NextStack.front();
info.recopy[info.numrecopy++] = param;
info.orig_addrs[param] = orig_addr;
info.sizes[param] = cells;
/* We don't want to override the copy. */
return true;
}
int CForward::Execute(cell_t *result, IForwardFilter *filter)
{
if (m_errstate)
@ -101,9 +94,6 @@ int CForward::Execute(cell_t *result, IForwardFilter *filter)
return err;
}
/* Reset marker */
m_curparam = 0;
if (filter)
{
filter->OnExecuteBegin();
@ -116,53 +106,53 @@ int CForward::Execute(cell_t *result, IForwardFilter *filter)
int err;
unsigned int failed=0, success=0;
unsigned int save_numcopy = 0;
unsigned int num_params = m_curparam;
FwdParamInfo temp_info[SP_MAX_EXEC_PARAMS];
FwdParamInfo *param;
ParamType type;
/* Save local, reset */
memcpy(temp_info, m_params, sizeof(m_params));
m_curparam = 0;
/**
* Save copyback into to start the chain,
* then reset it for re-entrancy
*/
m_NextStack.push(m_CopyBacks);
NextCallInfo &info = m_NextStack.front();
m_CopyBacks.numrecopy = 0;
for (iter=m_functions.begin(); iter!=m_functions.end(); iter++)
{
func = (*iter);
/**
* Check if we need to copy a new array back into the plugin.
*/
if (info.numrecopy)
{
/* The last plugin has a chained copyback, we must redirect it here. */
unsigned int param;
cell_t *orig_addr, *targ_addr;
for (unsigned int i=0; i<info.numrecopy; i++)
{
/* Get the parameter info to copy */
param = info.recopy[i];
targ_addr = func->GetAddressOfPushedParam(param);
orig_addr = info.orig_addrs[param];
/* Only do the copy for valid targets */
if (targ_addr && orig_addr)
{
if (info.sizes[param] == 1)
{
*targ_addr = *orig_addr;
} else {
memcpy(targ_addr, orig_addr, info.sizes[param] * sizeof(cell_t));
}
}
/* If this failed, the plugin will most likely be failing as well. */
}
save_numcopy = info.numrecopy;
info.numrecopy = 0;
}
for (unsigned int i=0; i<num_params; i++)
{
param = &temp_info[i];
if (i >= m_numparams || m_types[i] == Param_Any)
{
type = param->pushedas;
} else {
type = m_types[i];
}
if ((i >= m_numparams) || (type & SP_PARAMFLAG_BYREF))
{
/* If we're byref or we're vararg, we always push everything by ref.
* Even if they're byval, we must push them byref.
*/
if (type == Param_String)
{
func->PushStringEx((char *)param->byref.orig_addr, param->byref.flags);
} else if (type == Param_Float || type == Param_Cell) {
func->PushCellByRef(&param->val, 0);
} else {
func->PushArray(param->byref.orig_addr, param->byref.cells, NULL, param->byref.flags);
assert(type == Param_Array || type == Param_FloatByRef || type == Param_CellByRef);
}
} else {
/* If we're not byref or not vararg, our job is a bit easier. */
assert(type == Param_Cell || type == Param_Float);
func->PushCell(param->val);
}
}
/* Call the function and deal with the return value.
* :TODO: only pass reader if we know we have an array in the list
*/
if ((err=func->Execute(&cur_result, this)) != SP_ERROR_NONE)
if ((err=func->Execute(&cur_result)) != SP_ERROR_NONE)
{
bool handled = false;
if (filter)
@ -173,10 +163,6 @@ int CForward::Execute(cell_t *result, IForwardFilter *filter)
{
/* :TODO: invoke global error reporting here */
}
/* If we failed, we're not quite done. The copy chain has been broken.
* We have to restore it so past changes will continue to get mirrored.
*/
info.numrecopy = save_numcopy;
failed++;
} else {
success++;
@ -218,8 +204,6 @@ int CForward::Execute(cell_t *result, IForwardFilter *filter)
}
}
m_NextStack.pop();
if (m_ExecType == ET_Event || m_ExecType == ET_Hook)
{
cur_result = high_result;
@ -229,8 +213,6 @@ int CForward::Execute(cell_t *result, IForwardFilter *filter)
*result = cur_result;
DumpAdditionQueue();
if (filter)
{
filter->OnExecuteEnd(&cur_result, success, failed);
@ -243,8 +225,10 @@ int CForward::PushCell(cell_t cell)
{
if (m_curparam < m_numparams)
{
if (m_types[m_curparam] != Param_Cell && m_types[m_curparam] != Param_Any)
if (m_types[m_curparam] == Param_Any)
{
m_params[m_curparam].pushedas = Param_Cell;
} else if (m_types[m_curparam] != Param_Cell) {
return SetError(SP_ERROR_PARAM);
}
} else {
@ -252,17 +236,10 @@ int CForward::PushCell(cell_t cell)
{
return SetError(SP_ERROR_PARAMS_MAX);
}
m_params[m_curparam].pushedas = Param_Cell;
}
FuncIter iter;
IPluginFunction *func;
for (iter=m_functions.begin(); iter!=m_functions.end(); iter++)
{
func = (*iter);
func->PushCell(cell);
}
m_curparam++;
m_params[m_curparam++].val = cell;
return SP_ERROR_NONE;
}
@ -271,8 +248,10 @@ int CForward::PushFloat(float number)
{
if (m_curparam < m_numparams)
{
if (m_types[m_curparam] != Param_Float && m_types[m_curparam] != Param_Any)
if (m_types[m_curparam] == Param_Any)
{
m_params[m_curparam].pushedas = Param_Float;
} else if (m_types[m_curparam] != Param_Float) {
return SetError(SP_ERROR_PARAM);
}
} else {
@ -280,17 +259,10 @@ int CForward::PushFloat(float number)
{
return SetError(SP_ERROR_PARAMS_MAX);
}
m_params[m_curparam].pushedas = Param_Float;
}
FuncIter iter;
IPluginFunction *func;
for (iter=m_functions.begin(); iter!=m_functions.end(); iter++)
{
func = (*iter);
func->PushFloat(number);
}
m_curparam++;
m_params[m_curparam++].val = *(cell_t *)&number;
return SP_ERROR_NONE;
}
@ -310,19 +282,7 @@ int CForward::PushCellByRef(cell_t *cell, int flags)
}
}
if (flags & SMFUNC_COPYBACK_ALWAYS)
{
_Int_PushArray(cell, 1, flags);
} else {
FuncIter iter;
IPluginFunction *func;
for (iter=m_functions.begin(); iter!=m_functions.end(); iter++)
{
func = (*iter);
func->PushCellByRef(cell, flags);
}
}
_Int_PushArray(cell, 1, flags);
m_curparam++;
return SP_ERROR_NONE;
@ -343,93 +303,17 @@ int CForward::PushFloatByRef(float *num, int flags)
}
}
if (flags & SMFUNC_COPYBACK_ALWAYS)
{
_Int_PushArray((cell_t *)num, 1, flags);
} else {
FuncIter iter;
IPluginFunction *func;
for (iter=m_functions.begin(); iter!=m_functions.end(); iter++)
{
func = (*iter);
func->PushFloatByRef(num, flags);
}
}
_Int_PushArray((cell_t *)num, 1, flags);
m_curparam++;
return SP_ERROR_NONE;
}
int CForward::PushCells(cell_t array[], unsigned int numcells, bool each)
{
if (each)
{
/* Type check each cell if we need to! */
if (m_curparam + numcells >= m_numparams && !m_varargs)
{
return SetError(SP_ERROR_PARAMS_MAX);
} else {
for (unsigned int i=m_curparam; i<m_numparams; i++)
{
if (m_types[i] != Param_Any || m_types[i] != Param_Cell)
{
return SetError(SP_ERROR_PARAM);
}
}
}
} else {
if (m_curparam < m_numparams)
{
if (m_types[m_curparam] != Param_Any || m_types[m_curparam] == Param_Array)
{
return SetError(SP_ERROR_PARAM);
}
} else {
if (!m_varargs || m_curparam > SP_MAX_EXEC_PARAMS)
{
return SetError(SP_ERROR_PARAMS_MAX);
}
}
}
FuncIter iter;
IPluginFunction *func;
for (iter=m_functions.begin(); iter!=m_functions.end(); iter++)
{
func = (*iter);
func->PushCells(array, numcells, each);
}
m_curparam += each ? numcells : 1;
return SP_ERROR_NONE;
}
void CForward::_Int_PushArray(cell_t *inarray, unsigned int cells, int flags)
{
FuncIter iter;
IPluginFunction *func;
if (flags & SMFUNC_COPYBACK_ALWAYS)
{
/* As a special optimization, we create blank default arrays because they will be
* copied over anyway!
*/
for (iter=m_functions.begin(); iter!=m_functions.end(); iter++)
{
func = (*iter);
func->PushArray(inarray, cells, NULL, flags|SMFUNC_ARRAY_NOINIT);
}
m_CopyBacks.recopy[m_CopyBacks.numrecopy++] = m_curparam;
m_CopyBacks.orig_addrs[m_curparam] = inarray;
m_CopyBacks.sizes[m_curparam] = cells;
} else {
for (iter=m_functions.begin(); iter!=m_functions.end(); iter++)
{
func = (*iter);
func->PushArray(inarray, cells, NULL, flags);
}
}
m_params[m_curparam].byref.cells = cells;
m_params[m_curparam].byref.flags = flags;
m_params[m_curparam].byref.orig_addr = inarray;
}
int CForward::PushArray(cell_t *inarray, unsigned int cells, cell_t **phys_addr, int flags)
@ -442,8 +326,10 @@ int CForward::PushArray(cell_t *inarray, unsigned int cells, cell_t **phys_addr,
if (m_curparam < m_numparams)
{
if (m_types[m_curparam] != Param_Any || m_types[m_curparam] == Param_Array)
if (m_types[m_curparam] == Param_Any)
{
m_params[m_curparam].pushedas = Param_Array;
} else if (m_types[m_curparam] != Param_Array) {
return SetError(SP_ERROR_PARAM);
}
} else {
@ -451,6 +337,7 @@ int CForward::PushArray(cell_t *inarray, unsigned int cells, cell_t **phys_addr,
{
return SetError(SP_ERROR_PARAMS_MAX);
}
m_params[m_curparam].pushedas = Param_Array;
}
if (phys_addr)
@ -469,8 +356,10 @@ int CForward::PushString(const char *string)
{
if (m_curparam < m_numparams)
{
if (m_types[m_curparam] != Param_Any || m_types[m_curparam] == Param_String)
if (m_types[m_curparam] == Param_Any)
{
m_params[m_curparam].pushedas = Param_String;
} else if (m_types[m_curparam] == Param_String) {
return SetError(SP_ERROR_PARAM);
}
} else {
@ -478,27 +367,23 @@ int CForward::PushString(const char *string)
{
return SetError(SP_ERROR_PARAMS_MAX);
}
m_params[m_curparam].pushedas = Param_String;
}
FuncIter iter;
IPluginFunction *func;
for (iter=m_functions.begin(); iter!=m_functions.end(); iter++)
{
func = (*iter);
func->PushString(string);
}
_Int_PushArray((cell_t *)string, 0, 0);
m_curparam++;
return SP_ERROR_NONE;
}
int CForward::PushStringByRef(char *string, int flags)
int CForward::PushStringEx(char *string, int flags)
{
if (m_curparam < m_numparams)
{
if (m_types[m_curparam] != Param_Any || m_types[m_curparam] == Param_String)
if (m_types[m_curparam] == Param_Any)
{
m_params[m_curparam].pushedas = Param_String;
} else if (m_types[m_curparam] == Param_String) {
return SetError(SP_ERROR_PARAM);
}
} else {
@ -506,16 +391,10 @@ int CForward::PushStringByRef(char *string, int flags)
{
return SetError(SP_ERROR_PARAMS_MAX);
}
m_params[m_curparam].pushedas = Param_String;
}
FuncIter iter;
IPluginFunction *func;
for (iter=m_functions.begin(); iter!=m_functions.end(); iter++)
{
func = (*iter);
func->PushStringByRef(string, flags);
}
_Int_PushArray((cell_t *)string, 0, flags);
m_curparam++;
return SP_ERROR_NONE;
@ -528,18 +407,7 @@ void CForward::Cancel()
return;
}
FuncIter iter;
IPluginFunction *func;
for (iter=m_functions.begin(); iter!=m_functions.end(); iter++)
{
func = (*iter);
func->Cancel();
}
m_CopyBacks.numrecopy = 0;
DumpAdditionQueue();
m_curparam = 0;
m_errstate = SP_ERROR_NONE;
}
@ -578,11 +446,8 @@ bool CForward::RemoveFunction(IPluginFunction *func)
}
}
/* Just in case */
m_AddQueue.remove(func);
/* Cancel a call, if any */
if (found && (!m_NextStack.empty() || m_curparam))
if (found || m_curparam)
{
func->Cancel();
}
@ -607,32 +472,9 @@ unsigned int CForward::RemoveFunctionsOfPlugin(IPlugin *plugin)
}
}
for (iter=m_AddQueue.begin(); iter!=m_AddQueue.end();)
{
func = (*iter);
if (func->GetParentPlugin() == plugin)
{
/* Don't count these toward the total */
iter = m_functions.erase(iter);
} else {
iter++;
}
}
return removed;
}
void CForward::DumpAdditionQueue()
{
FuncIter iter = m_AddQueue.begin();
while (iter != m_AddQueue.end())
{
m_functions.push_back((*iter));
//:TODO: eventually we will tell the plugin we're using it
iter = m_AddQueue.erase(iter);
}
}
bool CForward::AddFunction(IPluginFunction *func)
{
if (m_curparam)
@ -640,13 +482,8 @@ bool CForward::AddFunction(IPluginFunction *func)
return false;
}
if (!m_NextStack.empty())
{
m_AddQueue.push_back(func);
} else {
//:TODO: eventually we will tell the plugin we're using it
m_functions.push_back(func);
}
//:TODO: eventually we will tell the plugin we're using it
m_functions.push_back(func);
return true;
}

View File

@ -14,25 +14,30 @@ typedef List<IPluginFunction *>::iterator FuncIter;
//:TODO: a global name max define for sourcepawn, should mirror compiler's sNAMEMAX
#define FORWARDS_NAME_MAX 64
struct NextCallInfo
struct ByrefInfo
{
unsigned int recopy[SP_MAX_EXEC_PARAMS];
unsigned int sizes[SP_MAX_EXEC_PARAMS];
cell_t *orig_addrs[SP_MAX_EXEC_PARAMS];
unsigned int numrecopy;
unsigned int cells;
cell_t *orig_addr;
int flags;
};
class CForward : public IChangeableForward, IFunctionCopybackReader
struct FwdParamInfo
{
cell_t val;
ByrefInfo byref;
ParamType pushedas;
};
class CForward : public IChangeableForward
{
public: //ICallable
virtual int PushCell(cell_t cell);
virtual int PushCellByRef(cell_t *cell, int flags);
virtual int PushFloat(float number);
virtual int PushFloatByRef(float *number, int flags);
virtual int PushCells(cell_t array[], unsigned int numcells, bool each);
virtual int PushArray(cell_t *inarray, unsigned int cells, cell_t **phys_addr, int flags);
virtual int PushString(const char *string);
virtual int PushStringByRef(char *string, int flags);
virtual int PushStringEx(char *string, int flags);
virtual void Cancel();
public: //IForward
virtual const char *GetForwardName();
@ -44,12 +49,6 @@ public: //IChangeableForward
virtual unsigned int RemoveFunctionsOfPlugin(IPlugin *plugin);
virtual bool AddFunction(IPluginFunction *func);
virtual bool AddFunction(sp_context_t *ctx, funcid_t index);
public: //IFunctionCopybackReader
virtual bool OnCopybackArray(unsigned int param,
unsigned int cells,
cell_t *source_addr,
cell_t *orig_addr,
int flags);
public:
static CForward *CreateForward(const char *name,
ExecType et,
@ -57,7 +56,6 @@ public:
ParamType *types,
va_list ap);
private:
void DumpAdditionQueue();
void _Int_PushArray(cell_t *inarray, unsigned int cells, int flags);
inline int SetError(int err)
{
@ -71,18 +69,16 @@ protected:
List<IPluginFunction *> m_functions;
/* Type and name information */
FwdParamInfo m_params[SP_MAX_EXEC_PARAMS];
ParamType m_types[SP_MAX_EXEC_PARAMS];
char m_name[FORWARDS_NAME_MAX+1];
unsigned int m_numparams;
bool m_varargs;
unsigned int m_varargs;
ExecType m_ExecType;
/* State information */
unsigned int m_curparam;
int m_errstate;
CStack<NextCallInfo> m_NextStack;
List<IPluginFunction *> m_AddQueue;
NextCallInfo m_CopyBacks;
};
#endif //_INCLUDE_SOURCEMOD_FORWARDSYSTEM_H_

View File

@ -216,10 +216,10 @@ namespace SourcePawn
* Converts a physical string to a local address.
*
* @param local_addr Local address in plugin.
* @param chars Number of chars to write, including NULL terminator.
* @param bytes Number of chars to write, including NULL terminator.
* @param source Source string to copy.
*/
virtual int StringToLocal(cell_t local_addr, size_t chars, const char *source) =0;
virtual int StringToLocal(cell_t local_addr, size_t bytes, const char *source) =0;
/**
* Converts a physical UTF-8 string to a local address.
@ -229,9 +229,12 @@ namespace SourcePawn
* @param local_addr Local address in plugin.
* @param maxbytes Number of bytes to write, including NULL terminator.
* @param source Source string to copy.
* @param wrtnbytes Optionally filled with the number of written bytes.
* @param wrtnbytes Optionally set to the number of actual bytes written.
*/
virtual int StringToLocalUTF8(cell_t local_addr, size_t maxbytes, const char *source, size_t *wrtnbytes) =0;
virtual int StringToLocalUTF8(cell_t local_addr,
size_t maxbytes,
const char *source,
size_t *wrtnbytes) =0;
/**
* Pushes a cell onto the stack. Increases the parameter count by one.