BGE performance, 4th round: logic
[blender.git] / source / gameengine / Expressions / ListValue.cpp
1 // ListValue.cpp: implementation of the CListValue class.
2 //
3 //////////////////////////////////////////////////////////////////////
4 /*
5  * Copyright (c) 1996-2000 Erwin Coumans <coockie@acm.org>
6  *
7  * Permission to use, copy, modify, distribute and sell this software
8  * and its documentation for any purpose is hereby granted without fee,
9  * provided that the above copyright notice appear in all copies and
10  * that both that copyright notice and this permission notice appear
11  * in supporting documentation.  Erwin Coumans makes no
12  * representations about the suitability of this software for any
13  * purpose.  It is provided "as is" without express or implied warranty.
14  *
15  */
16
17 #include "ListValue.h"
18 #include "StringValue.h"
19 #include "VoidValue.h"
20 #include <algorithm>
21 #include "BoolValue.h"
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #if  ((PY_MAJOR_VERSION == 2) &&(PY_MINOR_VERSION < 5))
28 #define Py_ssize_t int
29 #endif
30
31 Py_ssize_t listvalue_bufferlen(PyObject* self)
32 {
33         CListValue *list= static_cast<CListValue *>(BGE_PROXY_REF(self));
34         if (list==NULL)
35                 return 0;
36         
37         return (Py_ssize_t)list->GetCount();
38 }
39
40 PyObject* listvalue_buffer_item(PyObject* self, Py_ssize_t index)
41 {
42         CListValue *list= static_cast<CListValue *>(BGE_PROXY_REF(self));
43         CValue *cval;
44         
45         if (list==NULL) {
46                 PyErr_SetString(PyExc_SystemError, "val = CList[i], "BGE_PROXY_ERROR_MSG);
47                 return NULL;
48         }
49         
50         int count = list->GetCount();
51         
52         if (index < 0)
53                 index = count+index;
54         
55         if (index < 0 || index >= count) {
56                 PyErr_SetString(PyExc_IndexError, "CList[i]: Python ListIndex out of range in CValueList");
57                 return NULL;
58         }
59         
60         cval= list->GetValue(index);
61         
62         PyObject* pyobj = cval->ConvertValueToPython();
63         if (pyobj)
64                 return pyobj;
65         else
66                 return cval->GetProxy();
67 }
68
69 PyObject* listvalue_mapping_subscript(PyObject* self, PyObject* pyindex)
70 {
71         CListValue *list= static_cast<CListValue *>(BGE_PROXY_REF(self));
72         if (list==NULL) {
73                 PyErr_SetString(PyExc_SystemError, "value = CList[i], "BGE_PROXY_ERROR_MSG);
74                 return NULL;
75         }
76         
77         if (PyString_Check(pyindex))
78         {
79                 const char *index = PyString_AsString(pyindex);
80                 CValue *item = ((CListValue*) list)->FindValue(index);
81                 if (item)
82                 {
83                         item->Release(); /* FindValue() AddRef's */ 
84                         return item->GetProxy();
85                 }
86         }
87         else if (PyInt_Check(pyindex))
88         {
89                 int index = PyInt_AsLong(pyindex);
90                 return listvalue_buffer_item(self, index); /* wont add a ref */
91         }
92         
93         PyObject *pyindex_str = PyObject_Repr(pyindex); /* new ref */
94         PyErr_Format(PyExc_KeyError, "CList[key]: '%s' key not in list", PyString_AsString(pyindex_str));
95         Py_DECREF(pyindex_str);
96         return NULL;
97 }
98
99
100 /* just slice it into a python list... */
101 PyObject* listvalue_buffer_slice(PyObject* self,Py_ssize_t ilow, Py_ssize_t ihigh)
102 {
103         CListValue *list= static_cast<CListValue *>(BGE_PROXY_REF(self));
104         if (list==NULL) {
105                 PyErr_SetString(PyExc_SystemError, "val = CList[i:j], "BGE_PROXY_ERROR_MSG);
106                 return NULL;
107         }
108         
109         int i, j;
110         PyObject *newlist;
111
112         if (ilow < 0) ilow = 0;
113
114         int n = ((CListValue*) list)->GetCount();
115
116         if (ihigh >= n)
117                 ihigh = n;
118     if (ihigh < ilow)
119         ihigh = ilow;
120
121         newlist = PyList_New(ihigh - ilow);
122         if (!newlist)
123                 return NULL;
124
125         for (i = ilow, j = 0; i < ihigh; i++, j++)
126         {
127                 PyObject* pyobj = list->GetValue(i)->ConvertValueToPython();
128                 if (!pyobj)
129                         pyobj = list->GetValue(i)->GetProxy();
130                 PyList_SET_ITEM(newlist, i, pyobj);
131         }       
132         return newlist;
133 }
134
135
136 /* clist + list, return a list that python owns */
137 static PyObject *listvalue_buffer_concat(PyObject * self, PyObject * other)
138 {
139         CListValue *listval= static_cast<CListValue *>(BGE_PROXY_REF(self));
140         int i, numitems, numitems_orig;
141         
142         if (listval==NULL) {
143                 PyErr_SetString(PyExc_SystemError, "CList+other, "BGE_PROXY_ERROR_MSG);
144                 return NULL;
145         }
146         
147         numitems_orig= listval->GetCount();
148         
149         // for now, we support CListValue concatenated with items
150         // and CListValue concatenated to Python Lists
151         // and CListValue concatenated with another CListValue
152         
153         /* Shallow copy, dont use listval->GetReplica(), it will screw up with KX_GameObjects */
154         CListValue* listval_new = new CListValue();
155         
156         if (PyList_Check(other))
157         {
158                 CValue* listitemval;
159                 bool error = false;
160                 
161                 numitems = PyList_Size(other);
162                 
163                 /* copy the first part of the list */
164                 listval_new->Resize(numitems_orig + numitems);
165                 for (i=0;i<numitems_orig;i++)
166                         listval_new->SetValue(i, listval->GetValue(i)->AddRef());
167                 
168                 for (i=0;i<numitems;i++)
169                 {
170                         listitemval = listval->ConvertPythonToValue(PyList_GetItem(other,i), "cList + pyList: CListValue, ");
171                         
172                         if (listitemval) {
173                                 listval_new->SetValue(i+numitems_orig, listitemval);
174                         } else {
175                                 error= true;
176                                 break;
177                         }
178                 }
179                 
180                 if (error) {
181                         listval_new->Resize(numitems_orig+i); /* resize so we dont try release NULL pointers */
182                         listval_new->Release();
183                         return NULL; /* ConvertPythonToValue above sets the error */ 
184                 }
185         
186         }
187         else if (PyObject_TypeCheck(other, &CListValue::Type)) {
188                 // add items from otherlist to this list
189                 CListValue* otherval = static_cast<CListValue *>(BGE_PROXY_REF(other));
190                 if(otherval==NULL) {
191                         listval_new->Release();
192                         PyErr_SetString(PyExc_SystemError, "CList+other, "BGE_PROXY_ERROR_MSG);
193                         return NULL;
194                 }
195                 
196                 numitems = otherval->GetCount();
197                 
198                 /* copy the first part of the list */
199                 listval_new->Resize(numitems_orig + numitems); /* resize so we dont try release NULL pointers */
200                 for (i=0;i<numitems_orig;i++)
201                         listval_new->SetValue(i, listval->GetValue(i)->AddRef());
202                 
203                 /* now copy the other part of the list */
204                 for (i=0;i<numitems;i++)
205                         listval_new->SetValue(i+numitems_orig, otherval->GetValue(i)->AddRef());
206                 
207         }
208         return listval_new->NewProxy(true); /* python owns this list */
209 }
210
211
212
213 static  PySequenceMethods listvalue_as_sequence = {
214         listvalue_bufferlen,//(inquiry)buffer_length, /*sq_length*/
215         listvalue_buffer_concat, /*sq_concat*/
216         NULL, /*sq_repeat*/
217         listvalue_buffer_item, /*sq_item*/
218 #if (PY_VERSION_HEX >= 0x03000000) // TODO, slicing in py3?
219         NULL,
220         NULL,
221         NULL,
222 #else
223         listvalue_buffer_slice, /*sq_slice*/
224         NULL, /*sq_ass_item*/
225         NULL, /*sq_ass_slice*/
226 #endif
227 };
228
229
230
231 /* Is this one used ? */
232 static  PyMappingMethods instance_as_mapping = {
233         listvalue_bufferlen, /*mp_length*/
234         listvalue_mapping_subscript, /*mp_subscript*/
235         NULL /*mp_ass_subscript*/
236 };
237
238
239
240 PyTypeObject CListValue::Type = {
241 #if (PY_VERSION_HEX >= 0x02060000)
242         PyVarObject_HEAD_INIT(NULL, 0)
243 #else
244         /* python 2.5 and below */
245         PyObject_HEAD_INIT( NULL )  /* required py macro */
246         0,                              /*ob_size*/
247 #endif
248         "CListValue",                   /*tp_name*/
249         sizeof(PyObjectPlus_Proxy), /*tp_basicsize*/
250         0,                              /*tp_itemsize*/
251         /* methods */
252         py_base_dealloc,                        /*tp_dealloc*/
253         0,                              /*tp_print*/
254         0,                      /*tp_getattr*/
255         0,                      /*tp_setattr*/
256         0,                              /*tp_compare*/
257         py_base_repr,                           /*tp_repr*/
258         0,                              /*tp_as_number*/
259         &listvalue_as_sequence, /*tp_as_sequence*/
260         &instance_as_mapping,           /*tp_as_mapping*/
261         0,                              /*tp_hash*/
262         0,                              /*tp_call */
263         0,
264         py_base_getattro,
265         py_base_setattro,
266         0,0,0,0,0,0,0,0,0,
267         Methods
268 };
269
270
271
272 PyParentObject CListValue::Parents[] = {
273         &CListValue::Type,
274         &CValue::Type,
275                 NULL
276 };
277
278
279
280
281 PyMethodDef CListValue::Methods[] = {
282         {"append", (PyCFunction)CListValue::sPyappend,METH_O},
283         {"reverse", (PyCFunction)CListValue::sPyreverse,METH_NOARGS},
284         {"index", (PyCFunction)CListValue::sPyindex,METH_O},
285         {"count", (PyCFunction)CListValue::sPycount,METH_O},
286         {"from_id", (PyCFunction)CListValue::sPyfrom_id,METH_O},
287         
288         {NULL,NULL} //Sentinel
289 };
290
291 PyAttributeDef CListValue::Attributes[] = {
292         { NULL }        //Sentinel
293 };
294
295 PyObject* CListValue::py_getattro(PyObject* attr) {
296         py_getattro_up(CValue);
297 }
298
299 PyObject* CListValue::py_getattro_dict() {
300         py_getattro_dict_up(CValue);
301 }
302
303
304 //////////////////////////////////////////////////////////////////////
305 // Construction/Destruction
306 //////////////////////////////////////////////////////////////////////
307
308 CListValue::CListValue(PyTypeObject *T ) 
309 : CPropValue(T)
310 {
311         m_bReleaseContents=true;        
312 }
313
314
315
316 CListValue::~CListValue()
317 {
318
319         if (m_bReleaseContents) {
320                 for (unsigned int i=0;i<m_pValueArray.size();i++) {
321                         m_pValueArray[i]->Release();
322                 }
323         }
324 }
325
326
327 static STR_String gstrListRep=STR_String("List");
328
329 const STR_String & CListValue::GetText()
330 {
331         gstrListRep = "[";
332         STR_String commastr = "";
333
334         for (int i=0;i<GetCount();i++)
335         {
336                 gstrListRep += commastr;
337                 gstrListRep += GetValue(i)->GetText();
338                 commastr = ",";
339         }
340         gstrListRep += "]";
341
342         return gstrListRep;
343 }
344
345
346
347 CValue* CListValue::GetReplica() { 
348         CListValue* replica = new CListValue(*this);
349
350         replica->ProcessReplica();
351
352         replica->m_bReleaseContents=true; // for copy, complete array is copied for now...
353         // copy all values
354         int numelements = m_pValueArray.size();
355         unsigned int i=0;
356         replica->m_pValueArray.resize(numelements);
357         for (i=0;i<m_pValueArray.size();i++)
358                 replica->m_pValueArray[i] = m_pValueArray[i]->GetReplica();
359
360
361         return replica;
362 };
363
364
365
366 void CListValue::SetValue(int i, CValue *val)
367 {
368         assertd(i < m_pValueArray.size());
369         m_pValueArray[i]=val;
370 }
371
372
373
374 void CListValue::Resize(int num)
375 {
376         m_pValueArray.resize(num);
377 }
378
379
380
381 void CListValue::Remove(int i)
382 {
383         assertd(i<m_pValueArray.size());
384         m_pValueArray.erase(m_pValueArray.begin()+i);
385 }
386
387
388
389 void CListValue::ReleaseAndRemoveAll()
390 {
391         for (unsigned int i=0;i<m_pValueArray.size();i++)
392                 m_pValueArray[i]->Release();
393         m_pValueArray.clear();//.Clear();
394 }
395
396
397
398 CValue* CListValue::FindValue(const char * name)
399 {
400         CValue* resultval = NULL;
401         int i=0;
402         
403         while (!resultval && i < GetCount())
404         {
405                 CValue* myval = GetValue(i);
406                                 
407                 if (myval->GetName() == name)
408                         resultval = GetValue(i)->AddRef(); // add referencecount
409                 else
410                         i++;
411                 
412         }
413         return resultval;
414 }
415
416
417
418 bool CListValue::SearchValue(CValue *val)
419 {
420         for (int i=0;i<GetCount();i++)
421                 if (val == GetValue(i))
422                         return true;
423         return false;
424 }
425
426
427
428 void CListValue::SetReleaseOnDestruct(bool bReleaseContents)
429 {
430         m_bReleaseContents = bReleaseContents;
431 }
432
433
434
435 bool CListValue::RemoveValue(CValue *val)
436 {
437         bool result=false;
438
439         for (int i=GetCount()-1;i>=0;i--)
440                 if (val == GetValue(i))
441                 {
442                         Remove(i);
443                         result=true;
444                 }
445         return result;
446 }
447
448
449
450 void CListValue::MergeList(CListValue *otherlist)
451 {
452
453         int numelements = this->GetCount();
454         int numotherelements = otherlist->GetCount();
455
456
457         Resize(numelements+numotherelements);
458
459         for (int i=0;i<numotherelements;i++)
460         {
461                 SetValue(i+numelements,otherlist->GetValue(i)->AddRef());
462         }
463 }
464
465
466 PyObject* CListValue::Pyappend(PyObject* value)
467 {
468         CValue* objval = ConvertPythonToValue(value, "CList.append(i): CValueList, ");
469
470         if (!objval) /* ConvertPythonToValue sets the error */
471                 return NULL;
472         
473         if (!BGE_PROXY_PYOWNS(m_proxy)) {
474                 PyErr_SetString(PyExc_TypeError, "CList.append(i): this CValueList is used internally for the game engine and can't be modified");
475                 return NULL;
476         }
477         
478         Add(objval);
479         
480         Py_RETURN_NONE;
481 }
482
483
484
485 PyObject* CListValue::Pyreverse()
486 {
487         std::reverse(m_pValueArray.begin(),m_pValueArray.end());
488         Py_RETURN_NONE;
489 }
490
491
492
493 bool CListValue::CheckEqual(CValue* first,CValue* second)
494 {
495         bool result = false;
496
497         CValue* eqval =  ((CValue*)first)->Calc(VALUE_EQL_OPERATOR,(CValue*)second);
498         
499         if (eqval==NULL)
500                 return false;
501         const STR_String& text = eqval->GetText();
502         if (&text==&CBoolValue::sTrueString)
503         {
504                 result = true;
505         }
506         eqval->Release();
507         return result;
508
509 }
510
511
512
513 PyObject* CListValue::Pyindex(PyObject *value)
514 {
515         PyObject* result = NULL;
516
517         CValue* checkobj = ConvertPythonToValue(value, "val = cList[i]: CValueList, ");
518         if (checkobj==NULL)
519                 return NULL; /* ConvertPythonToValue sets the error */
520
521         int numelem = GetCount();
522         for (int i=0;i<numelem;i++)
523         {
524                 CValue* elem =                  GetValue(i);
525                 if (CheckEqual(checkobj,elem))
526                 {
527                         result = PyInt_FromLong(i);
528                         break;
529                 }
530         }
531         checkobj->Release();
532
533         if (result==NULL) {
534                 PyErr_SetString(PyExc_ValueError, "CList.index(x): x not in CListValue");
535         }
536         return result;
537         
538 }
539
540
541
542 PyObject* CListValue::Pycount(PyObject* value)
543 {
544         int numfound = 0;
545
546         CValue* checkobj = ConvertPythonToValue(value, ""); /* error ignored */
547         
548         if (checkobj==NULL) { /* in this case just return that there are no items in the list */
549                 PyErr_Clear();
550                 return PyInt_FromLong(0);
551         }
552
553         int numelem = GetCount();
554         for (int i=0;i<numelem;i++)
555         {
556                 CValue* elem =                  GetValue(i);
557                 if (CheckEqual(checkobj,elem))
558                 {
559                         numfound ++;
560                 }
561         }
562         checkobj->Release();
563
564         return PyInt_FromLong(numfound);
565 }
566
567
568
569 PyObject* CListValue::Pyfrom_id(PyObject* value)
570 {
571         uintptr_t id= (uintptr_t)PyLong_AsVoidPtr(value);
572         
573         if (PyErr_Occurred())
574                 return NULL;
575
576         int numelem = GetCount();
577         for (int i=0;i<numelem;i++)
578         {
579                 if (reinterpret_cast<uintptr_t>(m_pValueArray[i]->m_proxy) == id)
580                         return GetValue(i)->GetProxy();
581         }
582         PyErr_SetString(PyExc_IndexError, "from_id(#): id not found in CValueList");
583         return NULL;    
584
585 }
586
587
588 /* --------------------------------------------------------------------- 
589  * Some stuff taken from the header
590  * --------------------------------------------------------------------- */
591 CValue* CListValue::Calc(VALUE_OPERATOR op,CValue *val) 
592 {
593         //assert(false); // todo: implement me!
594         static int error_printed =  0;
595         if (error_printed==0) {
596                 fprintf(stderr, "CValueList::Calc not yet implimented\n");
597                 error_printed = 1;
598         }
599         return NULL;
600 }
601
602 CValue* CListValue::CalcFinal(VALUE_DATA_TYPE dtype,
603                                                           VALUE_OPERATOR op, 
604                                                           CValue* val) 
605 {
606         //assert(false); // todo: implement me!
607         static int error_printed =  0;
608         if (error_printed==0) {
609                 fprintf(stderr, "CValueList::CalcFinal not yet implimented\n");
610                 error_printed = 1;
611         }
612         return NULL;
613 }
614
615
616
617 void CListValue::Add(CValue* value)
618 {
619         m_pValueArray.push_back(value);
620 }
621
622
623
624 double CListValue::GetNumber()
625 {
626         return -1;
627 }
628
629
630
631 void CListValue::SetModified(bool bModified)
632 {       
633         CValue::SetModified(bModified);
634         int numels = GetCount();
635
636         for (int i=0;i<numels;i++)
637                 GetValue(i)->SetModified(bModified);
638 }
639
640
641
642 bool CListValue::IsModified()
643 {
644         bool bmod = CValue::IsModified(); //normal own flag
645         int numels = GetCount();
646
647         for (int i=0;i<numels;i++)
648                 bmod = bmod || GetValue(i)->IsModified();
649
650         return bmod;
651 }