source: cpp/gdk/extvalue.cpp @ 104

Last change on this file since 104 was 104, checked in by sz, 11 years ago

introducing object de/serialization - see serialtest.cpp
the core GDK classes can be now used in multiple threads (ifdef MULTITHREADED)

  • Property svn:eol-style set to native
File size: 12.5 KB
Line 
1// This file is a part of the Framsticks GDK library.
2// Copyright (C) 2002-2011  Szymon Ulatowski.  See LICENSE.txt for details.
3// Refer to http://www.framsticks.com/ for further information.
4
5#include "extvalue.h"
6#include "param.h"
7#include "sstringutils.h"
8#include <math.h>
9#include <ctype.h>
10#include "collectionobj.h"
11#include "3dobject.h"
12
13SString ExtObject::toString() const
14{
15if (isEmpty()) return SString("<empty object>");
16Param tmp_param;
17ParamInterface *p=getParamInterface(tmp_param);
18int tostr=p->findId("toString");
19if (tostr>=0)
20        {
21        return SString(p->getString(tostr));
22        }
23else
24        {
25        SString tmp("<");
26        tmp+=p->getName();
27        sprintf(tmp.directAppend(30)," object at %p>",
28                (object?object:paraminterface));
29        tmp.endAppend();
30        return tmp;
31        }
32}
33
34ThreadSingleton<ExtObject::Serialization> ExtObject::serialization;
35
36void ExtObject::Serialization::begin()
37{
38if (level==0)
39        refs.clear();
40level++;
41}
42
43int ExtObject::Serialization::add(const ExtObject &o)
44{
45if (o.isEmpty()) return -1;
46for(int i=0;i<refs.size();i++)
47        {
48        ExtObject& r=refs[i];
49        if (r==o) return i;
50        }
51refs.push_back(o);
52return -1;
53}
54
55void ExtObject::Serialization::replace(const ExtObject& o,const ExtObject& other)
56{
57if (o.isEmpty()) return;
58for(int i=0;i<refs.size();i++)
59        {
60        ExtObject& r=refs[i];
61        if (r==o)
62                {
63                r=other;
64                return;
65                }
66        }
67}
68
69void ExtObject::Serialization::remove(const ExtObject& o)
70{
71if (o.isEmpty()) return;
72for(int i=0;i<refs.size();i++)
73        {
74        ExtObject& r=refs[i];
75        if (o==r) refs.erase(refs.begin()+i);
76        }
77}
78
79const ExtObject* ExtObject::Serialization::get(int ref)
80{
81if (ref<0) return NULL;
82if (ref>=refs.size()) return NULL;
83return &refs[ref];
84}
85
86void ExtObject::Serialization::end()
87{
88level--;
89if (level==0)
90        refs.clear();
91}
92
93SString ExtObject::serialize_inner() const
94{
95int ref=serialization.getref().add(*this);
96if (ref>=0)
97        {
98        SString ret;
99        sprintf(ret.directAppend(20),"^%d",ref);ret.endAppend();
100        return ret;
101        }
102
103if (isEmpty()) return SString();
104VectorObject *vec=VectorObject::fromObject(*this);
105if (vec)
106        return vec->serialize();
107DictionaryObject *dic=DictionaryObject::fromObject(*this);
108if (dic)
109        return dic->serialize();
110Param tmp_param;
111ParamInterface *p=getParamInterface(tmp_param);
112int m=p->findId("toVector");
113if (m<0)
114        m=p->findId("toDictionary");
115if (m>=0)
116        {
117        ExtObject o(p->getObject(m));
118        SString ret=SString(interfaceName())+o.serialize();
119        return ret;
120        }
121m=p->findId("toString");
122if (m>=0)
123        {
124        SString str=p->getString(m);
125        sstringQuote(str);
126        SString ret=SString(interfaceName())+"\""+str+"\"";
127        return ret;
128        }
129
130SString ret=interfaceName();
131sprintf(ret.directAppend(30),"<%p>",object?object:paraminterface);ret.endAppend();
132return ret;
133}
134
135SString ExtObject::serialize() const
136{
137serialization.getref().begin();
138SString ret=serialize_inner();
139serialization.getref().end();
140return ret;
141}
142
143///////////////////////////////////////
144
145void ExtValue::set(const ExtValue& src)
146{
147switch(src.type)
148        {
149        case TString: sets(src.sdata()); break;
150        case TInt: seti(src.idata()); break;
151        case TDouble: setd(src.ddata()); break;
152        case TObj: seto(src.odata()); break;
153        case TInvalid: type=TInvalid; break;
154        }
155}
156
157void ExtValue::setEmpty()
158{
159switch(type)
160        {
161#ifdef EXTVALUEUNION
162        case TString: sdata().~SString(); break;
163        case TObj: odata().~ExtObject(); break;
164#else
165        case TString: delete s; break;
166        case TObj: delete o; break;
167#endif
168        }
169type=TUnknown;
170}
171
172static long longsign(long x)
173{
174if (x<0) return -1;
175if (x>0) return 1;
176return 0;
177}
178
179long ExtValue::compare(const ExtValue& src) const
180{
181if (type==TUnknown)
182        {
183        if (src.type==TDouble)
184                return (src.getDouble()!=0.0);
185        if (src.type==TString)
186                return 1;
187        return src.getInt()?1:0;
188        }
189else if (src.type==TUnknown)
190        {
191        if (type==TDouble)
192                return (getDouble()!=0.0);
193        if (type==TString)
194                return 1;
195        return getInt()?1:0;
196        }
197switch(type)
198        {
199        case TInt:
200        {
201        long t=src.getInt();
202        if (idata()>0)
203                {if (t>0) return longsign(idata()-t); else return +1;}
204        else
205                {if (t<=0) return longsign(idata()-t); else return -1;}
206        }
207        case TDouble:
208                {
209                double t=ddata()-src.getDouble();
210                if (t<0) return -1;
211                else if (t>0) return 1;
212                return 0;
213                }
214        case TString:
215        {
216        SString t=src.getString();
217        SString& t2=sdata();
218        const char* s1=(const char*)t2;
219        const char* s2=(const char*)t;
220        return longsign(strcmp(s1,s2));
221        }
222        case TObj:
223        {
224        if (src.type==TObj)
225                return !(odata()==src.odata());
226        return 1;
227        }
228        }
229return 1;
230}
231
232int ExtValue::operator==(const ExtValue& src) const
233{
234if (type!=src.type) return 0;
235switch(type)
236        {
237        case TInt: return idata()==src.idata();
238        case TDouble: return ddata()==src.ddata();
239        case TString: return sdata()==src.sdata();
240        case TObj: return odata()==src.odata();
241        }
242return 1;
243}
244
245void ExtValue::operator+=(const ExtValue& src)
246{
247switch(type)
248        {
249        case TInt: idata()+=src.getInt(); break;
250        case TDouble: ddata()+=src.getDouble(); break;
251        case TString: sdata()+=src.getString(); break;
252        }
253}
254
255void ExtValue::operator-=(const ExtValue& src)
256{
257switch(type)
258        {
259        case TInt: idata()-=src.getInt(); break;
260        case TDouble: ddata()-=src.getDouble(); break;
261        }
262}
263
264void ExtValue::operator*=(const ExtValue& src)
265{
266switch(type)
267        {
268        case TInt: idata()*=src.getInt(); break;
269        case TDouble: ddata()*=src.getDouble(); break;
270        }
271}
272
273#include "framsg.h"
274/*#include "fpu_control.h"
275#include <signal.h>
276
277static int fpuexception;
278void mathhandler(int sig)
279{
280printf("fpu exception!\n");
281fpuexception=1;
282signal(SIGFPE,SIG_IGN);
283} */
284
285void ExtValue::operator/=(const ExtValue& src)
286{
287switch(type)
288        {
289        case TInt:
290        { int a=src.getInt();
291//              idata()/=src.getInt();
292        if (a) idata()/=a;
293        else FMprintf("ExtValue","divide",FMLV_ERROR,"%s/0",(const char*)getString());
294        }
295                break;
296
297        case TDouble:
298// ugly ;-(
299#ifdef FPU_THROWS_EXCEPTIONS
300                try
301                        {
302                        ddata()/=src.getDouble();
303                        }
304                catch(...)
305                        {
306                        FMprintf("ExtValue","divide",FMLV_ERROR,"%s/0.0",(const char*)getString());
307                        }
308#else
309                {
310                double d=ddata();
311                d/=src.getDouble();
312#ifdef IPHONE
313        if (!isinf(d))
314#else
315                if (finite(d))
316#endif
317                        ddata()=d;
318                else
319                        FMprintf("ExtValue","divide",FMLV_ERROR,"%s/0.0",(const char*)getString());
320                }
321#endif
322                break;
323        }
324}
325
326void ExtValue::operator%=(const ExtValue& src)
327{
328switch(type)
329        {
330        case TInt: idata()=idata()%src.getInt(); break;
331        case TDouble: ddata()=fmod(ddata(),src.getDouble()); break;
332        }
333}
334
335long ExtValue::getInt() const
336{
337switch(type)
338        {
339        case TInt: return idata();
340        case TDouble: return (int)ddata();
341        case TString:
342        {
343        const char* s=(const char*)sdata();
344        if ((s[0]=='0')&&(s[1]=='x'))
345                {
346                long val;
347                sscanf(s+2,"%lx",&val);
348                return val;
349                }
350        else
351                {
352                if (strchr(s,'e')||(strchr(s,'E')))
353                        return atof(s);
354                else
355                        return atol(s);
356                }
357        }
358        case TObj: return (long)odata().param;
359        }
360return 0;
361}
362double ExtValue::getDouble() const
363{
364switch(type)
365        {
366        case TDouble: return ddata();
367        case TInt: return (double)idata();
368        case TString:
369        {
370        const char* s=(const char*)sdata();
371        if ((s[0]=='0')&&(s[1]=='x'))
372                {
373                long val;
374                sscanf(s+2,"%lx",&val);
375                return val;
376                }
377        else
378                return atof(s);
379        }
380        case TObj: return (double)(long)odata().param;
381        }
382return 0.0;
383}
384SString ExtValue::getString() const
385{
386switch(type)
387        {
388        case TString: return sdata();
389        case TInt:
390                {
391                SString tmp;
392                sprintf(tmp.directAppend(20),"%d",idata());
393                tmp.endAppend();
394                return tmp;
395                }
396        case TDouble:
397                {
398                SString tmp;
399                sprintf(tmp.directAppend(20),"%.15g",ddata());
400                tmp.endAppend();
401                if ((!strchr(tmp,'.'))&&(!strchr(tmp,'e'))) tmp+=".0";
402                return tmp;
403                }
404        case TObj:
405                return odata().toString();
406        case TInvalid:
407                return SString("undefined");
408        default:
409                return SString("null");
410        }
411}
412
413SString ExtValue::serialize() const
414{
415switch(type)
416        {
417        case TString:
418                {
419                SString q=sdata();
420                sstringQuote(q);
421                return SString("\"")+q+SString("\"");
422                }
423        case TInt:
424                {
425                SString tmp;
426                sprintf(tmp.directAppend(20),"%d",idata());
427                tmp.endAppend();
428                return tmp;
429                }
430        case TDouble:
431                {
432                SString tmp;
433                sprintf(tmp.directAppend(20),"%.15g",ddata());
434                tmp.endAppend();
435                if ((!strchr(tmp,'.'))&&(!strchr(tmp,'e'))) tmp+=".0";
436                return tmp;
437                }
438        case TObj:
439                return odata().serialize();
440        case TInvalid:
441                return SString("undefined");
442        default:
443                return SString("null");
444        }
445}
446
447//returns the first character after the parsed number or NULL if not a number
448const char* ExtValue::parseNumber(const char* in)
449{
450if (isdigit(*in)||((*in=='-')&&(isdigit(in[1]))))
451        {
452        const char* p=in;
453        if (*p=='-') p++;
454        while(isdigit(*p)) p++;
455        bool fp=false;
456        if ((*p=='.') && isdigit(p[1]))
457                {
458                p++;
459                while(isdigit(*p)) p++;
460                fp=true;
461                }
462        if (((*p=='e')||(*p=='E')) && (isdigit(p[1]) || (((p[1]=='-') || (p[1]=='+')) && isdigit(p[2]))))
463                {
464                p++;
465                if ((*p=='-')||(*p=='+')) p++;
466                while(isdigit(*p)) p++;
467                fp=true;
468                }
469
470        if (fp)
471                {
472                setDouble(atof(in));
473                return p;
474                }
475        else
476                {
477                setInt(atol(in));
478                return p;
479                }
480        }
481return NULL;
482}
483
484PtrListTempl<ParamInterface*> ExtValue::deserializable_classes;
485
486void ExtValue::initDeserializableClasses()
487{
488deserializable_classes+=&Pt3D_Ext::getStaticParam();
489deserializable_classes+=&Orient_Ext::getStaticParam();
490}
491
492ParamInterface *ExtValue::findDeserializableClass(const char* name)
493{
494FOREACH(ParamInterface*,cls,deserializable_classes)
495        if (!strcmp(cls->getName(),name))
496                return cls;
497return NULL;
498}
499
500static const char* skipWord(const char* in)
501{
502while(isalpha(*in))
503        in++;
504return in;
505}
506
507//returns the first character after the parsed portion or NULL if invalid format
508const char* ExtValue::deserialize_inner(const char* in)
509{
510const char* ret=parseNumber(in);
511if (ret)
512        return ret;
513else if (*in=='\"')
514        {
515        ret=skipQuoteString(in+1,NULL);
516        SString s(in+1,ret-(in+1));
517        sstringUnquote(s);
518        setString(s);
519        if (*ret=='\"')
520                return ret+1;
521        else
522                return NULL;
523        }
524else if (*in=='[')
525        {
526        VectorObject *vec=new VectorObject;
527        ExtObject o(&VectorObject::par,vec);
528        ExtObject::serialization.getref().add(o);
529        const char* p=in+1;
530        ExtValue tmp;
531        while(*p)
532                {
533                if (*p==']') {p++;break;}
534                ret=tmp.deserialize(p);
535                if (ret)
536                        {
537                        vec->data+=new ExtValue(tmp);
538                        p=ret;
539                        if (*p==',') p++;
540                        }
541                else
542                        {
543                        p=NULL;
544                        break;
545                        }
546                }
547        setObject(o);
548        return p;
549        }
550else if (*in=='{')
551        {
552        DictionaryObject *dic=new DictionaryObject;
553        ExtObject o(&DictionaryObject::par,dic);
554        ExtObject::serialization.getref().add(o);
555        const char* p=in+1;
556        ExtValue args[2]/*={value,key}*/, dummy_ret;
557        while(*p)
558                {
559                if (*p=='}') {p++;break;}
560                ret=args[1].deserialize(p);
561                if ((!ret)||(args[1].getType()!=TString)) {p=NULL;break;}
562                p=ret;
563                if (*p!=':') {p=NULL;break;}
564                p++;
565                ret=args[0].deserialize(p);
566                if (!ret) {p=NULL;break;}
567                p=ret;
568                dic->p_set(args,&dummy_ret);
569                if (*p==',') p++;
570                }
571        setObject(o);
572        return p;
573        }
574else if (!strncmp(in,"null",4))
575        {
576        setEmpty();
577        return in+4;
578        }
579else if (!strncmp(in,"undefined",9))
580        {
581        setInvalid();
582        return in+9;
583        }
584else if (*in=='<')
585        { //unserializable object
586        setInvalid();
587        while(*in)
588                if (*in=='>')
589                        return in+1;
590                else in++;
591        return in;
592        }
593else if (*in=='^')
594        {
595        in++;
596        ExtValue ref;
597        ret=ref.parseNumber(in);
598        if (ret && (ref.getType()==TInt))
599                {
600                const ExtObject* o=ExtObject::serialization.getref().get(ref.getInt());
601                if (o)
602                        {
603                        setObject(*o);
604                        return ret;
605                        }
606                }
607        return NULL;
608        }
609else if ((ret=skipWord(in))&&(ret!=in))
610        {
611        SString clsname(in,ret-in);
612        ExtValue tmp;
613        ret=tmp.deserialize(ret);
614        ParamInterface *cls=findDeserializableClass(clsname);
615        if (cls && (tmp.getType()!=TUnknown) && (tmp.getType()!=TInvalid))
616                {
617                VectorObject *vec=VectorObject::fromObject(tmp.getObject());
618                if (vec)
619                        {
620                        int m=cls->findId("newFromVector");
621                        if (m>=0)
622                                {
623                                cls->call(m,&tmp,this);
624                                ExtObject::serialization.getref().replace(tmp.getObject(),getObject());
625                                return ret;
626                                }
627                        }
628                DictionaryObject *dic=DictionaryObject::fromObject(tmp.getObject());
629                if (dic)
630                        {
631                        int m=cls->findId("newFromDictionary");
632                        if (m>=0)
633                                {
634                                cls->call(m,&tmp,this);
635                                ExtObject::serialization.getref().replace(tmp.getObject(),getObject());
636                                return ret;
637                                }
638                        }
639                if (tmp.getType()==TString)
640                        {
641                        int m=cls->findId("newFromString");
642                        if (m>=0)
643                                {
644                                cls->call(m,&tmp,this);
645                                ExtObject::serialization.getref().replace(tmp.getObject(),getObject());
646                                return ret;
647                                }
648                        }
649                ExtObject::serialization.getref().remove(tmp.getObject());
650                setEmpty();
651                }
652        FMprintf("ExtValue","deserialize",FMLV_WARN,"object of class \"%s\" could not be deserialized",(const char*)clsname);
653        return ret;
654        }
655return NULL;
656}
657
658const char* ExtValue::deserialize(const char* in)
659{
660ExtObject::serialization.getref().begin();
661const char* ret=deserialize_inner(in);
662ExtObject::serialization.getref().end();
663return ret;
664}
665
666ExtObject ExtValue::getObject() const
667{
668if (type==TObj) return odata();
669return ExtObject();
670}
671
672ExtValue ExtValue::getExtType()
673{
674if (getType()!=TObj) return ExtValue((long)getType());
675ExtObject& o=odata();
676return ExtValue(SString(o.isEmpty()?"":o.interfaceName()));
677}
Note: See TracBrowser for help on using the repository browser.