Addons.cpp 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652
  1. //
  2. // Copyright (c) 2008-2013 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "Precompiled.h"
  23. #include "Addons.h"
  24. #include <cstring>
  25. #include <new>
  26. #include <stdio.h>
  27. // Adapted from Angelscript's scriptarray & scriptstdstring add-ons, but with garbage collection disabled
  28. namespace Urho3D
  29. {
  30. using namespace std;
  31. /// %Script array buffer.
  32. struct SArrayBuffer
  33. {
  34. asDWORD maxElements;
  35. asDWORD numElements;
  36. asBYTE data[1];
  37. };
  38. /// %Script array cache.
  39. struct SArrayCache
  40. {
  41. asIScriptFunction *cmpFunc;
  42. asIScriptFunction *eqFunc;
  43. int cmpFuncReturnCode; // To allow better error message in case of multiple matches
  44. int eqFuncReturnCode;
  45. };
  46. // We just define a number here that we assume nobody else is using for
  47. // object type user data. The add-ons have reserved the numbers 1000
  48. // through 1999 for this purpose, so we should be fine.
  49. const asPWORD ARRAY_CACHE = 1000;
  50. static void CleanupObjectTypeArrayCache(asIObjectType *type)
  51. {
  52. SArrayCache *cache = reinterpret_cast<SArrayCache*>(type->GetUserData(ARRAY_CACHE));
  53. if( cache )
  54. delete cache;
  55. }
  56. static CScriptArray* ScriptArrayFactory2(asIObjectType *ot, asUINT length)
  57. {
  58. CScriptArray *a = new CScriptArray(length, ot);
  59. // It's possible the constructor raised a script exception, in which case we
  60. // need to free the memory and return null instead, else we get a memory leak.
  61. asIScriptContext *ctx = asGetActiveContext();
  62. if( ctx && ctx->GetState() == asEXECUTION_EXCEPTION )
  63. {
  64. a->Release();
  65. return 0;
  66. }
  67. return a;
  68. }
  69. static CScriptArray* ScriptArrayFactoryDefVal(asIObjectType *ot, asUINT length, void *defVal)
  70. {
  71. CScriptArray *a = new CScriptArray(length, defVal, ot);
  72. // It's possible the constructor raised a script exception, in which case we
  73. // need to free the memory and return null instead, else we get a memory leak.
  74. asIScriptContext *ctx = asGetActiveContext();
  75. if( ctx && ctx->GetState() == asEXECUTION_EXCEPTION )
  76. {
  77. a->Release();
  78. return 0;
  79. }
  80. return a;
  81. }
  82. static CScriptArray* ScriptArrayFactory(asIObjectType *ot)
  83. {
  84. return ScriptArrayFactory2(ot, 0);
  85. }
  86. // This optional callback is called when the template type is first used by the compiler.
  87. // It allows the application to validate if the template can be instanciated for the requested
  88. // subtype at compile time, instead of at runtime. The output argument dontGarbageCollect
  89. // allow the callback to tell the engine if the template instance type shouldn't be garbage collected,
  90. // i.e. no asOBJ_GC flag.
  91. static bool ScriptArrayTemplateCallback(asIObjectType *ot, bool &dontGarbageCollect)
  92. {
  93. // Urho3D: nothing is garbage collected
  94. dontGarbageCollect = true;
  95. // Make sure the subtype can be instanciated with a default factory/constructor,
  96. // otherwise we won't be able to instanciate the elements.
  97. int typeId = ot->GetSubTypeId();
  98. if( typeId == asTYPEID_VOID )
  99. return false;
  100. if( (typeId & asTYPEID_MASK_OBJECT) && !(typeId & asTYPEID_OBJHANDLE) )
  101. {
  102. asIObjectType *subtype = ot->GetEngine()->GetObjectTypeById(typeId);
  103. asDWORD flags = subtype->GetFlags();
  104. if( (flags & asOBJ_VALUE) && !(flags & asOBJ_POD) )
  105. {
  106. // Verify that there is a default constructor
  107. bool found = false;
  108. for( asUINT n = 0; n < subtype->GetBehaviourCount(); n++ )
  109. {
  110. asEBehaviours beh;
  111. asIScriptFunction *func = subtype->GetBehaviourByIndex(n, &beh);
  112. if( beh != asBEHAVE_CONSTRUCT ) continue;
  113. if( func->GetParamCount() == 0 )
  114. {
  115. // Found the default constructor
  116. found = true;
  117. break;
  118. }
  119. }
  120. if( !found )
  121. {
  122. // There is no default constructor
  123. return false;
  124. }
  125. }
  126. else if( (flags & asOBJ_REF) )
  127. {
  128. bool found = false;
  129. // If value assignment for ref type has been disabled then the array
  130. // can be created if the type has a default factory function
  131. if( !ot->GetEngine()->GetEngineProperty(asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE) )
  132. {
  133. // Verify that there is a default factory
  134. for( asUINT n = 0; n < subtype->GetFactoryCount(); n++ )
  135. {
  136. asIScriptFunction *func = subtype->GetFactoryByIndex(n);
  137. if( func->GetParamCount() == 0 )
  138. {
  139. // Found the default factory
  140. found = true;
  141. break;
  142. }
  143. }
  144. }
  145. if( !found )
  146. {
  147. // No default factory
  148. return false;
  149. }
  150. }
  151. // If the object type is not garbage collected then the array also doesn't need to be
  152. if( !(flags & asOBJ_GC) )
  153. dontGarbageCollect = true;
  154. }
  155. else if( !(typeId & asTYPEID_OBJHANDLE) )
  156. {
  157. // Arrays with primitives cannot form circular references,
  158. // thus there is no need to garbage collect them
  159. dontGarbageCollect = true;
  160. }
  161. // The type is ok
  162. return true;
  163. }
  164. CScriptArray &CScriptArray::operator=(const CScriptArray &other)
  165. {
  166. // Only perform the copy if the array types are the same
  167. if( &other != this &&
  168. other.GetArrayObjectType() == GetArrayObjectType() )
  169. {
  170. // Make sure the arrays are of the same size
  171. Resize(other.buffer->numElements);
  172. // Copy the value of each element
  173. CopyBuffer(buffer, other.buffer);
  174. }
  175. return *this;
  176. }
  177. CScriptArray::CScriptArray(asUINT length, asIObjectType *ot)
  178. {
  179. refCount = 1;
  180. gcFlag = false;
  181. objType = ot;
  182. objType->AddRef();
  183. buffer = 0;
  184. Precache();
  185. // Determine element size
  186. if( subTypeId & asTYPEID_MASK_OBJECT )
  187. elementSize = sizeof(asPWORD);
  188. else
  189. elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(subTypeId);
  190. // Make sure the array size isn't too large for us to handle
  191. if( !CheckMaxSize(length) )
  192. {
  193. // Don't continue with the initialization
  194. return;
  195. }
  196. CreateBuffer(&buffer, length);
  197. // Urho3D: garbage collection disabled
  198. /*
  199. // Notify the GC of the successful creation
  200. if( objType->GetFlags() & asOBJ_GC )
  201. objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
  202. */
  203. }
  204. CScriptArray::CScriptArray(const CScriptArray &other)
  205. {
  206. refCount = 1;
  207. gcFlag = false;
  208. objType = other.objType;
  209. objType->AddRef();
  210. buffer = 0;
  211. Precache();
  212. elementSize = other.elementSize;
  213. if( objType->GetFlags() & asOBJ_GC )
  214. objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
  215. CreateBuffer(&buffer, 0);
  216. // Copy the content
  217. *this = other;
  218. }
  219. CScriptArray::CScriptArray(asUINT length, void *defVal, asIObjectType *ot)
  220. {
  221. refCount = 1;
  222. gcFlag = false;
  223. objType = ot;
  224. objType->AddRef();
  225. buffer = 0;
  226. Precache();
  227. // Determine element size
  228. if( subTypeId & asTYPEID_MASK_OBJECT )
  229. elementSize = sizeof(asPWORD);
  230. else
  231. elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(subTypeId);
  232. // Make sure the array size isn't too large for us to handle
  233. if( !CheckMaxSize(length) )
  234. {
  235. // Don't continue with the initialization
  236. return;
  237. }
  238. CreateBuffer(&buffer, length);
  239. // Notify the GC of the successful creation
  240. if( objType->GetFlags() & asOBJ_GC )
  241. objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
  242. // Initialize the elements with the default value
  243. for( asUINT n = 0; n < GetSize(); n++ )
  244. SetValue(n, defVal);
  245. }
  246. void CScriptArray::SetValue(asUINT index, void *value)
  247. {
  248. // At() will take care of the out-of-bounds checking, though
  249. // if called from the application then nothing will be done
  250. void *ptr = At(index);
  251. if( ptr == 0 ) return;
  252. if( (subTypeId & ~asTYPEID_MASK_SEQNBR) && !(subTypeId & asTYPEID_OBJHANDLE) )
  253. objType->GetEngine()->AssignScriptObject(ptr, value, objType->GetSubType());
  254. else if( subTypeId & asTYPEID_OBJHANDLE )
  255. {
  256. void *tmp = *(void**)ptr;
  257. *(void**)ptr = *(void**)value;
  258. objType->GetEngine()->AddRefScriptObject(*(void**)value, objType->GetSubType());
  259. if( tmp )
  260. objType->GetEngine()->ReleaseScriptObject(tmp, objType->GetSubType());
  261. }
  262. else if( subTypeId == asTYPEID_BOOL ||
  263. subTypeId == asTYPEID_INT8 ||
  264. subTypeId == asTYPEID_UINT8 )
  265. *(char*)ptr = *(char*)value;
  266. else if( subTypeId == asTYPEID_INT16 ||
  267. subTypeId == asTYPEID_UINT16 )
  268. *(short*)ptr = *(short*)value;
  269. else if( subTypeId == asTYPEID_INT32 ||
  270. subTypeId == asTYPEID_UINT32 ||
  271. subTypeId == asTYPEID_FLOAT ||
  272. subTypeId > asTYPEID_DOUBLE ) // enums have a type id larger than doubles
  273. *(int*)ptr = *(int*)value;
  274. else if( subTypeId == asTYPEID_INT64 ||
  275. subTypeId == asTYPEID_UINT64 ||
  276. subTypeId == asTYPEID_DOUBLE )
  277. *(double*)ptr = *(double*)value;
  278. }
  279. CScriptArray::~CScriptArray()
  280. {
  281. if( buffer )
  282. {
  283. DeleteBuffer(buffer);
  284. buffer = 0;
  285. }
  286. if( objType ) objType->Release();
  287. }
  288. asUINT CScriptArray::GetSize() const
  289. {
  290. return buffer->numElements;
  291. }
  292. bool CScriptArray::IsEmpty() const
  293. {
  294. return buffer->numElements == 0;
  295. }
  296. void CScriptArray::Reserve(asUINT maxElements)
  297. {
  298. if( maxElements <= buffer->maxElements )
  299. return;
  300. // Allocate memory for the buffer
  301. SArrayBuffer *newBuffer;
  302. #if defined(__S3E__) // Marmalade doesn't understand (nothrow)
  303. newBuffer = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1 + elementSize*maxElements];
  304. #else
  305. newBuffer = (SArrayBuffer*)new (nothrow) asBYTE[sizeof(SArrayBuffer)-1 + elementSize*maxElements];
  306. #endif
  307. if( newBuffer )
  308. {
  309. newBuffer->numElements = buffer->numElements;
  310. newBuffer->maxElements = maxElements;
  311. }
  312. else
  313. {
  314. // Out of memory
  315. asIScriptContext *ctx = asGetActiveContext();
  316. if( ctx )
  317. ctx->SetException("Out of memory");
  318. return;
  319. }
  320. memcpy(newBuffer->data, buffer->data, buffer->numElements*elementSize);
  321. // Release the old buffer
  322. delete[] (asBYTE*)buffer;
  323. buffer = newBuffer;
  324. }
  325. void CScriptArray::Resize(asUINT numElements)
  326. {
  327. if( numElements & 0x80000000 )
  328. {
  329. CheckMaxSize(numElements);
  330. return;
  331. }
  332. Resize((int)numElements - (int)buffer->numElements, (asUINT)-1);
  333. }
  334. // Internal
  335. void CScriptArray::Resize(int delta, asUINT at)
  336. {
  337. if( delta < 0 )
  338. {
  339. if( -delta > (int)buffer->numElements )
  340. delta = -(int)buffer->numElements;
  341. if( at > buffer->numElements + delta )
  342. at = buffer->numElements + delta;
  343. }
  344. else if( delta > 0 )
  345. {
  346. // Make sure the array size isn't too large for us to handle
  347. if( delta > 0 && !CheckMaxSize(buffer->numElements + delta) )
  348. return;
  349. if( at > buffer->numElements )
  350. at = buffer->numElements;
  351. }
  352. if( delta == 0 ) return;
  353. if( buffer->maxElements < buffer->numElements + delta )
  354. {
  355. // Allocate memory for the buffer
  356. SArrayBuffer *newBuffer;
  357. #if defined(__S3E__) // Marmalade doesn't understand (nothrow)
  358. newBuffer = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1 + elementSize*(buffer->numElements + delta)];
  359. #else
  360. newBuffer = (SArrayBuffer*)new (nothrow) asBYTE[sizeof(SArrayBuffer)-1 + elementSize*(buffer->numElements + delta)];
  361. #endif
  362. if( newBuffer )
  363. {
  364. newBuffer->numElements = buffer->numElements + delta;
  365. newBuffer->maxElements = newBuffer->numElements;
  366. }
  367. else
  368. {
  369. // Out of memory
  370. asIScriptContext *ctx = asGetActiveContext();
  371. if( ctx )
  372. ctx->SetException("Out of memory");
  373. return;
  374. }
  375. memcpy(newBuffer->data, buffer->data, at*elementSize);
  376. if( at < buffer->numElements )
  377. memcpy(newBuffer->data + (at+delta)*elementSize, buffer->data + at*elementSize, (buffer->numElements-at)*elementSize);
  378. if( subTypeId & asTYPEID_MASK_OBJECT )
  379. Construct(newBuffer, at, at+delta);
  380. // Release the old buffer
  381. delete[] (asBYTE*)buffer;
  382. buffer = newBuffer;
  383. }
  384. else if( delta < 0 )
  385. {
  386. Destruct(buffer, at, at-delta);
  387. memmove(buffer->data + at*elementSize, buffer->data + (at-delta)*elementSize, (buffer->numElements - (at-delta))*elementSize);
  388. buffer->numElements += delta;
  389. }
  390. else
  391. {
  392. memmove(buffer->data + (at+delta)*elementSize, buffer->data + at*elementSize, (buffer->numElements - at)*elementSize);
  393. Construct(buffer, at, at+delta);
  394. buffer->numElements += delta;
  395. }
  396. }
  397. // internal
  398. bool CScriptArray::CheckMaxSize(asUINT numElements)
  399. {
  400. // This code makes sure the size of the buffer that is allocated
  401. // for the array doesn't overflow and becomes smaller than requested
  402. asUINT maxSize = 0xFFFFFFFFul - sizeof(SArrayBuffer) + 1;
  403. if( subTypeId & asTYPEID_MASK_OBJECT )
  404. maxSize /= sizeof(void*);
  405. else if( elementSize > 0 )
  406. maxSize /= elementSize;
  407. if( numElements > maxSize )
  408. {
  409. asIScriptContext *ctx = asGetActiveContext();
  410. if( ctx )
  411. {
  412. // Set a script exception
  413. ctx->SetException("Too large array size");
  414. }
  415. return false;
  416. }
  417. // OK
  418. return true;
  419. }
  420. asIObjectType *CScriptArray::GetArrayObjectType() const
  421. {
  422. return objType;
  423. }
  424. int CScriptArray::GetArrayTypeId() const
  425. {
  426. return objType->GetTypeId();
  427. }
  428. int CScriptArray::GetElementTypeId() const
  429. {
  430. return subTypeId;
  431. }
  432. void CScriptArray::InsertAt(asUINT index, void *value)
  433. {
  434. if( index > buffer->numElements )
  435. {
  436. // If this is called from a script we raise a script exception
  437. asIScriptContext *ctx = asGetActiveContext();
  438. if( ctx )
  439. ctx->SetException("Index out of bounds");
  440. return;
  441. }
  442. // Make room for the new element
  443. Resize(1, index);
  444. // Set the value of the new element
  445. SetValue(index, value);
  446. }
  447. void CScriptArray::InsertLast(void *value)
  448. {
  449. InsertAt(buffer->numElements, value);
  450. }
  451. void CScriptArray::RemoveAt(asUINT index)
  452. {
  453. if( index >= buffer->numElements )
  454. {
  455. // If this is called from a script we raise a script exception
  456. asIScriptContext *ctx = asGetActiveContext();
  457. if( ctx )
  458. ctx->SetException("Index out of bounds");
  459. return;
  460. }
  461. // Remove the element
  462. Resize(-1, index);
  463. }
  464. void CScriptArray::RemoveLast()
  465. {
  466. RemoveAt(buffer->numElements-1);
  467. }
  468. // Return a pointer to the array element. Returns 0 if the index is out of bounds
  469. const void *CScriptArray::At(asUINT index) const
  470. {
  471. if( buffer == 0 || index >= buffer->numElements )
  472. {
  473. // If this is called from a script we raise a script exception
  474. asIScriptContext *ctx = asGetActiveContext();
  475. if( ctx )
  476. ctx->SetException("Index out of bounds");
  477. return 0;
  478. }
  479. if( (subTypeId & asTYPEID_MASK_OBJECT) && !(subTypeId & asTYPEID_OBJHANDLE) )
  480. return (void*)((size_t*)buffer->data)[index];
  481. else
  482. return buffer->data + elementSize*index;
  483. }
  484. void *CScriptArray::At(asUINT index)
  485. {
  486. return const_cast<void*>(const_cast<const CScriptArray *>(this)->At(index));
  487. }
  488. // internal
  489. void CScriptArray::CreateBuffer(SArrayBuffer **buf, asUINT numElements)
  490. {
  491. if( subTypeId & asTYPEID_MASK_OBJECT )
  492. {
  493. #if defined(__S3E__) // Marmalade doesn't understand (nothrow)
  494. *buf = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1+sizeof(void*)*numElements];
  495. #else
  496. *buf = (SArrayBuffer*)new (nothrow) asBYTE[sizeof(SArrayBuffer)-1+sizeof(void*)*numElements];
  497. #endif
  498. }
  499. else
  500. {
  501. #if defined(__S3E__)
  502. *buf = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1+elementSize*numElements];
  503. #else
  504. *buf = (SArrayBuffer*)new (nothrow) asBYTE[sizeof(SArrayBuffer)-1+elementSize*numElements];
  505. #endif
  506. }
  507. if( *buf )
  508. {
  509. (*buf)->numElements = numElements;
  510. (*buf)->maxElements = numElements;
  511. Construct(*buf, 0, numElements);
  512. }
  513. else
  514. {
  515. // Oops, out of memory
  516. asIScriptContext *ctx = asGetActiveContext();
  517. if( ctx )
  518. ctx->SetException("Out of memory");
  519. }
  520. }
  521. // internal
  522. void CScriptArray::DeleteBuffer(SArrayBuffer *buf)
  523. {
  524. Destruct(buf, 0, buf->numElements);
  525. // Free the buffer
  526. delete[] (asBYTE*)buf;
  527. }
  528. // internal
  529. void CScriptArray::Construct(SArrayBuffer *buf, asUINT start, asUINT end)
  530. {
  531. if( subTypeId & asTYPEID_OBJHANDLE )
  532. {
  533. // Set all object handles to null
  534. void *d = (void*)(buf->data + start * sizeof(void*));
  535. memset(d, 0, (end-start)*sizeof(void*));
  536. }
  537. else if( subTypeId & asTYPEID_MASK_OBJECT )
  538. {
  539. void **max = (void**)(buf->data + end * sizeof(void*));
  540. void **d = (void**)(buf->data + start * sizeof(void*));
  541. asIScriptEngine *engine = objType->GetEngine();
  542. asIObjectType *subType = objType->GetSubType();
  543. for( ; d < max; d++ )
  544. *d = (void*)engine->CreateScriptObject(subType);
  545. }
  546. }
  547. // internal
  548. void CScriptArray::Destruct(SArrayBuffer *buf, asUINT start, asUINT end)
  549. {
  550. if( subTypeId & asTYPEID_MASK_OBJECT )
  551. {
  552. asIScriptEngine *engine = objType->GetEngine();
  553. void **max = (void**)(buf->data + end * sizeof(void*));
  554. void **d = (void**)(buf->data + start * sizeof(void*));
  555. for( ; d < max; d++ )
  556. {
  557. if( *d )
  558. engine->ReleaseScriptObject(*d, objType->GetSubType());
  559. }
  560. }
  561. }
  562. // internal
  563. bool CScriptArray::Less(const void *a, const void *b, bool asc, asIScriptContext *ctx, SArrayCache *cache)
  564. {
  565. if( !asc )
  566. {
  567. // Swap items
  568. const void *TEMP = a;
  569. a = b;
  570. b = TEMP;
  571. }
  572. if( !(subTypeId & ~asTYPEID_MASK_SEQNBR) )
  573. {
  574. // Simple compare of values
  575. switch( subTypeId )
  576. {
  577. #define COMPARE(T) *((T*)a) < *((T*)b)
  578. case asTYPEID_BOOL: return COMPARE(bool);
  579. case asTYPEID_INT8: return COMPARE(signed char);
  580. case asTYPEID_UINT8: return COMPARE(unsigned char);
  581. case asTYPEID_INT16: return COMPARE(signed short);
  582. case asTYPEID_UINT16: return COMPARE(unsigned short);
  583. case asTYPEID_INT32: return COMPARE(signed int);
  584. case asTYPEID_UINT32: return COMPARE(unsigned int);
  585. case asTYPEID_FLOAT: return COMPARE(float);
  586. case asTYPEID_DOUBLE: return COMPARE(double);
  587. default: return COMPARE(signed int); // All enums fall in this case
  588. #undef COMPARE
  589. }
  590. }
  591. else
  592. {
  593. int r = 0;
  594. if( subTypeId & asTYPEID_OBJHANDLE )
  595. {
  596. // Allow sort to work even if the array contains null handles
  597. if( *(void**)a == 0 ) return true;
  598. if( *(void**)b == 0 ) return false;
  599. }
  600. // Execute object opCmp
  601. if( cache && cache->cmpFunc )
  602. {
  603. // TODO: Add proper error handling
  604. r = ctx->Prepare(cache->cmpFunc); assert(r >= 0);
  605. if( subTypeId & asTYPEID_OBJHANDLE )
  606. {
  607. r = ctx->SetObject(*((void**)a)); assert(r >= 0);
  608. r = ctx->SetArgObject(0, *((void**)b)); assert(r >= 0);
  609. }
  610. else
  611. {
  612. r = ctx->SetObject((void*)a); assert(r >= 0);
  613. r = ctx->SetArgObject(0, (void*)b); assert(r >= 0);
  614. }
  615. r = ctx->Execute();
  616. if( r == asEXECUTION_FINISHED )
  617. {
  618. return (int)ctx->GetReturnDWord() < 0;
  619. }
  620. }
  621. }
  622. return false;
  623. }
  624. void CScriptArray::Reverse()
  625. {
  626. asUINT size = GetSize();
  627. if( size >= 2 )
  628. {
  629. asBYTE TEMP[16];
  630. for( asUINT i = 0; i < size / 2; i++ )
  631. {
  632. Copy(TEMP, GetArrayItemPointer(i));
  633. Copy(GetArrayItemPointer(i), GetArrayItemPointer(size - i - 1));
  634. Copy(GetArrayItemPointer(size - i - 1), TEMP);
  635. }
  636. }
  637. }
  638. bool CScriptArray::operator==(const CScriptArray &other) const
  639. {
  640. if( objType != other.objType )
  641. return false;
  642. if( GetSize() != other.GetSize() )
  643. return false;
  644. asIScriptContext *cmpContext = 0;
  645. bool isNested = false;
  646. if( subTypeId & ~asTYPEID_MASK_SEQNBR )
  647. {
  648. // Try to reuse the active context
  649. cmpContext = asGetActiveContext();
  650. if( cmpContext )
  651. {
  652. if( cmpContext->PushState() >= 0 )
  653. isNested = true;
  654. else
  655. cmpContext = 0;
  656. }
  657. if( cmpContext == 0 )
  658. {
  659. // TODO: Ideally this context would be retrieved from a pool, so we don't have to
  660. // create a new one everytime. We could keep a context with the array object
  661. // but that would consume a lot of resources as each context is quite heavy.
  662. cmpContext = objType->GetEngine()->CreateContext();
  663. }
  664. }
  665. // Check if all elements are equal
  666. bool isEqual = true;
  667. SArrayCache *cache = reinterpret_cast<SArrayCache*>(objType->GetUserData(ARRAY_CACHE));
  668. for( asUINT n = 0; n < GetSize(); n++ )
  669. if( !Equals(At(n), other.At(n), cmpContext, cache) )
  670. {
  671. isEqual = false;
  672. break;
  673. }
  674. if( cmpContext )
  675. if( isNested )
  676. {
  677. asEContextState state = cmpContext->GetState();
  678. cmpContext->PopState();
  679. if( state == asEXECUTION_ABORTED )
  680. cmpContext->Abort();
  681. }
  682. else
  683. cmpContext->Release();
  684. return isEqual;
  685. }
  686. // internal
  687. bool CScriptArray::Equals(const void *a, const void *b, asIScriptContext *ctx, SArrayCache *cache) const
  688. {
  689. if( !(subTypeId & ~asTYPEID_MASK_SEQNBR) )
  690. {
  691. // Simple compare of values
  692. switch( subTypeId )
  693. {
  694. #define COMPARE(T) *((T*)a) == *((T*)b)
  695. case asTYPEID_BOOL: return COMPARE(bool);
  696. case asTYPEID_INT8: return COMPARE(signed char);
  697. case asTYPEID_UINT8: return COMPARE(unsigned char);
  698. case asTYPEID_INT16: return COMPARE(signed short);
  699. case asTYPEID_UINT16: return COMPARE(unsigned short);
  700. case asTYPEID_INT32: return COMPARE(signed int);
  701. case asTYPEID_UINT32: return COMPARE(unsigned int);
  702. case asTYPEID_FLOAT: return COMPARE(float);
  703. case asTYPEID_DOUBLE: return COMPARE(double);
  704. default: return COMPARE(signed int); // All enums fall here
  705. #undef COMPARE
  706. }
  707. }
  708. else
  709. {
  710. int r = 0;
  711. if( subTypeId & asTYPEID_OBJHANDLE )
  712. {
  713. // Allow the find to work even if the array contains null handles
  714. if( *(void**)a == *(void**)b ) return true;
  715. }
  716. // Execute object opEquals if available
  717. if( cache && cache->eqFunc )
  718. {
  719. // TODO: Add proper error handling
  720. r = ctx->Prepare(cache->eqFunc); assert(r >= 0);
  721. if( subTypeId & asTYPEID_OBJHANDLE )
  722. {
  723. r = ctx->SetObject(*((void**)a)); assert(r >= 0);
  724. r = ctx->SetArgObject(0, *((void**)b)); assert(r >= 0);
  725. }
  726. else
  727. {
  728. r = ctx->SetObject((void*)a); assert(r >= 0);
  729. r = ctx->SetArgObject(0, (void*)b); assert(r >= 0);
  730. }
  731. r = ctx->Execute();
  732. if( r == asEXECUTION_FINISHED )
  733. return ctx->GetReturnByte() != 0;
  734. return false;
  735. }
  736. // Execute object opCmp if available
  737. if( cache && cache->cmpFunc )
  738. {
  739. // TODO: Add proper error handling
  740. r = ctx->Prepare(cache->cmpFunc); assert(r >= 0);
  741. if( subTypeId & asTYPEID_OBJHANDLE )
  742. {
  743. r = ctx->SetObject(*((void**)a)); assert(r >= 0);
  744. r = ctx->SetArgObject(0, *((void**)b)); assert(r >= 0);
  745. }
  746. else
  747. {
  748. r = ctx->SetObject((void*)a); assert(r >= 0);
  749. r = ctx->SetArgObject(0, (void*)b); assert(r >= 0);
  750. }
  751. r = ctx->Execute();
  752. if( r == asEXECUTION_FINISHED )
  753. return (int)ctx->GetReturnDWord() == 0;
  754. return false;
  755. }
  756. }
  757. return false;
  758. }
  759. int CScriptArray::Find(void *value) const
  760. {
  761. return Find(0, value);
  762. }
  763. int CScriptArray::Find(asUINT index, void *value) const
  764. {
  765. // Check if the subtype really supports find()
  766. // TODO: Can't this be done at compile time too by the template callback
  767. SArrayCache *cache = 0;
  768. if( subTypeId & ~asTYPEID_MASK_SEQNBR )
  769. {
  770. cache = reinterpret_cast<SArrayCache*>(objType->GetUserData(ARRAY_CACHE));
  771. if( !cache || (cache->cmpFunc == 0 && cache->eqFunc == 0) )
  772. {
  773. asIScriptContext *ctx = asGetActiveContext();
  774. asIObjectType* subType = objType->GetEngine()->GetObjectTypeById(subTypeId);
  775. // Throw an exception
  776. if( ctx )
  777. {
  778. char tmp[512];
  779. if( cache && cache->eqFuncReturnCode == asMULTIPLE_FUNCTIONS )
  780. #if defined(_MSC_VER) && _MSC_VER >= 1500 && !defined(__S3E__)
  781. sprintf_s(tmp, 512, "Type '%s' has multiple matching opEquals or opCmp methods", subType->GetName());
  782. #else
  783. sprintf(tmp, "Type '%s' has multiple matching opEquals or opCmp methods", subType->GetName());
  784. #endif
  785. else
  786. #if defined(_MSC_VER) && _MSC_VER >= 1500 && !defined(__S3E__)
  787. sprintf_s(tmp, 512, "Type '%s' does not have a matching opEquals or opCmp method", subType->GetName());
  788. #else
  789. sprintf(tmp, "Type '%s' does not have a matching opEquals or opCmp method", subType->GetName());
  790. #endif
  791. ctx->SetException(tmp);
  792. }
  793. return -1;
  794. }
  795. }
  796. asIScriptContext *cmpContext = 0;
  797. bool isNested = false;
  798. if( subTypeId & ~asTYPEID_MASK_SEQNBR )
  799. {
  800. // Try to reuse the active context
  801. cmpContext = asGetActiveContext();
  802. if( cmpContext )
  803. {
  804. if( cmpContext->PushState() >= 0 )
  805. isNested = true;
  806. else
  807. cmpContext = 0;
  808. }
  809. if( cmpContext == 0 )
  810. {
  811. // TODO: Ideally this context would be retrieved from a pool, so we don't have to
  812. // create a new one everytime. We could keep a context with the array object
  813. // but that would consume a lot of resources as each context is quite heavy.
  814. cmpContext = objType->GetEngine()->CreateContext();
  815. }
  816. }
  817. // Find the matching element
  818. int ret = -1;
  819. asUINT size = GetSize();
  820. if( index < size )
  821. {
  822. for( asUINT i = index; i < size; i++ )
  823. {
  824. // value passed by reference
  825. if( Equals(At(i), (value), cmpContext, cache) )
  826. {
  827. ret = (int)i;
  828. break;
  829. }
  830. }
  831. }
  832. if( cmpContext )
  833. if( isNested )
  834. {
  835. asEContextState state = cmpContext->GetState();
  836. cmpContext->PopState();
  837. if( state == asEXECUTION_ABORTED )
  838. cmpContext->Abort();
  839. }
  840. else
  841. cmpContext->Release();
  842. return ret;
  843. }
  844. // internal
  845. // Copy object handle or primitive value
  846. void CScriptArray::Copy(void *dst, void *src)
  847. {
  848. memcpy(dst, src, elementSize);
  849. }
  850. // internal
  851. // Return pointer to array item (object handle or primitive value)
  852. void *CScriptArray::GetArrayItemPointer(int index)
  853. {
  854. return buffer->data + index * elementSize;
  855. }
  856. // internal
  857. // Return pointer to data in buffer (object or primitive)
  858. void *CScriptArray::GetDataPointer(void *buffer)
  859. {
  860. if ((subTypeId & asTYPEID_MASK_OBJECT) && !(subTypeId & asTYPEID_OBJHANDLE) )
  861. {
  862. // Real address of object
  863. return reinterpret_cast<void*>(*(size_t*)buffer);
  864. }
  865. else
  866. {
  867. // Primitive is just a raw data
  868. return buffer;
  869. }
  870. }
  871. // Sort ascending
  872. void CScriptArray::SortAsc()
  873. {
  874. Sort(0, GetSize(), true);
  875. }
  876. // Sort ascending
  877. void CScriptArray::SortAsc(asUINT index, asUINT count)
  878. {
  879. Sort(index, count, true);
  880. }
  881. // Sort descending
  882. void CScriptArray::SortDesc()
  883. {
  884. Sort(0, GetSize(), false);
  885. }
  886. // Sort descending
  887. void CScriptArray::SortDesc(asUINT index, asUINT count)
  888. {
  889. Sort(index, count, false);
  890. }
  891. // internal
  892. void CScriptArray::Sort(asUINT index, asUINT count, bool asc)
  893. {
  894. // Subtype isn't primitive and doesn't have opCmp
  895. SArrayCache *cache = reinterpret_cast<SArrayCache*>(objType->GetUserData(ARRAY_CACHE));
  896. if( subTypeId & ~asTYPEID_MASK_SEQNBR )
  897. {
  898. if( !cache || cache->cmpFunc == 0 )
  899. {
  900. asIScriptContext *ctx = asGetActiveContext();
  901. asIObjectType* subType = objType->GetEngine()->GetObjectTypeById(subTypeId);
  902. // Throw an exception
  903. if( ctx )
  904. {
  905. char tmp[512];
  906. if( cache && cache->cmpFuncReturnCode == asMULTIPLE_FUNCTIONS )
  907. #if defined(_MSC_VER) && _MSC_VER >= 1500 && !defined(__S3E__)
  908. sprintf_s(tmp, 512, "Type '%s' has multiple matching opCmp methods", subType->GetName());
  909. #else
  910. sprintf(tmp, "Type '%s' has multiple matching opCmp methods", subType->GetName());
  911. #endif
  912. else
  913. #if defined(_MSC_VER) && _MSC_VER >= 1500 && !defined(__S3E__)
  914. sprintf_s(tmp, 512, "Type '%s' does not have a matching opCmp method", subType->GetName());
  915. #else
  916. sprintf(tmp, "Type '%s' does not have a matching opCmp method", subType->GetName());
  917. #endif
  918. ctx->SetException(tmp);
  919. }
  920. return;
  921. }
  922. }
  923. // No need to sort
  924. if( count < 2 )
  925. {
  926. return;
  927. }
  928. int start = index;
  929. int end = index + count;
  930. // Check if we could access invalid item while sorting
  931. if( start >= (int)buffer->numElements || end > (int)buffer->numElements )
  932. {
  933. asIScriptContext *ctx = asGetActiveContext();
  934. // Throw an exception
  935. if( ctx )
  936. {
  937. ctx->SetException("Index out of bounds");
  938. }
  939. return;
  940. }
  941. asBYTE tmp[16];
  942. asIScriptContext *cmpContext = 0;
  943. bool isNested = false;
  944. if( subTypeId & ~asTYPEID_MASK_SEQNBR )
  945. {
  946. // Try to reuse the active context
  947. cmpContext = asGetActiveContext();
  948. if( cmpContext )
  949. {
  950. if( cmpContext->PushState() >= 0 )
  951. isNested = true;
  952. else
  953. cmpContext = 0;
  954. }
  955. if( cmpContext == 0 )
  956. {
  957. // TODO: Ideally this context would be retrieved from a pool, so we don't have to
  958. // create a new one everytime. We could keep a context with the array object
  959. // but that would consume a lot of resources as each context is quite heavy.
  960. cmpContext = objType->GetEngine()->CreateContext();
  961. }
  962. }
  963. // Insertion sort
  964. for( int i = start + 1; i < end; i++ )
  965. {
  966. Copy(tmp, GetArrayItemPointer(i));
  967. int j = i - 1;
  968. while( j >= start && Less(GetDataPointer(tmp), At(j), asc, cmpContext, cache) )
  969. {
  970. Copy(GetArrayItemPointer(j + 1), GetArrayItemPointer(j));
  971. j--;
  972. }
  973. Copy(GetArrayItemPointer(j + 1), tmp);
  974. }
  975. if( cmpContext )
  976. if( isNested )
  977. {
  978. asEContextState state = cmpContext->GetState();
  979. cmpContext->PopState();
  980. if( state == asEXECUTION_ABORTED )
  981. cmpContext->Abort();
  982. }
  983. else
  984. cmpContext->Release();
  985. }
  986. // internal
  987. void CScriptArray::CopyBuffer(SArrayBuffer *dst, SArrayBuffer *src)
  988. {
  989. asIScriptEngine *engine = objType->GetEngine();
  990. if( subTypeId & asTYPEID_OBJHANDLE )
  991. {
  992. // Copy the references and increase the reference counters
  993. if( dst->numElements > 0 && src->numElements > 0 )
  994. {
  995. int count = dst->numElements > src->numElements ? src->numElements : dst->numElements;
  996. void **max = (void**)(dst->data + count * sizeof(void*));
  997. void **d = (void**)dst->data;
  998. void **s = (void**)src->data;
  999. for( ; d < max; d++, s++ )
  1000. {
  1001. void *tmp = *d;
  1002. *d = *s;
  1003. if( *d )
  1004. engine->AddRefScriptObject(*d, objType->GetSubType());
  1005. // Release the old ref after incrementing the new to avoid problem incase it is the same ref
  1006. if( tmp )
  1007. engine->ReleaseScriptObject(tmp, objType->GetSubType());
  1008. }
  1009. }
  1010. }
  1011. else
  1012. {
  1013. if( dst->numElements > 0 && src->numElements > 0 )
  1014. {
  1015. int count = dst->numElements > src->numElements ? src->numElements : dst->numElements;
  1016. if( subTypeId & asTYPEID_MASK_OBJECT )
  1017. {
  1018. // Call the assignment operator on all of the objects
  1019. void **max = (void**)(dst->data + count * sizeof(void*));
  1020. void **d = (void**)dst->data;
  1021. void **s = (void**)src->data;
  1022. asIObjectType *subType = objType->GetSubType();
  1023. for( ; d < max; d++, s++ )
  1024. engine->AssignScriptObject(*d, *s, subType);
  1025. }
  1026. else
  1027. {
  1028. // Primitives are copied byte for byte
  1029. memcpy(dst->data, src->data, count*elementSize);
  1030. }
  1031. }
  1032. }
  1033. }
  1034. // internal
  1035. // Precache some info
  1036. void CScriptArray::Precache()
  1037. {
  1038. subTypeId = objType->GetSubTypeId();
  1039. // Check if it is an array of objects. Only for these do we need to cache anything
  1040. // Type ids for primitives and enums only has the sequence number part
  1041. if( !(subTypeId & ~asTYPEID_MASK_SEQNBR) )
  1042. return;
  1043. // The opCmp and opEquals methods are cached because the searching for the
  1044. // methods is quite time consuming if a lot of array objects are created.
  1045. // First check if a cache already exists for this array type
  1046. SArrayCache *cache = reinterpret_cast<SArrayCache*>(objType->GetUserData(ARRAY_CACHE));
  1047. if( cache ) return;
  1048. // We need to make sure the cache is created only once, even
  1049. // if multiple threads reach the same point at the same time
  1050. asAcquireExclusiveLock();
  1051. // Now that we got the lock, we need to check again to make sure the
  1052. // cache wasn't created while we were waiting for the lock
  1053. cache = reinterpret_cast<SArrayCache*>(objType->GetUserData(ARRAY_CACHE));
  1054. if( cache )
  1055. {
  1056. asReleaseExclusiveLock();
  1057. return;
  1058. }
  1059. // Create the cache
  1060. cache = new SArrayCache();
  1061. memset(cache, 0, sizeof(SArrayCache));
  1062. // If the sub type is a handle to const, then the methods must be const too
  1063. bool mustBeConst = (subTypeId & asTYPEID_HANDLETOCONST) ? true : false;
  1064. asIObjectType *subType = objType->GetEngine()->GetObjectTypeById(subTypeId);
  1065. if( subType )
  1066. {
  1067. for( asUINT i = 0; i < subType->GetMethodCount(); i++ )
  1068. {
  1069. asIScriptFunction *func = subType->GetMethodByIndex(i);
  1070. if( func->GetParamCount() == 1 && (!mustBeConst || func->IsReadOnly()) )
  1071. {
  1072. asDWORD flags = 0;
  1073. int returnTypeId = func->GetReturnTypeId(&flags);
  1074. // The method must not return a reference
  1075. if( flags != asTM_NONE )
  1076. continue;
  1077. // opCmp returns an int and opEquals returns a bool
  1078. bool isCmp = false, isEq = false;
  1079. if( returnTypeId == asTYPEID_INT32 && strcmp(func->GetName(), "opCmp") == 0 )
  1080. isCmp = true;
  1081. if( returnTypeId == asTYPEID_BOOL && strcmp(func->GetName(), "opEquals") == 0 )
  1082. isEq = true;
  1083. if( !isCmp && !isEq )
  1084. continue;
  1085. // The parameter must either be a reference to the subtype or a handle to the subtype
  1086. int paramTypeId = func->GetParamTypeId(0, &flags);
  1087. if( (paramTypeId & ~(asTYPEID_OBJHANDLE|asTYPEID_HANDLETOCONST)) != (subTypeId & ~(asTYPEID_OBJHANDLE|asTYPEID_HANDLETOCONST)) )
  1088. continue;
  1089. if( (flags & asTM_INREF) )
  1090. {
  1091. if( (paramTypeId & asTYPEID_OBJHANDLE) || mustBeConst && !(flags & asTM_CONST) )
  1092. continue;
  1093. }
  1094. else if( paramTypeId & asTYPEID_OBJHANDLE )
  1095. {
  1096. if( mustBeConst && !(paramTypeId & asTYPEID_HANDLETOCONST) )
  1097. continue;
  1098. }
  1099. else
  1100. continue;
  1101. if( isCmp )
  1102. {
  1103. if( cache->cmpFunc || cache->cmpFuncReturnCode )
  1104. {
  1105. cache->cmpFunc = 0;
  1106. cache->cmpFuncReturnCode = asMULTIPLE_FUNCTIONS;
  1107. }
  1108. else
  1109. cache->cmpFunc = func;
  1110. }
  1111. else if( isEq )
  1112. {
  1113. if( cache->eqFunc || cache->eqFuncReturnCode )
  1114. {
  1115. cache->eqFunc = 0;
  1116. cache->eqFuncReturnCode = asMULTIPLE_FUNCTIONS;
  1117. }
  1118. else
  1119. cache->eqFunc = func;
  1120. }
  1121. }
  1122. }
  1123. }
  1124. if( cache->eqFunc == 0 && cache->eqFuncReturnCode == 0 )
  1125. cache->eqFuncReturnCode = asNO_FUNCTION;
  1126. if( cache->cmpFunc == 0 && cache->cmpFuncReturnCode == 0 )
  1127. cache->cmpFuncReturnCode = asNO_FUNCTION;
  1128. // Set the user data only at the end so others that retrieve it will know it is complete
  1129. objType->SetUserData(cache, ARRAY_CACHE);
  1130. asReleaseExclusiveLock();
  1131. }
  1132. // GC behaviour
  1133. void CScriptArray::EnumReferences(asIScriptEngine *engine)
  1134. {
  1135. // If the array is holding handles, then we need to notify the GC of them
  1136. if( subTypeId & asTYPEID_MASK_OBJECT )
  1137. {
  1138. void **d = (void**)buffer->data;
  1139. for( asUINT n = 0; n < buffer->numElements; n++ )
  1140. {
  1141. if( d[n] )
  1142. engine->GCEnumCallback(d[n]);
  1143. }
  1144. }
  1145. }
  1146. // GC behaviour
  1147. void CScriptArray::ReleaseAllHandles(asIScriptEngine *engine)
  1148. {
  1149. // Resizing to zero will release everything
  1150. Resize(0);
  1151. }
  1152. void CScriptArray::AddRef() const
  1153. {
  1154. // Clear the GC flag then increase the counter
  1155. gcFlag = false;
  1156. asAtomicInc(refCount);
  1157. }
  1158. void CScriptArray::Release() const
  1159. {
  1160. // Clearing the GC flag then descrease the counter
  1161. gcFlag = false;
  1162. if( asAtomicDec(refCount) == 0 )
  1163. {
  1164. // When reaching 0 no more references to this instance
  1165. // exists and the object should be destroyed
  1166. delete this;
  1167. }
  1168. }
  1169. // GC behaviour
  1170. int CScriptArray::GetRefCount()
  1171. {
  1172. return refCount;
  1173. }
  1174. // GC behaviour
  1175. void CScriptArray::SetFlag()
  1176. {
  1177. gcFlag = true;
  1178. }
  1179. // GC behaviour
  1180. bool CScriptArray::GetFlag()
  1181. {
  1182. return gcFlag;
  1183. }
  1184. static void ScriptArrayClear(CScriptArray* ptr)
  1185. {
  1186. ptr->Resize(0);
  1187. }
  1188. void RegisterArray(asIScriptEngine* engine)
  1189. {
  1190. // Register the object type user data clean up
  1191. engine->SetObjectTypeUserDataCleanupCallback(CleanupObjectTypeArrayCache, ARRAY_CACHE);
  1192. engine->RegisterObjectType("Array<class T>", 0, asOBJ_REF | asOBJ_TEMPLATE);
  1193. engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", asFUNCTION(ScriptArrayTemplateCallback), asCALL_CDECL);
  1194. engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_FACTORY, "Array<T>@ f(int& in)", asFUNCTIONPR(ScriptArrayFactory, (asIObjectType*), CScriptArray*), asCALL_CDECL);
  1195. engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_FACTORY, "Array<T>@ f(int& in, uint)", asFUNCTIONPR(ScriptArrayFactory2, (asIObjectType*, asUINT), CScriptArray*), asCALL_CDECL);
  1196. engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_FACTORY, "Array<T>@ f(int& in, uint, const T& in)", asFUNCTIONPR(ScriptArrayFactoryDefVal, (asIObjectType*, asUINT, void *), CScriptArray*), asCALL_CDECL);
  1197. engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_LIST_FACTORY, "Array<T>@ f(int& in, uint)", asFUNCTIONPR(ScriptArrayFactory2, (asIObjectType*, asUINT), CScriptArray*), asCALL_CDECL);
  1198. engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptArray,AddRef), asCALL_THISCALL);
  1199. engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptArray,Release), asCALL_THISCALL);
  1200. engine->RegisterObjectMethod("Array<T>", "T& opIndex(uint)", asMETHODPR(CScriptArray, At, (unsigned), void*), asCALL_THISCALL);
  1201. engine->RegisterObjectMethod("Array<T>", "const T& opIndex(uint) const", asMETHODPR(CScriptArray, At, (unsigned), void*), asCALL_THISCALL);
  1202. engine->RegisterObjectMethod("Array<T>", "Array<T>& opAssign(const Array<T>& in)", asMETHOD(CScriptArray, operator=), asCALL_THISCALL);
  1203. engine->RegisterObjectMethod("Array<T>", "void Insert(uint, const T& in)", asMETHOD(CScriptArray, InsertAt), asCALL_THISCALL);
  1204. engine->RegisterObjectMethod("Array<T>", "void Erase(uint)", asMETHOD(CScriptArray, RemoveAt), asCALL_THISCALL);
  1205. engine->RegisterObjectMethod("Array<T>", "void Push(const T& in)", asMETHOD(CScriptArray, InsertLast), asCALL_THISCALL);
  1206. engine->RegisterObjectMethod("Array<T>", "void Pop()", asMETHOD(CScriptArray, RemoveLast), asCALL_THISCALL);
  1207. engine->RegisterObjectMethod("Array<T>", "void Resize(uint)", asMETHODPR(CScriptArray, Resize, (asUINT), void), asCALL_THISCALL);
  1208. engine->RegisterObjectMethod("Array<T>", "void Clear()", asFUNCTION(ScriptArrayClear), asCALL_CDECL_OBJLAST);
  1209. engine->RegisterObjectMethod("Array<T>", "void Sort()", asMETHODPR(CScriptArray, SortAsc, (), void), asCALL_THISCALL);
  1210. engine->RegisterObjectMethod("Array<T>", "void Sort(uint, uint)", asMETHODPR(CScriptArray, SortAsc, (asUINT, asUINT), void), asCALL_THISCALL);
  1211. engine->RegisterObjectMethod("Array<T>", "void SortReverse()", asMETHODPR(CScriptArray, SortDesc, (), void), asCALL_THISCALL);
  1212. engine->RegisterObjectMethod("Array<T>", "void SortReverse(uint, uint)", asMETHODPR(CScriptArray, SortDesc, (asUINT, asUINT), void), asCALL_THISCALL);
  1213. engine->RegisterObjectMethod("Array<T>", "void Reverse()", asMETHOD(CScriptArray, Reverse), asCALL_THISCALL);
  1214. engine->RegisterObjectMethod("Array<T>", "int Find(const T&in) const", asMETHODPR(CScriptArray, Find, (void*) const, int), asCALL_THISCALL);
  1215. engine->RegisterObjectMethod("Array<T>", "int Find(uint, const T&in) const", asMETHODPR(CScriptArray, Find, (asUINT, void*) const, int), asCALL_THISCALL);
  1216. engine->RegisterObjectMethod("Array<T>", "bool opEquals(const Array<T>&in) const", asMETHOD(CScriptArray, operator==), asCALL_THISCALL);
  1217. engine->RegisterObjectMethod("Array<T>", "uint get_length() const", asMETHOD(CScriptArray, GetSize), asCALL_THISCALL);
  1218. engine->RegisterObjectMethod("Array<T>", "void set_length(uint)", asMETHODPR(CScriptArray, Resize, (asUINT), void), asCALL_THISCALL);
  1219. engine->RegisterObjectMethod("Array<T>", "bool get_empty() const", asMETHOD(CScriptArray, IsEmpty), asCALL_THISCALL);
  1220. engine->RegisterDefaultArrayType("Array<T>");
  1221. }
  1222. static String StringFactory(asUINT length, const char* s)
  1223. {
  1224. return String(s, length);
  1225. }
  1226. static void ConstructString(String* ptr)
  1227. {
  1228. new(ptr) String();
  1229. }
  1230. static void ConstructStringCopy(const String& str, String* ptr)
  1231. {
  1232. new(ptr) String(str);
  1233. }
  1234. static void DestructString(String* ptr)
  1235. {
  1236. ptr->~String();
  1237. }
  1238. static char* StringCharAt(unsigned int i, String& str)
  1239. {
  1240. if (i >= str.Length())
  1241. {
  1242. asIScriptContext* context = asGetActiveContext();
  1243. if (context)
  1244. context->SetException("Index out of bounds");
  1245. return 0;
  1246. }
  1247. return &str[i];
  1248. }
  1249. static int StringCmp(const String& lhs, const String& rhs)
  1250. {
  1251. int cmp = 0;
  1252. if (lhs < rhs)
  1253. cmp = -1;
  1254. else if (lhs > rhs)
  1255. cmp = 1;
  1256. return cmp;
  1257. }
  1258. void StringResize(unsigned newSize, String& str)
  1259. {
  1260. unsigned oldSize = str.Length();
  1261. str.Resize(newSize);
  1262. for (unsigned i = oldSize; i < newSize; ++i)
  1263. str[i] = ' ';
  1264. }
  1265. static void ConstructStringInt(int value, String* ptr)
  1266. {
  1267. new(ptr) String(value);
  1268. }
  1269. static void ConstructStringUInt(unsigned value, String* ptr)
  1270. {
  1271. new(ptr) String(value);
  1272. }
  1273. static void ConstructStringFloat(float value, String* ptr)
  1274. {
  1275. new(ptr) String(value);
  1276. }
  1277. static void ConstructStringBool(bool value, String* ptr)
  1278. {
  1279. new(ptr) String(value);
  1280. }
  1281. static String& StringAssignInt(int value, String& str)
  1282. {
  1283. str = String(value);
  1284. return str;
  1285. }
  1286. static String& StringAddAssignInt(int value, String& str)
  1287. {
  1288. str += String(value);
  1289. return str;
  1290. }
  1291. static String StringAddInt(int value, const String& str)
  1292. {
  1293. return str + String(value);
  1294. }
  1295. static String StringAddIntReverse(int value, const String& str)
  1296. {
  1297. return String(value) + str;
  1298. }
  1299. static String& StringAssignUInt(unsigned value, String& str)
  1300. {
  1301. str = String(value);
  1302. return str;
  1303. }
  1304. static String& StringAddAssignUInt(unsigned value, String& str)
  1305. {
  1306. str += String(value);
  1307. return str;
  1308. }
  1309. static String StringAddUInt(unsigned value, const String& str)
  1310. {
  1311. return str + String(value);
  1312. }
  1313. static String StringAddUIntReverse(unsigned value, const String& str)
  1314. {
  1315. return String(value) + str;
  1316. }
  1317. static String& StringAssignFloat(float value, String& str)
  1318. {
  1319. str = String(value);
  1320. return str;
  1321. }
  1322. static String& StringAddAssignFloat(float value, String& str)
  1323. {
  1324. str += String(value);
  1325. return str;
  1326. }
  1327. static String StringAddFloat(float value, const String& str)
  1328. {
  1329. return str + String(value);
  1330. }
  1331. static String StringAddFloatReverse(float value, const String& str)
  1332. {
  1333. return String(value) + str;
  1334. }
  1335. static String& StringAssignBool(bool value, String& str)
  1336. {
  1337. str = String(value);
  1338. return str;
  1339. }
  1340. static String& StringAddAssignBool(bool value, String& str)
  1341. {
  1342. str += String(value);
  1343. return str;
  1344. }
  1345. static String StringAddBool(bool value, const String& str)
  1346. {
  1347. return str + String(value);
  1348. }
  1349. static String StringAddBoolReverse(bool value, const String& str)
  1350. {
  1351. return String(value) + str;
  1352. }
  1353. static void StringSetUTF8FromLatin1(const String& src, String& str)
  1354. {
  1355. str.SetUTF8FromLatin1(src.CString());
  1356. }
  1357. void RegisterString(asIScriptEngine *engine)
  1358. {
  1359. engine->RegisterObjectType("String", sizeof(String), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK);
  1360. engine->RegisterStringFactory("String", asFUNCTION(StringFactory), asCALL_CDECL);
  1361. engine->RegisterObjectBehaviour("String", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructString), asCALL_CDECL_OBJLAST);
  1362. engine->RegisterObjectBehaviour("String", asBEHAVE_CONSTRUCT, "void f(const String&in)", asFUNCTION(ConstructStringCopy), asCALL_CDECL_OBJLAST);
  1363. engine->RegisterObjectBehaviour("String", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructString), asCALL_CDECL_OBJLAST);
  1364. engine->RegisterObjectMethod("String", "String& opAssign(const String&in)", asMETHODPR(String, operator =, (const String&), String&), asCALL_THISCALL);
  1365. engine->RegisterObjectMethod("String", "String& opAddAssign(const String&in)", asMETHODPR(String, operator +=, (const String&), String&), asCALL_THISCALL);
  1366. engine->RegisterObjectMethod("String", "bool opEquals(const String&in) const", asMETHODPR(String, operator ==, (const String&) const, bool), asCALL_THISCALL);
  1367. engine->RegisterObjectMethod("String", "int opCmp(const String&in) const", asFUNCTION(StringCmp), asCALL_CDECL_OBJFIRST);
  1368. engine->RegisterObjectMethod("String", "String opAdd(const String&in) const", asMETHODPR(String, operator +, (const String&) const, String), asCALL_THISCALL);
  1369. engine->RegisterObjectMethod("String", "uint8 &opIndex(uint)", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST);
  1370. engine->RegisterObjectMethod("String", "const uint8 &opIndex(uint) const", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST);
  1371. engine->RegisterObjectMethod("String", "void Replace(uint8, uint8, bool caseSensitive = true)", asMETHODPR(String, Replace, (char, char, bool), void), asCALL_THISCALL);
  1372. engine->RegisterObjectMethod("String", "void Replace(const String&in, const String&in, bool caseSensitive = true)", asMETHODPR(String, Replace, (const String&, const String&, bool), void), asCALL_THISCALL);
  1373. engine->RegisterObjectMethod("String", "String Replaced(uint8, uint8, bool caseSensitive = true) const", asMETHODPR(String, Replaced, (char, char, bool) const, String), asCALL_THISCALL);
  1374. engine->RegisterObjectMethod("String", "String Replaced(const String&in, const String&in, bool caseSensitive = true) const", asMETHODPR(String, Replaced, (const String&, const String&, bool) const, String), asCALL_THISCALL);
  1375. engine->RegisterObjectMethod("String", "void Resize(uint)", asFUNCTION(StringResize), asCALL_CDECL_OBJLAST);
  1376. engine->RegisterObjectMethod("String", "uint Find(const String&in, uint start = 0, bool caseSensitive = true) const", asMETHODPR(String, Find, (const String&, unsigned, bool) const, unsigned), asCALL_THISCALL);
  1377. engine->RegisterObjectMethod("String", "uint Find(uint8, uint start = 0, bool caseSensitive = true) const", asMETHODPR(String, Find, (char, unsigned, bool) const, unsigned), asCALL_THISCALL);
  1378. engine->RegisterObjectMethod("String", "uint FindLast(const String&in, uint start = 0xffffffff, bool caseSensitive = true) const", asMETHODPR(String, FindLast, (const String&, unsigned, bool) const, unsigned), asCALL_THISCALL);
  1379. engine->RegisterObjectMethod("String", "uint FindLast(uint8, uint start = 0xffffffff, bool caseSensitive = true) const", asMETHODPR(String, FindLast, (char, unsigned, bool) const, unsigned), asCALL_THISCALL);
  1380. engine->RegisterObjectMethod("String", "bool StartsWith(const String&in, bool caseSensitive = true) const", asMETHOD(String, StartsWith), asCALL_THISCALL);
  1381. engine->RegisterObjectMethod("String", "bool EndsWith(const String&in, bool caseSensitive = true) const", asMETHOD(String, EndsWith), asCALL_THISCALL);
  1382. engine->RegisterObjectMethod("String", "String Substring(uint) const", asMETHODPR(String, Substring, (unsigned) const, String), asCALL_THISCALL);
  1383. engine->RegisterObjectMethod("String", "String Substring(uint, uint) const", asMETHODPR(String, Substring, (unsigned, unsigned) const, String), asCALL_THISCALL);
  1384. engine->RegisterObjectMethod("String", "String ToUpper() const", asMETHOD(String, ToUpper), asCALL_THISCALL);
  1385. engine->RegisterObjectMethod("String", "String ToLower() const", asMETHOD(String, ToLower), asCALL_THISCALL);
  1386. engine->RegisterObjectMethod("String", "String Trimmed() const", asMETHOD(String, Trimmed), asCALL_THISCALL);
  1387. engine->RegisterObjectMethod("String", "void SetUTF8FromLatin1(const String& in)", asFUNCTION(StringSetUTF8FromLatin1), asCALL_CDECL_OBJLAST);
  1388. engine->RegisterObjectMethod("String", "uint get_utf8Length() const", asMETHOD(String, LengthUTF8), asCALL_THISCALL);
  1389. engine->RegisterObjectMethod("String", "uint ByteOffsetUTF8(uint) const", asMETHOD(String, ByteOffsetUTF8), asCALL_THISCALL);
  1390. engine->RegisterObjectMethod("String", "uint NextUTF8Char(uint&) const", asMETHOD(String, NextUTF8Char), asCALL_THISCALL);
  1391. engine->RegisterObjectMethod("String", "uint AtUTF8(uint) const", asMETHOD(String, AtUTF8), asCALL_THISCALL);
  1392. engine->RegisterObjectMethod("String", "void ReplaceUTF8(uint, uint)", asMETHOD(String, ReplaceUTF8), asCALL_THISCALL);
  1393. engine->RegisterObjectMethod("String", "void AppendUTF8(uint)", asMETHOD(String, AppendUTF8), asCALL_THISCALL);
  1394. engine->RegisterObjectMethod("String", "String SubstringUTF8(uint) const", asMETHODPR(String, SubstringUTF8, (unsigned) const, String), asCALL_THISCALL);
  1395. engine->RegisterObjectMethod("String", "String SubstringUTF8(uint, uint) const", asMETHODPR(String, SubstringUTF8, (unsigned, unsigned) const, String), asCALL_THISCALL);
  1396. engine->RegisterObjectMethod("String", "uint get_length() const", asMETHOD(String, Length), asCALL_THISCALL);
  1397. engine->RegisterObjectMethod("String", "bool get_empty() const", asMETHOD(String, Empty), asCALL_THISCALL);
  1398. engine->RegisterObjectMethod("String", "int Compare(const String&in, bool caseSensitive = true) const", asMETHODPR(String, Compare, (const String&, bool) const, int), asCALL_THISCALL);
  1399. engine->RegisterObjectMethod("String", "bool Contains(const String&in) const", asMETHODPR(String, Contains, (const String&) const, bool), asCALL_THISCALL);
  1400. engine->RegisterObjectMethod("String", "bool Contains(uint8) const", asMETHODPR(String, Contains, (char) const, bool), asCALL_THISCALL);
  1401. engine->RegisterObjectMethod("String", "void Clear()", asMETHOD(String, Clear), asCALL_THISCALL);
  1402. // Register automatic conversion functions for convenience
  1403. engine->RegisterObjectBehaviour("String", asBEHAVE_CONSTRUCT, "void f(int)", asFUNCTION(ConstructStringInt), asCALL_CDECL_OBJLAST);
  1404. engine->RegisterObjectBehaviour("String", asBEHAVE_CONSTRUCT, "void f(uint)", asFUNCTION(ConstructStringUInt), asCALL_CDECL_OBJLAST);
  1405. engine->RegisterObjectBehaviour("String", asBEHAVE_CONSTRUCT, "void f(float)", asFUNCTION(ConstructStringFloat), asCALL_CDECL_OBJLAST);
  1406. engine->RegisterObjectBehaviour("String", asBEHAVE_CONSTRUCT, "void f(bool)", asFUNCTION(ConstructStringBool), asCALL_CDECL_OBJLAST);
  1407. engine->RegisterObjectMethod("String", "String& opAssign(int)", asFUNCTION(StringAssignInt), asCALL_CDECL_OBJLAST);
  1408. engine->RegisterObjectMethod("String", "String& opAddAssign(int)", asFUNCTION(StringAddAssignInt), asCALL_CDECL_OBJLAST);
  1409. engine->RegisterObjectMethod("String", "String opAdd(int) const", asFUNCTION(StringAddInt), asCALL_CDECL_OBJLAST);
  1410. engine->RegisterObjectMethod("String", "String opAdd_r(int) const", asFUNCTION(StringAddIntReverse), asCALL_CDECL_OBJLAST);
  1411. engine->RegisterObjectMethod("String", "String& opAssign(uint)", asFUNCTION(StringAssignUInt), asCALL_CDECL_OBJLAST);
  1412. engine->RegisterObjectMethod("String", "String& opAddAssign(uint)", asFUNCTION(StringAddAssignUInt), asCALL_CDECL_OBJLAST);
  1413. engine->RegisterObjectMethod("String", "String opAdd(uint) const", asFUNCTION(StringAddUInt), asCALL_CDECL_OBJLAST);
  1414. engine->RegisterObjectMethod("String", "String opAdd_r(uint) const", asFUNCTION(StringAddUIntReverse), asCALL_CDECL_OBJLAST);
  1415. engine->RegisterObjectMethod("String", "String& opAssign(float)", asFUNCTION(StringAssignFloat), asCALL_CDECL_OBJLAST);
  1416. engine->RegisterObjectMethod("String", "String& opAddAssign(float)", asFUNCTION(StringAddAssignFloat), asCALL_CDECL_OBJLAST);
  1417. engine->RegisterObjectMethod("String", "String opAdd(float) const", asFUNCTION(StringAddFloat), asCALL_CDECL_OBJLAST);
  1418. engine->RegisterObjectMethod("String", "String opAdd_r(float) const", asFUNCTION(StringAddFloatReverse), asCALL_CDECL_OBJLAST);
  1419. engine->RegisterObjectMethod("String", "String& opAssign(bool)", asFUNCTION(StringAssignBool), asCALL_CDECL_OBJLAST);
  1420. engine->RegisterObjectMethod("String", "String& opAddAssign(bool)", asFUNCTION(StringAddAssignBool), asCALL_CDECL_OBJLAST);
  1421. engine->RegisterObjectMethod("String", "String opAdd(bool) const", asFUNCTION(StringAddBool), asCALL_CDECL_OBJLAST);
  1422. engine->RegisterObjectMethod("String", "String opAdd_r(bool) const", asFUNCTION(StringAddBoolReverse), asCALL_CDECL_OBJLAST);
  1423. }
  1424. }