mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-08 17:59:37 +00:00
414 lines
9.8 KiB
C++
414 lines
9.8 KiB
C++
/*
|
|
_____ __ ___ __ ____ _ __
|
|
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
|
|
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
|
|
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
|
|
/___/ /_/
|
|
|
|
See Copyright Notice in gmMachine.h
|
|
|
|
*/
|
|
|
|
#include "gmConfig.h"
|
|
#include "gmArrayLib.h"
|
|
#include "gmThread.h"
|
|
#include "gmMachine.h"
|
|
#include "gmHelpers.h"
|
|
|
|
// Must be last header
|
|
#include "memdbgon.h"
|
|
|
|
//
|
|
//
|
|
// Implementation of array binding
|
|
//
|
|
//
|
|
|
|
#if GM_ARRAY_LIB
|
|
|
|
// Statics and globals
|
|
gmType GM_ARRAY = GM_NULL;
|
|
gmVariable gmUserArray::m_null;
|
|
|
|
|
|
bool gmUserArray::Construct(gmMachine * a_machine, int a_size)
|
|
{
|
|
m_null.Nullify();
|
|
m_array = NULL;
|
|
m_size = 0;
|
|
return Resize(a_machine, a_size);
|
|
}
|
|
|
|
|
|
void gmUserArray::Destruct(gmMachine * a_machine)
|
|
{
|
|
if(m_array)
|
|
{
|
|
a_machine->Sys_Free(m_array);
|
|
m_array = NULL;
|
|
}
|
|
m_size = 0;
|
|
}
|
|
|
|
|
|
bool gmUserArray::Resize(gmMachine * a_machine, int a_size)
|
|
{
|
|
if(a_size < 0) a_size = 0;
|
|
int copysize = (a_size > m_size) ? m_size : a_size;
|
|
gmVariable * array = (gmVariable *) a_machine->Sys_Alloc(sizeof(gmVariable) * a_size);
|
|
// copy contents.
|
|
if(m_array)
|
|
{
|
|
memcpy(array, m_array, sizeof(gmVariable) * copysize);
|
|
if(a_size > copysize)
|
|
{
|
|
memset(array + copysize, 0, sizeof(gmVariable) * (a_size - copysize));
|
|
}
|
|
a_machine->Sys_Free(m_array);
|
|
}
|
|
else
|
|
{
|
|
memset(array, 0, sizeof(gmVariable) * a_size);
|
|
}
|
|
m_array = array;
|
|
m_size = a_size;
|
|
return true;
|
|
}
|
|
|
|
#if GM_USE_INCGC
|
|
bool gmUserArray::Trace(gmMachine * a_machine, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone)
|
|
{
|
|
int i;
|
|
for(i = 0; i < m_size; ++i)
|
|
{
|
|
if(m_array[i].IsReference())
|
|
{
|
|
gmObject * object = GM_MOBJECT(a_machine, m_array[i].m_value.m_ref);
|
|
a_gc->GetNextObject(object);
|
|
++a_workDone;
|
|
}
|
|
}
|
|
|
|
++a_workDone;
|
|
return true;
|
|
}
|
|
#else //GM_USE_INCGC
|
|
void gmUserArray::Mark(gmMachine * a_machine, gmuint32 a_mark)
|
|
{
|
|
int i;
|
|
for(i = 0; i < m_size; ++i)
|
|
{
|
|
if(m_array[i].IsReference())
|
|
{
|
|
gmObject * object = GM_MOBJECT(a_machine, m_array[i].m_value.m_ref);
|
|
if(object->NeedsMark(a_mark)) object->Mark(a_machine, a_mark);
|
|
}
|
|
}
|
|
}
|
|
#endif //GM_USE_INCGC
|
|
|
|
|
|
bool gmUserArray::Shift(int a_shift)
|
|
{
|
|
if(a_shift < 0) // shift left
|
|
{
|
|
a_shift = -a_shift;
|
|
if(a_shift >= m_size)
|
|
{
|
|
memset(m_array, 0, m_size);
|
|
}
|
|
else
|
|
{
|
|
int size = m_size - a_shift;
|
|
memmove(m_array, m_array + a_shift, sizeof(gmVariable) * size);
|
|
memset(m_array + size, 0, sizeof(gmVariable) * a_shift);
|
|
}
|
|
}
|
|
else if(a_shift > 0) // shift right
|
|
{
|
|
if(a_shift >= m_size)
|
|
{
|
|
memset(m_array, 0, m_size);
|
|
}
|
|
else
|
|
{
|
|
int size = m_size - a_shift;
|
|
memmove(m_array + a_shift, m_array, sizeof(gmVariable) * size);
|
|
memset(m_array, 0, sizeof(gmVariable) * a_shift);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
int gmUserArray::Move(int a_dest, int a_src, int a_size)
|
|
{
|
|
int start = a_src;
|
|
int dest = a_dest;
|
|
int size = a_size;
|
|
|
|
if(start < 0)
|
|
{
|
|
size += start;
|
|
dest -= start;
|
|
start = 0;
|
|
}
|
|
|
|
if(dest < 0)
|
|
{
|
|
size += dest;
|
|
start -= dest;
|
|
dest = 0;
|
|
}
|
|
|
|
if(size <= 0) return 0;
|
|
if(start >= m_size) return 0;
|
|
if(dest >= m_size) return 0;
|
|
if((dest + size) < 0) return 0;
|
|
|
|
if(start + size > m_size)
|
|
{
|
|
size = m_size - start;
|
|
}
|
|
if(dest + size > m_size)
|
|
{
|
|
size = m_size - dest;
|
|
}
|
|
if(size <= 0) return 0;
|
|
|
|
GM_ASSERT(dest >= 0);
|
|
GM_ASSERT(start >= 0);
|
|
GM_ASSERT(start + size <= m_size);
|
|
GM_ASSERT(dest + size <= m_size);
|
|
|
|
memmove(m_array + dest, m_array + start, sizeof(gmVariable) * size);
|
|
|
|
return size;
|
|
}
|
|
|
|
gmUserArray* gmUserArray_Create(gmMachine* a_machine, int a_size)
|
|
{
|
|
gmUserArray * newArray = (gmUserArray *) a_machine->Sys_Alloc(sizeof(gmUserArray));
|
|
newArray->Construct(a_machine, a_size);
|
|
return newArray;
|
|
}
|
|
|
|
|
|
// functions
|
|
|
|
static int GM_CDECL gmfArray(gmThread * a_thread) // size
|
|
{
|
|
GM_INT_PARAM(size, 0, 0);
|
|
gmUserArray * array = (gmUserArray *) a_thread->GetMachine()->Sys_Alloc(sizeof(gmUserArray));
|
|
array->Construct(a_thread->GetMachine(), size);
|
|
a_thread->PushNewUser(array, GM_ARRAY);
|
|
return GM_OK;
|
|
}
|
|
|
|
static int GM_CDECL gmfArraySize(gmThread * a_thread) // return size
|
|
{
|
|
gmUserObject * arrayObject = a_thread->ThisUserObject();
|
|
GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
|
|
if(arrayObject->m_user)
|
|
{
|
|
gmUserArray * array = (gmUserArray *) arrayObject->m_user;
|
|
a_thread->PushInt(array->Size());
|
|
}
|
|
return GM_OK;
|
|
}
|
|
|
|
static int GM_CDECL gmfArrayResize(gmThread * a_thread) // size
|
|
{
|
|
GM_INT_PARAM(size, 0, 0);
|
|
gmUserObject * arrayObject = a_thread->ThisUserObject();
|
|
GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
|
|
if(arrayObject->m_user)
|
|
{
|
|
gmUserArray * array = (gmUserArray *) arrayObject->m_user;
|
|
array->Resize(a_thread->GetMachine(), size);
|
|
}
|
|
return GM_OK;
|
|
}
|
|
|
|
static int GM_CDECL gmfArrayShift(gmThread * a_thread) // shift
|
|
{
|
|
GM_CHECK_NUM_PARAMS(1);
|
|
GM_CHECK_INT_PARAM(shift, 0);
|
|
|
|
gmUserObject * arrayObject = a_thread->ThisUserObject();
|
|
GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
|
|
if(arrayObject->m_user)
|
|
{
|
|
gmUserArray * array = (gmUserArray *) arrayObject->m_user;
|
|
array->Shift(shift);
|
|
}
|
|
return GM_OK;
|
|
}
|
|
|
|
static int GM_CDECL gmfArrayMove(gmThread * a_thread) // dst, src, size
|
|
{
|
|
GM_CHECK_NUM_PARAMS(3);
|
|
GM_CHECK_INT_PARAM(dst, 0);
|
|
GM_CHECK_INT_PARAM(src, 1);
|
|
GM_CHECK_INT_PARAM(size, 2);
|
|
|
|
gmUserObject * arrayObject = a_thread->ThisUserObject();
|
|
GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
|
|
if(arrayObject->m_user)
|
|
{
|
|
gmUserArray * array = (gmUserArray *) arrayObject->m_user;
|
|
array->Move(dst, src, size);
|
|
}
|
|
return GM_OK;
|
|
}
|
|
|
|
static void GM_CDECL gmArrayGetInd(gmThread * a_thread, gmVariable * a_operands)
|
|
{
|
|
gmUserObject * arrayObject = (gmUserObject *) GM_OBJECT(a_operands->m_value.m_ref);
|
|
GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
|
|
gmUserArray * array = (gmUserArray *) arrayObject->m_user;
|
|
if(a_operands[1].m_type == GM_INT)
|
|
{
|
|
int index = a_operands[1].m_value.m_int;
|
|
*a_operands = array->GetAt(index);
|
|
return;
|
|
}
|
|
a_operands->Nullify();
|
|
}
|
|
|
|
static void GM_CDECL gmArraySetInd(gmThread * a_thread, gmVariable * a_operands)
|
|
{
|
|
gmUserObject * arrayObject = (gmUserObject *) GM_OBJECT(a_operands->m_value.m_ref);
|
|
GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
|
|
gmUserArray * array = (gmUserArray *) arrayObject->m_user;
|
|
if(a_operands[1].m_type == GM_INT)
|
|
{
|
|
int index = a_operands[1].m_value.m_int;
|
|
|
|
#if GM_USE_INCGC
|
|
//Apply write barrier
|
|
gmVariable oldVar = array->GetAt(index);
|
|
if(oldVar.IsReference())
|
|
{
|
|
a_thread->GetMachine()->GetGC()->WriteBarrier((gmObject*)oldVar.m_value.m_ref);
|
|
}
|
|
#endif //GM_USE_INCGC
|
|
|
|
array->SetAt(index, a_operands[2]);
|
|
}
|
|
}
|
|
|
|
#if GM_USE_INCGC
|
|
static void GM_CDECL gmGCDestructArrayUserType(gmMachine * a_machine, gmUserObject* a_object)
|
|
{
|
|
if(a_object->m_user)
|
|
{
|
|
gmUserArray * array = (gmUserArray *) a_object->m_user;
|
|
array->Destruct(a_machine);
|
|
a_machine->Sys_Free(array);
|
|
}
|
|
a_object->m_user = NULL;
|
|
}
|
|
|
|
static bool GM_CDECL gmGCTraceArrayUserType(gmMachine * a_machine, gmUserObject* a_object, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone)
|
|
{
|
|
if(a_object->m_user)
|
|
{
|
|
gmUserArray * array = (gmUserArray *) a_object->m_user;
|
|
return array->Trace(a_machine, a_gc, a_workLeftToGo, a_workDone);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#else //GM_USE_INCGC
|
|
static void GM_CDECL gmMarkArrayUserType(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
|
|
{
|
|
if(a_object->m_user)
|
|
{
|
|
gmUserArray * array = (gmUserArray *) a_object->m_user;
|
|
array->Mark(a_machine, a_mark);
|
|
}
|
|
}
|
|
|
|
static void GM_CDECL gmGCArrayUserType(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
|
|
{
|
|
if(a_object->m_user)
|
|
{
|
|
gmUserArray * array = (gmUserArray *) a_object->m_user;
|
|
array->Destruct(a_machine);
|
|
a_machine->Sys_Free(array);
|
|
}
|
|
a_object->m_user = NULL;
|
|
}
|
|
#endif //GM_USE_INCGC
|
|
|
|
// libs
|
|
|
|
static gmFunctionEntry s_arrayLib[] =
|
|
{
|
|
/*gm
|
|
\lib gm
|
|
*/
|
|
/*gm
|
|
\function array
|
|
\brief array will create a fixed size array object
|
|
\param int size optional (0)
|
|
\return array
|
|
*/
|
|
{"array", gmfArray},
|
|
};
|
|
|
|
static gmFunctionEntry s_arrayTypeLib[] =
|
|
{
|
|
/*gm
|
|
\lib array
|
|
*/
|
|
/*gm
|
|
\function Size
|
|
\brief Size will return the current size of the fixed array
|
|
\return int array size
|
|
*/
|
|
{"Size", gmfArraySize},
|
|
/*gm
|
|
\function Resize
|
|
\brief Resize will resize the array to a new size
|
|
\param int size optional (0)
|
|
\return null
|
|
*/
|
|
{"Resize", gmfArrayResize},
|
|
/*gm
|
|
\function Shift
|
|
\brief Shift will shift slide the array elements by a delta, nulls are shifted in
|
|
\param int delta
|
|
\return null
|
|
*/
|
|
{"Shift", gmfArrayShift},
|
|
/*gm
|
|
\function Move
|
|
\brief Move will perform a non destructive move on the array
|
|
\param int dst
|
|
\param int src
|
|
\param int size
|
|
\return null
|
|
*/
|
|
{"Move", gmfArrayMove},
|
|
};
|
|
|
|
void gmBindArrayLib(gmMachine * a_machine)
|
|
{
|
|
gmUserArray::m_null.Nullify(); //Init static null
|
|
|
|
a_machine->RegisterLibrary(s_arrayLib, sizeof(s_arrayLib) / sizeof(s_arrayLib[0]));
|
|
GM_ARRAY = a_machine->CreateUserType("array");
|
|
a_machine->RegisterTypeLibrary(GM_ARRAY, s_arrayTypeLib, sizeof(s_arrayTypeLib) / sizeof(s_arrayTypeLib[0]));
|
|
#if GM_USE_INCGC
|
|
a_machine->RegisterUserCallbacks(GM_ARRAY, gmGCTraceArrayUserType, gmGCDestructArrayUserType);
|
|
#else //GM_USE_INCGC
|
|
a_machine->RegisterUserCallbacks(GM_ARRAY, gmMarkArrayUserType, gmGCArrayUserType);
|
|
#endif //GM_USE_INCGC
|
|
a_machine->RegisterTypeOperator(GM_ARRAY, O_GETIND, NULL, gmArrayGetInd);
|
|
a_machine->RegisterTypeOperator(GM_ARRAY, O_SETIND, NULL, gmArraySetInd);
|
|
}
|
|
|
|
#endif // GM_ARRAY_LIB
|