Przeglądaj źródła

Migrated to new scriptarray add-on.

Lasse Öörni 14 lat temu
rodzic
commit
a48c7ccdfb
3 zmienionych plików z 568 dodań i 162 usunięć
  1. 8 1
      Docs/ScriptAPI.dox
  2. 529 142
      Engine/Script/Addons.cpp
  3. 31 19
      Engine/Script/Addons.h

+ 8 - 1
Docs/ScriptAPI.dox

@@ -215,9 +215,16 @@ Methods:<br>
 - void Pop()
 - void Resize(uint)
 - void Clear()
+- void Sort()
+- void Sort(uint, uint)
+- void SortReverse()
+- void SortReverse(uint, uint)
+- void Reverse()
+- int Find(const T&) const
+- int Find(uint, const T&) const
 
 Properties:<br>
-- uint length (readonly)
+- uint length
 - bool empty (readonly)
 
 

+ 529 - 142
Engine/Script/Addons.cpp

@@ -26,6 +26,7 @@
 #include "StringBase.h"
 
 #include <cstring>
+#include <stdio.h>
 
 // Adapted from Angelscript's scriptarray & scriptstdstring add-ons, but with garbage collection disabled
 
@@ -33,17 +34,17 @@
 struct SArrayBuffer
 {
     asDWORD numElements;
-    asBYTE data[1];
+    asBYTE  data[1];
 };
 
-static CScriptArray* ScriptArrayFactory2(asIObjectType* ot, asUINT length)
+static CScriptArray* ScriptArrayFactory2(asIObjectType *ot, asUINT length)
 {
-    CScriptArray* a = new CScriptArray(length, ot);
+    CScriptArray *a = new CScriptArray(length, ot);
     
     // It's possible the constructor raised a script exception, in which case we 
     // need to free the memory and return null instead, else we get a memory leak.
-    asIScriptContext* context = asGetActiveContext();
-    if (context && context->GetState() == asEXECUTION_EXCEPTION)
+    asIScriptContext *ctx = asGetActiveContext();
+    if( ctx && ctx->GetState() == asEXECUTION_EXCEPTION )
     {
         delete a;
         return 0;
@@ -52,14 +53,14 @@ static CScriptArray* ScriptArrayFactory2(asIObjectType* ot, asUINT length)
     return a;
 }
 
-static CScriptArray* ScriptArrayFactoryDefVal(asIObjectType* ot, asUINT length, void* defVal)
+static CScriptArray* ScriptArrayFactoryDefVal(asIObjectType *ot, asUINT length, void *defVal)
 {
-    CScriptArray* a = new CScriptArray(length, defVal, ot);
+    CScriptArray *a = new CScriptArray(length, defVal, ot);
     
     // It's possible the constructor raised a script exception, in which case we 
     // need to free the memory and return null instead, else we get a memory leak.
-    asIScriptContext* context = asGetActiveContext();
-    if (context && context->GetState() == asEXECUTION_EXCEPTION)
+    asIScriptContext *ctx = asGetActiveContext();
+    if( ctx && ctx->GetState() == asEXECUTION_EXCEPTION )
     {
         delete a;
         return 0;
@@ -68,7 +69,7 @@ static CScriptArray* ScriptArrayFactoryDefVal(asIObjectType* ot, asUINT length,
     return a;
 }
 
-static CScriptArray* ScriptArrayFactory(asIObjectType* ot)
+static CScriptArray* ScriptArrayFactory(asIObjectType *ot)
 {
     return ScriptArrayFactory2(ot, 0);
 }
@@ -76,28 +77,29 @@ static CScriptArray* ScriptArrayFactory(asIObjectType* ot)
 // This optional callback is called when the template type is first used by the compiler.
 // It allows the application to validate if the template can be instanciated for the requested 
 // subtype at compile time, instead of at runtime.
-static bool ScriptArrayTemplateCallback(asIObjectType* ot)
+static bool ScriptArrayTemplateCallback(asIObjectType *ot)
 {
     // Make sure the subtype can be instanciated with a default factory/constructor, 
     // otherwise we won't be able to instanciate the elements. Script classes always
     // have default factories, so we don't have to worry about those.
     int typeId = ot->GetSubTypeId();
-    if ((typeId & asTYPEID_MASK_OBJECT) && !(typeId & asTYPEID_OBJHANDLE) && !(typeId & asTYPEID_SCRIPTOBJECT))
+    if( typeId == asTYPEID_VOID )
+        return false;
+    if( (typeId & asTYPEID_MASK_OBJECT) && !(typeId & asTYPEID_OBJHANDLE) && !(typeId & asTYPEID_SCRIPTOBJECT) )
     {
         asIObjectType *subtype = ot->GetEngine()->GetObjectTypeById(typeId);
         asDWORD flags = subtype->GetFlags();
-        if ((flags & asOBJ_VALUE) && !(flags & asOBJ_POD))
+        if( (flags & asOBJ_VALUE) && !(flags & asOBJ_POD) )
         {
             // Verify that there is a default constructor
-            for (unsigned n = 0; n < subtype->GetBehaviourCount(); ++n)
+            for( asUINT n = 0; n < subtype->GetBehaviourCount(); n++ )
             {
                 asEBehaviours beh;
                 int funcId = subtype->GetBehaviourByIndex(n, &beh);
-                if (beh != asBEHAVE_CONSTRUCT)
-                    continue;
+                if( beh != asBEHAVE_CONSTRUCT ) continue;
                 
-                asIScriptFunction* func = ot->GetEngine()->GetFunctionById(funcId);
-                if (func->GetParamCount() == 0)
+                asIScriptFunction *func = ot->GetEngine()->GetFunctionById(funcId);
+                if( func->GetParamCount() == 0 )
                 {
                     // Found the default constructor
                     return true;
@@ -107,14 +109,14 @@ static bool ScriptArrayTemplateCallback(asIObjectType* ot)
             // There is no default constructor
             return false;
         }
-        else if ((flags & asOBJ_REF))
+        else if( (flags & asOBJ_REF) )
         {
             // Verify that there is a default factory
-            for (unsigned n = 0; n < subtype->GetFactoryCount(); ++n)
+            for( asUINT n = 0; n < subtype->GetFactoryCount(); n++ )
             {
                 int funcId = subtype->GetFactoryIdByIndex(n);
-                asIScriptFunction* func = ot->GetEngine()->GetFunctionById(funcId);
-                if (func->GetParamCount() == 0)
+                asIScriptFunction *func = ot->GetEngine()->GetFunctionById(funcId);
+                if( func->GetParamCount() == 0 )
                 {
                     // Found the default factory
                     return true;
@@ -130,12 +132,13 @@ static bool ScriptArrayTemplateCallback(asIObjectType* ot)
     return true;
 }
 
-CScriptArray& CScriptArray::operator = (const CScriptArray &other)
+CScriptArray &CScriptArray::operator=(const CScriptArray &other)
 {
     // Only perform the copy if the array types are the same
-    if (&other != this && other.GetArrayObjectType() == GetArrayObjectType())
+    if( &other != this && 
+        other.GetArrayObjectType() == GetArrayObjectType() )
     {
-        if (buffer)
+        if( buffer )
         {
             DeleteBuffer(buffer);
             buffer = 0;
@@ -149,7 +152,7 @@ CScriptArray& CScriptArray::operator = (const CScriptArray &other)
     return *this;
 }
 
-CScriptArray::CScriptArray(asUINT length, asIObjectType* ot)
+CScriptArray::CScriptArray(asUINT length, asIObjectType *ot)
 {
     refCount = 1;
     gcFlag = false;
@@ -157,18 +160,20 @@ CScriptArray::CScriptArray(asUINT length, asIObjectType* ot)
     objType->AddRef();
     buffer = 0;
     
+    Precache();
+    
     // Determine element size
-    // TODO: Should probably store the template sub type id as well
-    int typeId = objType->GetSubTypeId();
-    if (typeId & asTYPEID_MASK_OBJECT)
+    if( subTypeId & asTYPEID_MASK_OBJECT )
+    {
         elementSize = sizeof(asPWORD);
+    }
     else
-        elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(typeId);
-    
-    isArrayOfHandles = typeId & asTYPEID_OBJHANDLE ? true : false;
+    {
+        elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(subTypeId);
+    }
     
     // Make sure the array size isn't too large for us to handle
-    if (!CheckMaxSize(length))
+    if( !CheckMaxSize(length) )
     {
         // Don't continue with the initialization
         return;
@@ -185,18 +190,20 @@ CScriptArray::CScriptArray(asUINT length, void *defVal, asIObjectType *ot)
     objType->AddRef();
     buffer = 0;
     
+    Precache();
+    
     // Determine element size
-    // TODO: Should probably store the template sub type id as well
-    int typeId = objType->GetSubTypeId();
-    if (typeId & asTYPEID_MASK_OBJECT)
+    if( subTypeId & asTYPEID_MASK_OBJECT )
+    {
         elementSize = sizeof(asPWORD);
+    }
     else
-        elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(typeId);
-    
-    isArrayOfHandles = typeId & asTYPEID_OBJHANDLE ? true : false;
+    {
+        elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(subTypeId);
+    }
     
     // Make sure the array size isn't too large for us to handle
-    if (!CheckMaxSize(length))
+    if( !CheckMaxSize(length) )
     {
         // Don't continue with the initialization
         return;
@@ -205,41 +212,45 @@ CScriptArray::CScriptArray(asUINT length, void *defVal, asIObjectType *ot)
     CreateBuffer(&buffer, length);
     
     // Initialize the elements with the default value
-    for (asUINT n = 0; n < GetSize(); ++n)
+    for( asUINT n = 0; n < GetSize(); n++ )
         SetValue(n, defVal);
 }
 
 // Internal
 void CScriptArray::SetValue(asUINT index, void *value)
 {
-    int typeId = objType->GetSubTypeId();
-    
-    if ((typeId & ~0x03FFFFFF) && !(typeId & asTYPEID_OBJHANDLE))
-        objType->GetEngine()->CopyScriptObject(At(index), value, typeId);
-    else if (typeId & asTYPEID_OBJHANDLE)
+    if( (subTypeId & ~0x03FFFFFF) && !(subTypeId & asTYPEID_OBJHANDLE) )
+        objType->GetEngine()->CopyScriptObject(At(index), value, subTypeId);
+    else if( subTypeId & asTYPEID_OBJHANDLE )
     {
         *(void**)At(index) = *(void**)value;
-        objType->GetEngine()->AddRefScriptObject(*(void**)value, typeId);
+        objType->GetEngine()->AddRefScriptObject(*(void**)value, objType->GetSubType());
     }
-    else if (typeId == asTYPEID_BOOL || typeId == asTYPEID_INT8 || typeId == asTYPEID_UINT8)
+    else if( subTypeId == asTYPEID_BOOL ||
+             subTypeId == asTYPEID_INT8 ||
+             subTypeId == asTYPEID_UINT8 )
         *(char*)At(index) = *(char*)value;
-    else if (typeId == asTYPEID_INT16 || typeId == asTYPEID_UINT16)
+    else if( subTypeId == asTYPEID_INT16 ||
+             subTypeId == asTYPEID_UINT16 )
         *(short*)At(index) = *(short*)value;
-    else if (typeId == asTYPEID_INT32 || typeId == asTYPEID_UINT32 || typeId == asTYPEID_FLOAT)
+    else if( subTypeId == asTYPEID_INT32 ||
+             subTypeId == asTYPEID_UINT32 ||
+             subTypeId == asTYPEID_FLOAT )
         *(int*)At(index) = *(int*)value;
-    else if (typeId == asTYPEID_INT64 || typeId == asTYPEID_UINT64 || typeId == asTYPEID_DOUBLE)
+    else if( subTypeId == asTYPEID_INT64 ||
+             subTypeId == asTYPEID_UINT64 ||
+             subTypeId == asTYPEID_DOUBLE )
         *(double*)At(index) = *(double*)value;
 }
 
 CScriptArray::~CScriptArray()
 {
-    if (buffer)
+    if( buffer )
     {
         DeleteBuffer(buffer);
         buffer = 0;
     }
-    if (objType)
-        objType->Release();
+    if( objType ) objType->Release();
 }
 
 asUINT CScriptArray::GetSize() const
@@ -247,68 +258,55 @@ asUINT CScriptArray::GetSize() const
     return buffer->numElements;
 }
 
-bool CScriptArray::IsEmpty() const
-{
-    return buffer->numElements == 0;
-}
-
 void CScriptArray::Resize(asUINT numElements)
 {
-    if (numElements & 0x80000000)
+    if( numElements & 0x80000000 )
     {
         CheckMaxSize(numElements);
         return;
     }
     
-    Resize((int)numElements - (int)buffer->numElements, -1);
-}
-
-void CScriptArray::Clear()
-{
-    Resize(0);
+    Resize((int)numElements - (int)buffer->numElements, (asUINT)-1);
 }
 
 // Internal
 void CScriptArray::Resize(int delta, asUINT at)
 {
-    if (delta < 0)
+    if( delta < 0 )
     {
-        if (-delta > (int)buffer->numElements)
+        if( -delta > (int)buffer->numElements )
             delta = -(int)buffer->numElements;
-        if (at > buffer->numElements + delta)
+        if( at > buffer->numElements + delta )
             at = buffer->numElements + delta;
     }
-    else if (delta > 0)
+    else if( delta > 0 )
     {
         // Make sure the array size isn't too large for us to handle
-        if (delta > 0 && !CheckMaxSize(buffer->numElements + delta))
+        if( delta > 0 && !CheckMaxSize(buffer->numElements + delta) )
             return;
         
-        if (at > buffer->numElements)
+        if( at > buffer->numElements )
             at = buffer->numElements;
     }
     
-    if (delta == 0)
-        return;
+    if( delta == 0 ) return;
     
     // Allocate memory for the buffer
     SArrayBuffer *newBuffer;
     newBuffer = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1 + elementSize*(buffer->numElements + delta)];
     newBuffer->numElements = buffer->numElements + delta;
     
-    int c = newBuffer->numElements > buffer->numElements ? buffer->numElements : newBuffer->numElements;
     memcpy(newBuffer->data, buffer->data, at*elementSize);
-    if (delta > 0 && at < buffer->numElements)
+    if( delta > 0 && at < buffer->numElements )
         memcpy(newBuffer->data + (at+delta)*elementSize, buffer->data + at*elementSize, (buffer->numElements-at)*elementSize);
-    else if (delta < 0 && at < buffer->numElements)
+    else if( delta < 0 && at < buffer->numElements )
         memcpy(newBuffer->data + at*elementSize, buffer->data + (at-delta)*elementSize, (buffer->numElements-at+delta)*elementSize);
     
-    int typeId = objType->GetSubTypeId();
-    if (typeId & asTYPEID_MASK_OBJECT)
+    if( subTypeId & asTYPEID_MASK_OBJECT )
     {
-        if (delta > 0)
+        if( delta > 0 )
             Construct(newBuffer, at, at+delta);
-        else if (delta < 0)
+        else if( delta < 0 )
             Destruct(buffer, at, at-delta);
     }
     
@@ -325,16 +323,20 @@ bool CScriptArray::CheckMaxSize(asUINT numElements)
     // for the array doesn't overflow and becomes smaller than requested
     
     asUINT maxSize = 0xFFFFFFFFul - sizeof(SArrayBuffer) + 1;
-    if (objType->GetSubTypeId() & asTYPEID_MASK_OBJECT)
+    if( subTypeId & asTYPEID_MASK_OBJECT )
         maxSize /= sizeof(void*);
-    else
+    else if( elementSize > 0 )
         maxSize /= elementSize;
     
-    if (numElements > maxSize)
+    if( numElements > maxSize )
     {
-        asIScriptContext* context = asGetActiveContext();
-        if (context)
-            context->SetException("Too large array size");
+        asIScriptContext *ctx = asGetActiveContext();
+        if( ctx )
+        {
+            // Set a script exception
+            ctx->SetException("Too large array size");
+        }
+        
         return false;
     }
     
@@ -354,16 +356,17 @@ int CScriptArray::GetArrayTypeId() const
 
 int CScriptArray::GetElementTypeId() const
 {
-    return objType->GetSubTypeId();
+    return subTypeId;
 }
 
 void CScriptArray::InsertAt(asUINT index, void *value)
 {
-    if (index > buffer->numElements)
+    if( index > buffer->numElements )
     {
-        asIScriptContext* context = asGetActiveContext();
-        if (context)
-            context->SetException("Index out of bounds");
+        // If this is called from a script we raise a script exception
+        asIScriptContext *ctx = asGetActiveContext();
+        if( ctx )
+            ctx->SetException("Index out of bounds");
         return;
     }
     
@@ -381,11 +384,12 @@ void CScriptArray::InsertLast(void *value)
 
 void CScriptArray::RemoveAt(asUINT index)
 {
-    if (index >= buffer->numElements)
+    if( index >= buffer->numElements )
     {
-        asIScriptContext* context = asGetActiveContext();
-        if (context)
-            context->SetException("Index out of bounds");
+        // If this is called from a script we raise a script exception
+        asIScriptContext *ctx = asGetActiveContext();
+        if( ctx )
+            ctx->SetException("Index out of bounds");
         return;
     }
     
@@ -401,17 +405,17 @@ void CScriptArray::RemoveLast()
 // Return a pointer to the array element. Returns 0 if the index is out of bounds
 void *CScriptArray::At(asUINT index)
 {
-    if (index >= buffer->numElements)
+    if( index >= buffer->numElements )
     {
-        asIScriptContext* context = asGetActiveContext();
-        if (context)
-            context->SetException("Index out of bounds");
+        // If this is called from a script we raise a script exception
+        asIScriptContext *ctx = asGetActiveContext();
+        if( ctx )
+            ctx->SetException("Index out of bounds");
         return 0;
     }
     else
     {
-        int typeId = objType->GetSubTypeId();
-        if ((typeId & asTYPEID_MASK_OBJECT) && !isArrayOfHandles)
+        if( (subTypeId & asTYPEID_MASK_OBJECT) && !(subTypeId & asTYPEID_OBJHANDLE) )
             return (void*)((size_t*)buffer->data)[index];
         else
             return buffer->data + elementSize*index;
@@ -421,8 +425,7 @@ void *CScriptArray::At(asUINT index)
 // internal
 void CScriptArray::CreateBuffer(SArrayBuffer **buf, asUINT numElements)
 {
-    int typeId = objType->GetSubTypeId();
-    if (typeId & asTYPEID_MASK_OBJECT)
+    if( subTypeId & asTYPEID_MASK_OBJECT )
     {
         *buf = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1+sizeof(void*)*numElements];
         (*buf)->numElements = numElements;
@@ -448,84 +451,396 @@ void CScriptArray::DeleteBuffer(SArrayBuffer *buf)
 // internal
 void CScriptArray::Construct(SArrayBuffer *buf, asUINT start, asUINT end)
 {
-    int typeId = objType->GetSubTypeId();
-    if (isArrayOfHandles)
+    if( subTypeId & asTYPEID_OBJHANDLE )
     {
         // Set all object handles to null
-        void* d = (void*)(buf->data + start * sizeof(void*));
+        void *d = (void*)(buf->data + start * sizeof(void*));
         memset(d, 0, (end-start)*sizeof(void*));
     }
-    else if (typeId & asTYPEID_MASK_OBJECT)
+    else if( subTypeId & asTYPEID_MASK_OBJECT )
     {
-        void** max = (void**)(buf->data + end * sizeof(void*));
-        void** d = (void**)(buf->data + start * sizeof(void*));
+        void **max = (void**)(buf->data + end * sizeof(void*));
+        void **d = (void**)(buf->data + start * sizeof(void*));
         
-        asIScriptEngine* engine = objType->GetEngine();
+        asIScriptEngine *engine = objType->GetEngine();
         
-        for (; d < max; d++)
-            *d = (void*)engine->CreateScriptObject(typeId);
+        for( ; d < max; d++ )
+            *d = (void*)engine->CreateScriptObject(subTypeId);
     }
 }
 
 // internal
 void CScriptArray::Destruct(SArrayBuffer *buf, asUINT start, asUINT end)
 {
-    int typeId = objType->GetSubTypeId();
-    if (typeId & asTYPEID_MASK_OBJECT)
+    if( subTypeId & asTYPEID_MASK_OBJECT )
     {
-        asIScriptEngine* engine = objType->GetEngine();
+        asIScriptEngine *engine = objType->GetEngine();
         
-        void** max = (void**)(buf->data + end * sizeof(void*));
-        void** d = (void**)(buf->data + start * sizeof(void*));
+        void **max = (void**)(buf->data + end * sizeof(void*));
+        void **d   = (void**)(buf->data + start * sizeof(void*));
         
-        for(; d < max; d++ )
+        for( ; d < max; d++ )
         {
             if( *d )
-                engine->ReleaseScriptObject(*d, typeId);
+                engine->ReleaseScriptObject(*d, objType->GetSubType());
+        }
+    }
+}
+
+// internal
+bool CScriptArray::Less(const void *a, const void *b, bool asc, asIScriptContext *ctx)
+{
+    if( !asc )
+    {
+        // Swap items
+        const void *TEMP = a;
+        a = b;
+        b = TEMP;
+    }
+    
+    if( subTypeId <= asTYPEID_DOUBLE )
+    {
+        // Simple compare of values
+        switch( subTypeId )
+        {
+            #define COMPARE(T) *((T*)a) < *((T*)b)
+            case asTYPEID_BOOL: return COMPARE(bool);
+            case asTYPEID_INT8: return COMPARE(signed char);
+            case asTYPEID_UINT8: return COMPARE(unsigned char);
+            case asTYPEID_INT16: return COMPARE(signed short);
+            case asTYPEID_UINT16: return COMPARE(unsigned short);
+            case asTYPEID_INT32: return COMPARE(signed int);
+            case asTYPEID_UINT32: return COMPARE(unsigned int);
+            case asTYPEID_FLOAT: return COMPARE(float);
+            case asTYPEID_DOUBLE: return COMPARE(double);
+            #undef COMPARE
+        }
+    }
+    else
+    {
+        int r = 0;
+        
+        // Execute object opCmp
+        // TODO: Add proper error handling
+        r = ctx->Prepare(cmpFuncId); assert(r >= 0);
+        r = ctx->SetObject((void*)a); assert(r >= 0);
+        r = ctx->SetArgAddress(0, (void*)b); assert(r >= 0);
+        r = ctx->Execute();
+        
+        if( r == asEXECUTION_FINISHED )
+        {
+            return (int)ctx->GetReturnDWord() < 0;
+        }
+    }
+    
+    return false;
+}
+
+void CScriptArray::Reverse()
+{
+    asUINT size = GetSize();
+    
+    if( size >= 2 )
+    {
+        asBYTE TEMP[16];
+        
+        for( asUINT i = 0; i < size / 2; i++ )
+        {
+            Copy(TEMP, GetArrayItemPointer(i));
+            Copy(GetArrayItemPointer(i), GetArrayItemPointer(size - i - 1));
+            Copy(GetArrayItemPointer(size - i - 1), TEMP);
+        }
+    }
+}
+
+// internal
+bool CScriptArray::Equals(const void *a, const void *b, asIScriptContext *ctx)
+{
+    if( subTypeId <= asTYPEID_DOUBLE )
+    {
+        // Simple compare of values
+        switch( subTypeId )
+        {
+            #define COMPARE(T) *((T*)a) == *((T*)b)
+            case asTYPEID_BOOL: return COMPARE(bool);
+            case asTYPEID_INT8: return COMPARE(signed char);
+            case asTYPEID_UINT8: return COMPARE(unsigned char);
+            case asTYPEID_INT16: return COMPARE(signed short);
+            case asTYPEID_UINT16: return COMPARE(unsigned short);
+            case asTYPEID_INT32: return COMPARE(signed int);
+            case asTYPEID_UINT32: return COMPARE(unsigned int);
+            case asTYPEID_FLOAT: return COMPARE(float);
+            case asTYPEID_DOUBLE: return COMPARE(double);
+            #undef COMPARE
+        }
+    }
+    else
+    {
+        int r = 0;
+        
+        // Execute object opEquals if available
+        if( eqFuncId >= 0 )
+        {
+            // TODO: Add proper error handling
+            r = ctx->Prepare(eqFuncId); assert(r >= 0);
+            r = ctx->SetObject((void*)a); assert(r >= 0);
+            r = ctx->SetArgAddress(0, (void*)b); assert(r >= 0);
+            r = ctx->Execute();
+            
+            if( r == asEXECUTION_FINISHED )
+            {
+                return ctx->GetReturnByte() != 0;
+            }
+        }
+        
+        // Execute object opCmp if available
+        if( cmpFuncId >= 0 )
+        {
+            // TODO: Add proper error handling
+            r = ctx->Prepare(cmpFuncId); assert(r >= 0);
+            r = ctx->SetObject((void*)a); assert(r >= 0);
+            r = ctx->SetArgAddress(0, (void*)b); assert(r >= 0);
+            r = ctx->Execute();
+            
+            if( r == asEXECUTION_FINISHED )
+            {
+                return (int)ctx->GetReturnDWord() == 0;
+            }
+        }
+    }
+    
+    return false;
+}
+
+int CScriptArray::Find(void *value)
+{
+    return Find(0, value);
+}
+
+int CScriptArray::Find(asUINT index, void *value)
+{
+    // Subtype isn't primitive and doesn't have opEquals / opCmp
+    if( subTypeId > asTYPEID_DOUBLE && (cmpFuncId <= 0 && eqFuncId <= 0) )
+    {
+        asIScriptContext *ctx = asGetActiveContext();
+        asIObjectType* subType = objType->GetEngine()->GetObjectTypeById(subTypeId);
+        
+        // Throw an exception
+        if( ctx )
+        {
+            char tmp[512];
+#if defined(_MSC_VER) && _MSC_VER >= 1500
+            sprintf_s(tmp, 512, "Type '%s' does not have opEquals / opCmp", subType->GetName());
+#else
+            sprintf(tmp, "Type '%s' does not have opEquals / opCmp", subType->GetName());
+#endif
+            ctx->SetException(tmp);
+        }
+        
+        return -1;
+    }
+    
+    asIScriptContext *cmpContext = 0;
+    
+    if( subTypeId > asTYPEID_DOUBLE )
+    {
+        // TODO: Ideally this context would be retrieved from a pool, so we don't have to 
+        //       create a new one everytime. We could keep a context with the array object 
+        //       but that would consume a lot of resources as each context is quite heavy.
+        cmpContext = objType->GetEngine()->CreateContext();
+    }
+    
+    int ret = -1;
+    asUINT size = GetSize();
+    
+    if( index < size )
+    {
+        for( asUINT i = index; i < size; i++ )
+        {
+            // value passed by reference
+            if( Equals(At(i), (value), cmpContext) )
+            {
+                ret = (int)i;
+                break;
+            }
         }
     }
+    
+    if( cmpContext )
+        cmpContext->Release();
+    
+    return ret;
+}
+
+// internal
+// Copy object handle or primitive value
+void CScriptArray::Copy(void *dst, void *src)
+{
+    memcpy(dst, src, elementSize);
+}
+
+// internal
+// Return pointer to array item (object handle or primitive value)
+void *CScriptArray::GetArrayItemPointer(int index)
+{
+    return buffer->data + index * elementSize;
+}
+
+// internal
+// Return pointer to data in buffer (object or primitive)
+void *CScriptArray::GetDataPointer(void *buffer)
+{
+    if ((subTypeId & asTYPEID_MASK_OBJECT) && !(subTypeId & asTYPEID_OBJHANDLE) )
+    {
+        // Real address of object
+        return reinterpret_cast<void*>(*(size_t*)buffer);
+    }
+    else
+    {
+        // Primitive is just a raw data
+        return buffer;
+    }
+}
+
+// Sort ascending
+void CScriptArray::SortAsc()
+{
+    Sort(0, GetSize(), true);
+}
+
+// Sort ascending
+void CScriptArray::SortAsc(asUINT index, asUINT count)
+{
+    Sort(index, count, true);
+}
+
+// Sort descending
+void CScriptArray::SortDesc()
+{
+    Sort(0, GetSize(), false);
+}
+
+// Sort descending
+void CScriptArray::SortDesc(asUINT index, asUINT count)
+{
+    Sort(index, count, false);
+}
+
+// internal
+void CScriptArray::Sort(asUINT index, asUINT count, bool asc)
+{
+    // Subtype isn't primitive and doesn't have opCmp
+    if( subTypeId > asTYPEID_DOUBLE && cmpFuncId <= 0 )
+    {
+        asIScriptContext *ctx = asGetActiveContext();
+        asIObjectType* subType = objType->GetEngine()->GetObjectTypeById(subTypeId);
+        
+        // Throw an exception
+        if( ctx )
+        {
+            char tmp[512];
+#if defined(_MSC_VER) && _MSC_VER >= 1500
+            sprintf_s(tmp, 512, "Type '%s' does not have opCmp", subType->GetName());
+#else
+            sprintf(tmp, "Type '%s' does not have opCmp", subType->GetName());
+#endif
+            ctx->SetException(tmp);
+        }
+        
+        return;
+    }
+    
+    // No need to sort
+    if( count < 2 )
+    {
+        return;
+    }
+    
+    int start = index;
+    int end = index + count;
+    
+    // Check if we could access invalid item while sorting
+    if( start >= (int)buffer->numElements || end > (int)buffer->numElements )
+    {
+        asIScriptContext *ctx = asGetActiveContext();
+        
+        // Throw an exception
+        if( ctx )
+        {
+            ctx->SetException("Index out of bounds");
+        }
+        
+        return;
+    }
+    
+    asBYTE tmp[16];
+    asIScriptContext *cmpContext = 0;
+    
+    if( subTypeId > asTYPEID_DOUBLE )
+    {
+        // TODO: Ideally this context would be retrieved from a pool, so we don't have to 
+        //       create a new one everytime. We could keep a context with the array object 
+        //       but that would consume a lot of resources as each context is quite heavy.
+        cmpContext = objType->GetEngine()->CreateContext();
+    }
+    
+    // Insertion sort
+    for( int i = start + 1; i < end; i++ )
+    {
+        Copy(tmp, GetArrayItemPointer(i));
+        
+        int j = i - 1;
+        
+        while( j >= start && Less(GetDataPointer(tmp), At(j), asc, cmpContext) )
+        {
+            Copy(GetArrayItemPointer(j + 1), GetArrayItemPointer(j));
+            j--;
+        }
+        
+        Copy(GetArrayItemPointer(j + 1), tmp);
+    }
+    
+    if( cmpContext )
+        cmpContext->Release();
 }
 
 // internal
 void CScriptArray::CopyBuffer(SArrayBuffer *dst, SArrayBuffer *src)
 {
-    asIScriptEngine* engine = objType->GetEngine();
-    if(isArrayOfHandles)
+    asIScriptEngine *engine = objType->GetEngine();
+    if( subTypeId & asTYPEID_OBJHANDLE )
     {
         // Copy the references and increase the reference counters
-        if (dst->numElements > 0 && src->numElements > 0)
+        if( dst->numElements > 0 && src->numElements > 0 )
         {
-            int typeId = objType->GetSubTypeId();
             int count = dst->numElements > src->numElements ? src->numElements : dst->numElements;
             
-            void** max = (void**)(dst->data + count * sizeof(void*));
-            void** d = (void**)dst->data;
-            void** s = (void**)src->data;
+            void **max = (void**)(dst->data + count * sizeof(void*));
+            void **d   = (void**)dst->data;
+            void **s   = (void**)src->data;
             
-            for (; d < max; d++, s++)
+            for( ; d < max; d++, s++ )
             {
                 *d = *s;
-                if (*d)
-                    engine->AddRefScriptObject(*d, typeId);
+                if( *d )
+                    engine->AddRefScriptObject(*d, objType->GetSubType());
             }
         }
     }
     else
     {
-        int typeId = objType->GetSubTypeId();
-        
-        if (dst->numElements > 0 && src->numElements > 0)
+        if( dst->numElements > 0 && src->numElements > 0 )
         {
             int count = dst->numElements > src->numElements ? src->numElements : dst->numElements;
-            if (typeId & asTYPEID_MASK_OBJECT)
+            if( subTypeId & asTYPEID_MASK_OBJECT )
             {
                 // Call the assignment operator on all of the objects
-                void** max = (void**)(dst->data + count * sizeof(void*));
-                void** d = (void**)dst->data;
-                void** s = (void**)src->data;
+                void **max = (void**)(dst->data + count * sizeof(void*));
+                void **d   = (void**)dst->data;
+                void **s   = (void**)src->data;
                 
-                for(; d < max; d++, s++)
-                    engine->CopyScriptObject(*d, *s, typeId);
+                for( ; d < max; d++, s++ )
+                    engine->CopyScriptObject(*d, *s, subTypeId);
             }
             else
             {
@@ -536,6 +851,58 @@ void CScriptArray::CopyBuffer(SArrayBuffer *dst, SArrayBuffer *src)
     }
 }
 
+// internal
+// Precache some info
+void CScriptArray::Precache()
+{
+    // TODO: optimize: This information could be stored in the object type as user data,
+    //                 then it wouldn't be necessary to look for this for each array initialization
+    
+    subTypeId = objType->GetSubTypeId();
+    
+    cmpFuncId = -1;
+    eqFuncId = -1;
+    
+    // Object - search for opCmp / opEquals
+    if( subTypeId > asTYPEID_DOUBLE )
+    {
+        asIObjectType *subType = objType->GetEngine()->GetObjectTypeById(subTypeId);
+        
+        if( subType )
+        {
+            for( asUINT i = 0; i < subType->GetMethodCount(); i++ )
+            {
+                asIScriptFunction *func = subType->GetMethodByIndex(i);
+                
+                if( func->GetParamCount() == 1 /* && func->IsReadOnly() */ )
+                {
+                    asDWORD flags = 0;
+                    int returnTypeId = func->GetReturnTypeId();
+                    int paramTypeId = func->GetParamTypeId(0, &flags);
+                    
+                    if( flags == asTM_INREF && paramTypeId == subTypeId )
+                    {
+                        if( returnTypeId == asTYPEID_INT32 && strcmp(func->GetName(), "opCmp") == 0 )
+                        {
+                            cmpFuncId = subType->GetMethodIdByIndex(i);
+                        }
+                        
+                        if( returnTypeId == asTYPEID_BOOL && strcmp(func->GetName(), "opEquals") == 0 )
+                        {
+                            eqFuncId = subType->GetMethodIdByIndex(i);
+                        }
+                        
+                        if( cmpFuncId >= 0 && eqFuncId >= 0 )
+                        {
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
 void CScriptArray::AddRef() const
 {
     // Clear the GC flag then increase the counter
@@ -547,8 +914,20 @@ void CScriptArray::Release() const
 {
     // Now do the actual releasing (clearing the flag set by GC)
     gcFlag = false;
-    if (--refCount == 0)
+    if( --refCount == 0 )
+    {
         delete this;
+    }
+}
+
+static void ScriptArrayClear(CScriptArray* ptr)
+{
+    ptr->Resize(0);
+}
+
+static bool ScriptArrayIsEmpty(CScriptArray* ptr)
+{
+    return ptr->GetSize() == 0;
 }
 
 void RegisterArray(asIScriptEngine* engine)
@@ -569,9 +948,17 @@ void RegisterArray(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Array<T>", "void Push(const T& in)", asMETHOD(CScriptArray, InsertLast), asCALL_THISCALL);
     engine->RegisterObjectMethod("Array<T>", "void Pop()", asMETHOD(CScriptArray, RemoveLast), asCALL_THISCALL);
     engine->RegisterObjectMethod("Array<T>", "void Resize(uint)", asMETHODPR(CScriptArray, Resize, (asUINT), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Array<T>", "void Clear()", asMETHOD(CScriptArray, Clear), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Array<T>", "void Clear()", asFUNCTION(ScriptArrayClear), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("Array<T>", "void Sort()", asMETHODPR(CScriptArray, SortAsc, (), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Array<T>", "void Sort(uint, uint)", asMETHODPR(CScriptArray, SortAsc, (asUINT, asUINT), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Array<T>", "void SortReverse()", asMETHODPR(CScriptArray, SortDesc, (), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Array<T>", "void SortReverse(uint, uint)", asMETHODPR(CScriptArray, SortDesc, (asUINT, asUINT), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Array<T>", "void Reverse()", asMETHOD(CScriptArray, Reverse), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Array<T>", "int Find(const T&in) const", asMETHODPR(CScriptArray, Find, (void*), int), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Array<T>", "int Find(uint, const T&in) const", asMETHODPR(CScriptArray, Find, (asUINT, void*), int), asCALL_THISCALL);
     engine->RegisterObjectMethod("Array<T>", "uint get_length() const", asMETHOD(CScriptArray, GetSize), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Array<T>", "bool get_empty() const", asMETHOD(CScriptArray, IsEmpty), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Array<T>", "void set_length(uint)", asMETHODPR(CScriptArray, Resize, (asUINT), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Array<T>", "bool get_empty() const", asFUNCTION(ScriptArrayIsEmpty), asCALL_CDECL_OBJLAST);
     engine->RegisterDefaultArrayType("Array<T>");
 }
 

+ 31 - 19
Engine/Script/Addons.h

@@ -30,56 +30,68 @@
 struct SArrayBuffer;
 
 /// %Script array type.
+struct SArrayBuffer;
+
 class CScriptArray
 {
 public:
-    CScriptArray(asUINT length, asIObjectType* ot);
-    CScriptArray(asUINT length, void* defVal, asIObjectType* ot);
+    CScriptArray(asUINT length, asIObjectType *ot);
+    CScriptArray(asUINT length, void *defVal, asIObjectType *ot);
     virtual ~CScriptArray();
     
     void AddRef() const;
     void Release() const;
     
     // Type information
-    asIObjectType* GetArrayObjectType() const;
+    asIObjectType *GetArrayObjectType() const;
     int GetArrayTypeId() const;
     int GetElementTypeId() const;
     
     void Resize(asUINT numElements);
-    void Clear();
     asUINT GetSize() const;
-    bool IsEmpty() const;
     
     // Get a pointer to an element. Returns 0 if out of bounds
-    void* At(asUINT index);
+    void  *At(asUINT index);
     
-    CScriptArray& operator = (const CScriptArray&);
+    CScriptArray &operator=(const CScriptArray&);
     
-    // TODO: Add methods Sort, Reverse, Find, etc
     void InsertAt(asUINT index, void *value);
     void RemoveAt(asUINT index);
     void InsertLast(void *value);
     void RemoveLast();
+    void SortAsc();
+    void SortDesc();
+    void SortAsc(asUINT index, asUINT count);
+    void SortDesc(asUINT index, asUINT count);
+    void Sort(asUINT index, asUINT count, bool asc);
+    void Reverse();
+    int Find(void *value);
+    int Find(asUINT index, void *value);
     
 protected:
     mutable int refCount;
     mutable bool gcFlag;
-    asIObjectType* objType;
-    SArrayBuffer* buffer;
-    bool isArrayOfHandles;
+    asIObjectType *objType;
+    SArrayBuffer *buffer;
     int elementSize;
+    int cmpFuncId;
+    int eqFuncId;
+    int subTypeId;
     
+    bool Less(const void *a, const void *b, bool asc, asIScriptContext *ctx);
+    void *GetArrayItemPointer(int index);
+    void *GetDataPointer(void *buffer);
+    void Copy(void *dst, void *src);
+    void Precache();
     bool CheckMaxSize(asUINT numElements);
-    
     void Resize(int delta, asUINT at);
     void SetValue(asUINT index, void *value);
-    
-    void CreateBuffer(SArrayBuffer** buf, asUINT numElements);
-    void DeleteBuffer(SArrayBuffer* buf);
-    void CopyBuffer(SArrayBuffer* dst, SArrayBuffer* src);
-    
-    void Construct(SArrayBuffer* buf, asUINT start, asUINT end);
-    void Destruct(SArrayBuffer* buf, asUINT start, asUINT end);
+    void CreateBuffer(SArrayBuffer **buf, asUINT numElements);
+    void DeleteBuffer(SArrayBuffer *buf);
+    void CopyBuffer(SArrayBuffer *dst, SArrayBuffer *src);
+    void Construct(SArrayBuffer *buf, asUINT start, asUINT end);
+    void Destruct(SArrayBuffer *buf, asUINT start, asUINT end);
+    bool Equals(const void *a, const void *b, asIScriptContext *ctx);
 };
 
 /// Register the array type to script.