source: cpp/frams/util/extvalue.cpp @ 333

Last change on this file since 333 was 333, checked in by Maciej Komosinski, 9 years ago
  • use source/code mapping for line number and file information in vm error messages
  • enum ExtValue::CompareResult? instead of int
  • Property svn:eol-style set to native
File size: 25.5 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2015  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5#include "extvalue.h"
6#include <frams/param/param.h>
7#include "sstringutils.h"
8#include <ctype.h>
9#include <frams/vm/classes/collectionobj.h>
10#include <frams/vm/classes/3dobject.h>
11#include <frams/vm/classes/genoobj.h>
12#include <common/nonstd_math.h>
13#include <common/Convert.h>
14#include <climits>
15#include <errno.h>
16
17#ifndef NO_BARRIER
18#include <frams/simul/barrier.h>
19#include <common/threads.h>
20#endif
21
22#ifdef MULTITHREADED
23#include <pthread.h>
24//this lock only protects against ref.counter corruption caused by concurrent reads.
25//read/write conficts and nonatomicity are handled by BarrierObject (at least in theory ;-))
26static pthread_mutex_t extobject_ref_lock=PTHREAD_MUTEX_INITIALIZER;
27#define REF_LOCK pthread_mutex_lock(&extobject_ref_lock)
28#define REF_UNLOCK pthread_mutex_unlock(&extobject_ref_lock)
29#else
30#define REF_LOCK
31#define REF_UNLOCK
32#endif
33
34void ExtObject::incref() const
35{
36if (subtype&1)
37        {
38        REF_LOCK;
39        dbobject->refcount++;
40        REF_UNLOCK;
41        }
42}
43
44void ExtObject::decref() const
45{
46if (subtype&1)
47        {
48        REF_LOCK;
49        bool destroy=!--dbobject->refcount;
50        REF_UNLOCK;
51        //another thread can now access the object while we are deleting it
52        //but this is not a bug since we only guarantee read/read safety
53        if (destroy) delete dbobject;
54        }
55}
56
57bool ExtObject::operator==(const ExtObject& src) const
58{
59if (object!=src.object) return false;
60const char* n1=interfaceName();
61const char* n2=src.interfaceName();
62return (n1==n2) || (strcmp(n1,n2)==0);
63}
64
65bool ExtObject::makeUnique()
66{
67if (!(subtype&1)) return false;
68if (dbobject->refcount==1) return false;
69VectorObject* v=VectorObject::fromObject(*this,false);
70if (v)
71        {
72        VectorObject* n=new VectorObject;
73        n->data.setSize(n->data.size());
74        for(int i=0;i<v->data.size();i++)
75                {
76                ExtValue *x=(ExtValue*)v->data(i);
77                n->data.set(i,x?new ExtValue(*x):NULL);
78                }
79        operator=(n->makeObject());
80        return true;
81        }
82return false;
83}
84
85void* ExtObject::getTarget(const char* classname, bool through_barrier, bool warn) const
86{
87if (!strcmp(interfaceName(),classname))
88        return getTarget();
89#ifndef NO_BARRIER
90if (through_barrier)
91        {
92        BarrierObject *bo=BarrierObject::fromObject(*this);
93        if (bo)
94                return bo->getSourceObject().getTarget(classname,true,warn);
95        }
96#endif
97
98if (warn)
99        {
100        FMprintf("ExtValue","getObjectTarget",FMLV_WARN,"%s object expected, %s found",classname,interfaceName());
101        }
102
103return NULL;
104}
105
106
107SString ExtObject::toString() const
108{
109if (isEmpty()) return SString("null");
110Param tmp_param;
111ParamInterface *p=getParamInterface(tmp_param);
112int tostr=p->findId("toString");
113if (tostr>=0)
114        {
115        return SString(p->getString(tostr));
116        }
117else
118        {
119        SString tmp("<");
120        tmp+=p->getName();
121        tmp+=SString::sprintf(" object at %p>",object?object:paraminterface);
122        return tmp;
123        }
124}
125
126THREAD_LOCAL_DEF(ExtObject::Serialization,ExtObject::serialization);
127
128void ExtObject::Serialization::begin()
129{
130if (level==0)
131        refs.clear();
132level++;
133}
134
135int ExtObject::Serialization::add(const ExtObject &o)
136{
137if (o.isEmpty()) return -1;
138for(int i=0;i<(int)refs.size();i++)
139        {
140        ExtObject& r=refs[i];
141        if (r==o) return i;
142        }
143refs.push_back(o);
144return -1;
145}
146
147void ExtObject::Serialization::replace(const ExtObject& o,const ExtObject& other)
148{
149if (o.isEmpty()) return;
150for(int i=0;i<(int)refs.size();i++)
151        {
152        ExtObject& r=refs[i];
153        if (r==o)
154                {
155                r=other;
156                return;
157                }
158        }
159}
160
161void ExtObject::Serialization::remove(const ExtObject& o)
162{
163if (o.isEmpty()) return;
164for(int i=0;i<(int)refs.size();i++)
165        {
166        ExtObject& r=refs[i];
167        if (o==r) refs.erase(refs.begin()+i);
168        }
169}
170
171const ExtObject* ExtObject::Serialization::get(int ref)
172{
173if (ref<0) return NULL;
174if (ref>=(int)refs.size()) return NULL;
175return &refs[ref];
176}
177
178void ExtObject::Serialization::end()
179{
180level--;
181if (level==0)
182        refs.clear();
183}
184
185SString ExtObject::serialize_inner() const
186{
187int ref=tlsGetRef(serialization).add(*this);
188if (ref>=0)
189        return SString::sprintf("^%d",ref);
190
191if (isEmpty()) return SString("null");
192VectorObject *vec=VectorObject::fromObject(*this,false);
193if (vec)
194        return vec->serialize();
195DictionaryObject *dic=DictionaryObject::fromObject(*this,false);
196if (dic)
197        return dic->serialize();
198Param tmp_param;
199ParamInterface *p=getParamInterface(tmp_param);
200int m=p->findId("toVector");
201if (m<0)
202        m=p->findId("toDictionary");
203if (m>=0)
204        {
205        ExtObject o(p->getObject(m));
206        SString ret=SString(interfaceName())+o.serialize();
207        return ret;
208        }
209m=p->findId("toString");
210if (m>=0)
211        {
212        SString str=p->getString(m);
213        sstringQuote(str);
214        SString ret=SString(interfaceName())+"\""+str+"\"";
215        return ret;
216        }
217
218tlsGetRef(serialization).remove(*this);//undo nonserializable reference
219SString ret=interfaceName();
220ret+=SString::sprintf("<%p>",object?object:paraminterface);
221return ret;
222}
223
224SString ExtObject::serialize() const
225{
226tlsGetRef(serialization).begin();
227SString ret=serialize_inner();
228tlsGetRef(serialization).end();
229return ret;
230}
231
232///////////////////////////////////////
233
234SString ExtValue::typeDescription() const
235{
236switch(type)
237        {
238        case TInt: return SString("integer value");
239        case TDouble: return SString("floating point value");
240        case TString: return SString("text string");
241        case TUnknown: return SString("null value");
242        case TInvalid: return SString("invalid value");
243        case TObj: return getObject().isEmpty()?SString("null"):(SString(getObject().interfaceName())+" object");
244        }
245return SString::empty();
246}
247
248void *ExtValue::getObjectTarget(const char* classname,bool warn) const
249{
250if (type!=TObj)
251        {
252        if (warn)
253                {
254                SString tmp=getString();
255                if (tmp.len()>30) tmp=tmp.substr(0,30)+"...";
256                if (type==TString) tmp=SString("\"")+tmp+SString("\"");
257                FMprintf("ExtValue","getObjectTarget",FMLV_WARN,"%s object expected, %s found",classname,(const char*)tmp);
258                }
259        return NULL;
260        }
261
262return getObject().getTarget(classname,true,warn);
263}
264
265void ExtValue::set(const ExtValue& src)
266{
267switch(src.type)
268        {
269        case TString: sets(src.sdata()); break;
270        case TInt: seti(src.idata()); break;
271        case TDouble: setd(src.ddata()); break;
272        case TObj: seto(src.odata()); break;
273        default:type=src.type; break;
274        }
275}
276
277void ExtValue::setEmpty()
278{
279switch(type)
280        {
281#ifdef EXTVALUEUNION
282        case TString: sdata().~SString(); break;
283        case TObj: odata().~ExtObject(); break;
284#else
285        case TString: delete s; break;
286        case TObj: delete o; break;
287#endif
288        default:;
289        }
290type=TUnknown;
291}
292
293static ExtValue::CompareResult longsign(paInt x)
294{
295if (x<0) return ExtValue::ResultLower;
296if (x>0) return ExtValue::ResultHigher;
297return ExtValue::ResultEqual;
298}
299
300static ExtValue::CompareResult compareNull(const ExtValue& v)
301{
302if (v.isNull()) return ExtValue::ResultEqualUnordered;
303if ((v.getType()==TInt)&&(v.getInt()==0)) return ExtValue::ResultUnequal_RelaxedEqual;
304return ExtValue::ResultUnequal_RelaxedUnequal; //comparing anything else with null is valid but null is neither higher nor lower than numbers or strings
305}
306
307static ExtValue::CompareResult compareFloat(double a,double b)
308{
309double t=a-b;
310if (t<0) return ExtValue::ResultLower;
311else if (t>0) return ExtValue::ResultHigher;
312return ExtValue::ResultEqual;
313}
314
315static ExtValue::CompareResult compareString(const SString &a,const SString &b)
316{
317const char* s1=(const char*)a;
318const char* s2=(const char*)b;
319return longsign(strcmp(s1,s2));
320}
321
322ExtValue::CompareResult ExtValue::compare(const ExtValue& src) const
323{
324if (isNull())
325        return compareNull(src);
326else if (src.isNull())
327        return compareNull(*this);
328switch(type)
329        {
330
331        case TInt:
332
333                if (src.getType()==TInt)
334                        {
335                        paInt t=src.getInt();
336                        if (idata()>0)
337                                {if (t>0) return longsign(idata()-t); else return ResultHigher;}
338                        else
339                                {if (t<=0) return longsign(idata()-t); else return ResultLower;}
340                        }
341                else if (src.getType()==TDouble)
342                        return compareFloat((double)idata(),src.getDouble());
343                else
344                        return ResultMismatch;//comparing numbers with other things is invalid
345                break;
346
347        case TDouble:
348                if ((src.getType()==TDouble)||(src.getType()==TInt))
349                        return compareFloat(getDouble(),src.getDouble());
350                else
351                        return ResultMismatch;
352                break;
353
354        case TString:
355                if (src.getType()==TString)
356                        return compareString(sdata(),src.getString());
357                else
358                        return ResultMismatch;
359                break;
360
361        case TObj:
362        {
363        if (src.type==TObj)
364                return odata()==src.odata() ? ResultEqualUnordered : ResultUnequal_RelaxedUnequal;
365        if ((src.type==TInt)&&(src.getInt()==0))
366                return ResultMismatch_RelaxedUnequal;
367        return ResultMismatch;
368        }
369        default:;
370        }
371return ResultMismatch;
372}
373
374ExtValue::CmpMessageHandler ExtValue::default_cmp_message;
375ExtValue::CmpContext ExtValue::default_cmp_context={NULL,NULL,&default_cmp_message};
376
377const char* ExtValue::cmp_op_names[]={"==","!=",">=","<=",">","<","~=","!~",NULL};
378
379void ExtValue::CmpMessageHandler::cmpMessage(SString& text)
380{
381FMprintf("ExtValue","interpretCompare",FMLV_ERROR,"%s",(const char*)text);
382}
383
384int ExtValue::interpretCompare(CmpOperator op,CompareResult result,CmpContext *context)
385{
386int err=ResultUnequal_RelaxedEqual;//error when ResultUnequal_RelaxedEqual or higher (not comparable)
387int ret=0;
388switch (op)
389        {
390        case CmpEQ: ret=(result==ResultEqual)||(result==ResultEqualUnordered); err=ResultMismatch_RelaxedUnequal; break;
391        case CmpNE: ret=!((result==ResultEqual)||(result==ResultEqualUnordered)); err=ResultMismatch_RelaxedUnequal; break;
392        case CmpGT: ret=(result==ResultHigher); err=ResultEqualUnordered; break;
393        case CmpGE: ret=(result==ResultEqual)||(result==ResultHigher); err=ResultEqualUnordered; break;
394        case CmpLT: ret=(result==ResultLower); err=ResultEqualUnordered; break;
395        case CmpLE: ret=(result==ResultEqual)||(result==ResultLower); err=ResultEqualUnordered; break;
396        case CmpREQ: ret=(result==ResultEqual)||(result==ResultEqualUnordered)||(result==ResultUnequal_RelaxedEqual); err=ResultMismatch; break;
397        case CmpRNE: ret=!((result==ResultEqual)||(result==ResultEqualUnordered)||(result==ResultUnequal_RelaxedEqual)); err=ResultMismatch; break;
398        default:;
399        }
400if (result>=err)
401        {
402        if (context)
403                {
404                SString msg="Type mismatch";
405                if (context->v1 && context->v2)
406                        {
407                        const char* opname=cmp_op_names[op-CmpFIRST];
408                        msg+=": ";
409                        if (context->v1->isNull())
410                                msg+="null ";
411                        else
412                                msg+=SString::sprintf("%s '%s' ",(const char*)context->v1->typeDescription(),(const char*)context->v1->getString());
413                        msg+=opname;
414                        if (context->v2->isNull())
415                                msg+=" null";
416                        else
417                                msg+=SString::sprintf(" %s '%s'",(const char*)context->v2->typeDescription(),(const char*)context->v2->getString());
418                        }
419                context->handler->cmpMessage(msg);
420                }
421        ret=-1;
422        }
423return ret;
424}
425
426int ExtValue::operator==(const ExtValue& src) const
427{
428if (type!=src.type) return 0;
429switch(type)
430        {
431        case TInt: return idata()==src.idata();
432        case TDouble: return ddata()==src.ddata();
433        case TString: return sdata()==src.sdata();
434        case TObj: return odata()==src.odata();
435        default:;
436        }
437return 1;
438}
439
440void ExtValue::operator+=(const ExtValue& src)
441{
442switch(type)
443        {
444        case TInt: idata()+=src.getInt(); break;
445        case TDouble: ddata()+=src.getDouble(); break;
446        case TString: sdata()+=src.getString(); break;
447        case TObj:
448                {
449                VectorObject *vec=VectorObject::fromObject(getObject(),false);
450                VectorObject *vec2=VectorObject::fromObject(src.getObject(),false);
451                if (vec && vec2)
452                        {
453                        for(int i=0;i<vec2->data.size();i++)
454                                {
455                                ExtValue *s=(ExtValue*)vec2->data(i);
456                                ExtValue *d=s?new ExtValue(*s):NULL;
457                                vec->data+=d;
458                                }
459                        return;
460                        }
461                }
462                //NO break;
463        case TUnknown:
464        case TInvalid:
465                FMprintf("ExtValue","operator+=",FMLV_WARN,"Can't add %s to %s",(const char*)src.typeDescription(),(const char*)typeDescription());
466        default:;
467        }
468}
469
470void ExtValue::operator-=(const ExtValue& src)
471{
472switch(type)
473        {
474        case TInt: idata()-=src.getInt(); break;
475        case TDouble: ddata()-=src.getDouble(); break;
476        case TString: case TObj: case TUnknown: case TInvalid:
477                FMprintf("ExtValue","operator-=",FMLV_WARN,"Can't subtract %s from %s",(const char*)src.typeDescription(),(const char*)typeDescription());
478                break;
479        default:;
480        }
481}
482
483void ExtValue::operator*=(const ExtValue& src)
484{
485switch(type)
486        {
487        case TInt: idata()*=src.getInt(); break;
488        case TDouble: ddata()*=src.getDouble(); break;
489        case TString:
490        {
491        SString t;
492        for(int n=src.getInt();n>0;n--)
493                t+=getString();
494        setString(t);
495        break;
496        }
497        case TObj:
498                {
499                VectorObject *vec=VectorObject::fromObject(getObject(),false);
500                if (vec)
501                        {
502                        int n=src.getInt();
503                        int orig_size=vec->data.size();
504                        if (n<=0)
505                                {vec->clear();return;}
506                        for(;n>1;n--)
507                                {
508                                for(int i=0;i<orig_size;i++)
509                                        {
510                                        ExtValue *s=(ExtValue*)vec->data(i);
511                                        ExtValue *d=s?new ExtValue(*s):NULL;
512                                        vec->data+=d;
513                                        }
514                                }
515                        return;
516                        }
517                }
518                //NO break;
519        case TUnknown: case TInvalid:
520                FMprintf("ExtValue","operator*=",FMLV_WARN,"Can't multiply %s by %s",(const char*)typeDescription(),(const char*)src.typeDescription());
521                break;
522        default:;
523        }
524}
525
526#include <common/framsg.h>
527/*#include "fpu_control.h"
528#include <signal.h>
529
530static int fpuexception;
531void mathhandler(int sig)
532{
533printf("fpu exception!\n");
534fpuexception=1;
535signal(SIGFPE,SIG_IGN);
536} */
537
538void ExtValue::operator/=(const ExtValue& src)
539{
540switch(type)
541        {
542        case TInt:
543        { int a=src.getInt();
544//              idata()/=src.getInt();
545        if (a) idata()/=a;
546        else {FMprintf("ExtValue","divide",FMLV_CRITICAL,"%d/0",idata()); setInvalid();}
547        }
548                break;
549
550        case TDouble:
551                {
552                double d=src.getDouble();
553                if (d==0.0)
554                        {
555                        FMprintf("ExtValue","divide",FMLV_CRITICAL,"%s/0.0",(const char*)getString());
556                        setInvalid();
557                        }
558                else
559                        {
560                        fpExceptDisable();
561                        double tmp=ddata()/d;
562                        if (!finite(tmp))
563                                { FMprintf("ExtValue","divide",FMLV_CRITICAL,"overflow %s/%s",(const char*)getString(),(const char*)src.getString()); setInvalid(); }
564                        else
565                                ddata()=tmp;
566                        // niby dobrze ale lepiej byloby to robic bardziej systematycznie a nie tylko w dzieleniu?
567                        //if (isnan(ddata())) //http://www.digitalmars.com/d/archives/c++/Traping_divide_by_zero_5728.html
568                        //        { FMprintf("ExtValue","divide",FMLV_ERROR,"not-a-number",(const char*)getString()); setInvalid(); }
569                        fpExceptEnable();
570                        }
571                }
572                break;
573
574        case TString: case TObj: case TUnknown: case TInvalid:
575                FMprintf("ExtValue","operator/=",FMLV_WARN,"Can't divide %s by %s",(const char*)typeDescription(),(const char*)src.typeDescription());
576                break;
577
578        default:;
579        }
580}
581
582SString ExtValue::format(SString& fmt,const ExtValue **values,int count)
583{
584SString ret;
585// "..........%.........%..........%........"
586//  ^_cur     ^_next
587//  ^^^^^^^^^^___sub
588//
589// "..........%.........%..........%........"
590//            ^-cur     ^-next
591//            ^^^^^^^^^^___sub
592const char* begin=(const char*)fmt, *end=begin+fmt.len(), *curr=begin;
593int type=0;
594
595class Args
596{
597const ExtValue **values;
598int count;
599int arg;
600public:
601Args(const ExtValue **v,int c):values(v),count(c),arg(0) {}
602bool finished() {return arg>=count;}
603const ExtValue *getNext() {const ExtValue *ret=NULL; if ((arg<count)&&values[arg]) ret=values[arg]; arg++; return ret;}
604};
605Args args(values,count);
606
607while(curr<end)
608        {
609        const char* next=strchr(curr,'%');
610        if (!next) next=end; else if ((next==curr)&&(curr>begin))
611                {next=strchr(next+1,'%'); if (!next) next=end;}
612        type=0;
613        if (curr>begin)
614                {
615                type=0;
616                for(const char* t=curr;t<next;t++)
617                        switch(*t)
618                                {
619                                case 'd': case 'x': case 'X': case 'u': case 'p': case 'c': type='d'; t=next; break;
620                                case 'f': case 'g': case 'e': type='f'; t=next; break;
621                                case 's': type='s'; t=next; break;
622                                case 't': case 'T': type=*t; t=next; break;
623                                case '%': if (t>begin) {type=*t; t=next;} break;
624                                }
625                }
626        if (curr>begin) curr--;
627        const ExtValue *a;
628        if (args.finished() && (type!=0) && (type!='%'))
629                {
630                ret+=fmt.substr((int)(curr-begin));
631                break;
632                }
633        SString sub=fmt.substr((int)(curr-begin),(int)(next-curr));
634        switch(type)
635                {
636                case 'd': a=args.getNext(); ret+=SString::sprintf((const char*)sub,a?a->getInt():0); break;
637                case 'f': a=args.getNext(); ret+=SString::sprintf((const char*)sub,a?a->getDouble():0); break;
638                case 's': {a=args.getNext(); SString tmp; if (a) tmp=a->getString(); ret+=SString::sprintf((const char*)sub,(const char*)tmp);} break;
639                case 't': case 'T':
640                        {
641                        a=args.getNext();
642                        time_t ti=a?a->getInt():0;
643                        struct tm tm=Convert::localtime(ti);
644                        SString timtxt;
645                        if (type=='T')
646                                timtxt=SString::sprintf("%04d-%02d-%02d %02d:%02d:%02d",1900+tm.tm_year,1+tm.tm_mon,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec);
647                        else
648                                timtxt=Convert::asctime(tm).c_str();
649                        ret+=timtxt;
650                        ret+=sub.substr(2);
651                        }
652                        break;
653                case '%': ret+='%'; ret+=sub.substr(2); break;
654                case 0: ret+=sub; break;
655                }
656        curr=next+1;
657        }
658return ret;
659}
660
661
662void ExtValue::operator%=(const ExtValue& src)
663{
664switch(type)
665        {
666        case TInt: idata()=idata()%src.getInt(); break;
667        case TDouble: ddata()=fmod(ddata(),src.getDouble()); break;
668
669        case TString:
670        {
671        VectorObject *vec=VectorObject::fromObject(src.getObject(),false);
672        if (vec)
673                sdata()=format(sdata(),(const ExtValue**)&vec->data.getref(0),vec->data.size());
674        else
675                {const ExtValue *ptr=&src; sdata()=ExtValue::format(sdata(),&ptr,1);}
676        }
677        break;
678
679        case TObj: case TUnknown: case TInvalid:
680                FMprintf("ExtValue","operator%=",FMLV_WARN,"Can't apply modulo to %s",(const char*)typeDescription());
681
682        default:;
683        }
684}
685
686bool ExtValue::parseInt(const char* s, paInt &result, bool strict, bool error)
687{
688        ExtValue tmp;
689        const char* after = tmp.parseNumber(s, strict ? TInt : TUnknown);
690        if ((after == NULL) || (after[0] != 0))
691                {
692                if (error)
693                        FMprintf("ExtValue", "parseInt", FMLV_ERROR, "Could not parse '%s'%s", s, strict?" (strict)":"");
694                return false;
695                }
696        result = tmp.getInt();
697        return true;
698}
699
700bool ExtValue::parseDouble(const char* s, double &result, bool error)
701{
702        ExtValue tmp;
703        const char* after = tmp.parseNumber(s, TDouble);
704        if ((after == NULL) || (after[0] != 0))
705                {
706                if (error)
707                        FMprintf("ExtValue", "parseDouble", FMLV_ERROR, "Could not parse '%s'", s);
708                return false;
709                }
710        result = tmp.getDouble();
711        return true;
712}
713
714paInt ExtValue::getInt(const char* s,bool strict)
715{
716        paInt result;
717        if (parseInt(s, result, strict, true))
718                return result;
719        return 0;
720}
721
722double ExtValue::getDouble(const char* s)
723{
724        double result;
725        if (parseDouble(s, result, true))
726                return result;
727        return 0;
728}
729
730paInt ExtValue::getInt() const
731{
732        switch (type)
733        {
734        case TInt: return idata();
735        case TDouble: return (int)ddata();
736        case TString: return getInt((const char*)sdata());
737        case TObj:
738                FMprintf("ExtValue", "getInt", FMLV_WARN, "Getting integer value from object reference (%s)", (const char*)getString());
739                return (paInt)(intptr_t)odata().param;
740        default:;
741        }
742        return 0;
743}
744
745double ExtValue::getDouble() const
746{
747        switch (type)
748        {
749        case TDouble: return ddata();
750        case TInt: return (double)idata();
751        case TString: return getDouble((const char*)sdata());
752        case TObj:
753                FMprintf("ExtValue", "getDouble", FMLV_WARN, "Getting floating point value from object reference (%s)", (const char*)getString());
754                return (double)(intptr_t)odata().param;
755        default:;
756        }
757        return 0.0;
758}
759
760SString ExtValue::getString() const
761{
762        switch (type)
763        {
764        case TString: return sdata();
765        case TInt: return SString::valueOf(idata());
766        case TDouble: return SString::valueOf(ddata());
767        case TObj: return odata().toString();
768        case TInvalid:  return SString("invalid");
769        default: return SString("null");
770        }
771}
772
773const SString* ExtValue::getStringPtr() const
774{
775        if (type == TString)
776                return &sdata();
777        return NULL;
778}
779
780SString ExtValue::serialize() const
781{
782switch(type)
783        {
784        case TString:
785                {
786                SString q=sdata();
787                sstringQuote(q);
788                return SString("\"")+q+SString("\"");
789                }
790        case TInt:
791                return SString::valueOf(idata());
792        case TDouble:
793                return SString::valueOf(ddata());
794        case TObj:
795                return odata().serialize();
796        case TInvalid:
797                return SString("invalid");
798        default:
799                return SString("null");
800        }
801}
802
803/// returns the first character after the parsed number, or NULL if not a number
804/// @param strict_type = restrict the allowed return value (TUnknown = unrestricted)
805const char* ExtValue::parseNumber(const char* in, ExtPType strict_type)
806{
807        char* after;
808        if (in == NULL) return NULL;
809        if (in[0] == 0) return NULL;
810        while (isspace(*in)) in++;
811        bool minus = (in[0] == '-');
812        bool plus = (in[0] == '+');
813        if (((in[0] == '0') && ((in[1] == 'x') || (in[1]=='X')))
814             || (((minus || plus) && (in[1] == '0') && ((in[2] == 'x') || (in[2] == 'X')))))
815        {
816                in += (minus || plus) ? 3 : 2;
817                if (isspace(*in)) return NULL;
818                errno = 0;
819                unsigned long intvalue = strtoul(in, &after, 16);
820                if ((after > in) && (errno == 0) && (intvalue <= 0xffffffff))
821                {
822                        if (strict_type == TDouble)
823                                setDouble(minus ? -(double)intvalue : (double)intvalue);
824                        else
825                                setInt(minus ? -intvalue : intvalue);
826                        return after;
827                }
828                else
829                        return NULL;
830        }
831
832        errno = 0;
833        double fpvalue = strtod(in, &after);
834        if ((after > in) && (errno == 0))
835        {
836                if (strict_type != TDouble)
837                {
838                        if ((memchr(in, '.', after - in) == NULL) && (memchr(in, 'e', after - in) == NULL) && (memchr(in, 'E', after - in) == NULL) // no "special" characters
839                                && (fpvalue == floor(fpvalue)) // value is integer
840                                && (fpvalue >= INT_MIN) && (fpvalue <= INT_MAX)) // within limits
841                        {
842                                setInt(fpvalue);
843                                return after;
844                        }
845                        else if (strict_type == TInt)
846                                return NULL;
847                }
848                setDouble(fpvalue);
849                return after;
850        }
851        return NULL;
852}
853
854PtrListTempl<ParamInterface*> &ExtValue::getDeserializableClasses()
855{
856static PtrListTempl<ParamInterface*> classes;
857return classes;
858}
859
860ParamInterface *ExtValue::findDeserializableClass(const char* name)
861{
862FOREACH(ParamInterface*,cls,getDeserializableClasses())
863        if (!strcmp(cls->getName(),name))
864                return cls;
865return NULL;
866}
867
868static const char* skipWord(const char* in)
869{
870while(isalpha(*in)||(*in=='_'))
871        in++;
872return in;
873}
874
875//returns the first character after the parsed portion or NULL if invalid format
876const char* ExtValue::deserialize_inner(const char* in)
877{
878const char* ret=parseNumber(in);
879if (ret)
880        return ret;
881else if (*in=='\"')
882        {
883        ret=skipQuoteString(in+1,NULL);
884        SString s(in+1,(int)(ret-(in+1)));
885        sstringUnquote(s);
886        setString(s);
887        if (*ret=='\"')
888                return ret+1;
889        else
890                {
891                FMprintf("ExtValue","deserialize",FMLV_ERROR,"Missing '\"' in string: '%s'",ret);
892                return NULL;
893                }
894        }
895else if (*in=='[')
896        {
897        VectorObject *vec=new VectorObject;
898        ExtObject o(&VectorObject::par,vec);
899        tlsGetRef(ExtObject::serialization).add(o);
900        const char* p=in+1;
901        ExtValue tmp;
902        while(*p)
903                {
904                if (*p==']') {p++;break;}
905                ret=tmp.deserialize(p);
906                if (ret)
907                        {
908                        vec->data+=new ExtValue(tmp);
909                        p=ret;
910                        if (*p==',') p++;
911                        else if (*p!=']')
912                                {
913                                FMprintf("ExtValue","deserialize",FMLV_ERROR,"Missing ',' in Vector: '%s'",p);
914                                return NULL;
915                                }
916                        }
917                else
918                        {
919                        p=NULL;
920                        break;
921                        }
922                }
923        setObject(o);
924        return p;
925        }
926else if (*in=='{')
927        {
928        DictionaryObject *dic=new DictionaryObject;
929        ExtObject o(&DictionaryObject::par,dic);
930        tlsGetRef(ExtObject::serialization).add(o);
931        const char* p=in+1;
932        ExtValue args[2]/*={value,key}*/, dummy_ret;
933        while(*p)
934                {
935                if (*p=='}') {p++;break;}
936                ret=args[1].deserialize(p);
937                if ((!ret)||(args[1].getType()!=TString)) {p=NULL;break;}
938                p=ret;
939                if (*p!=':') {FMprintf("ExtValue","deserialize",FMLV_ERROR,"Missing ':' in Dictionary: '%s'",p);p=NULL;break;}
940                p++;
941                ret=args[0].deserialize(p);
942                if (!ret) {p=NULL;break;}
943                p=ret;
944                dic->p_set(args,&dummy_ret);
945                if (*p==',') p++;
946                else if (*p!='}')
947                        {
948                        FMprintf("ExtValue","deserialize",FMLV_ERROR,"Missing ',' in Dictionary: '%s'",p);
949                        return NULL;
950                        }
951                }
952        setObject(o);
953        return p;
954        }
955else if (!strncmp(in,"null",4))
956        {
957        setEmpty();
958        return in+4;
959        }
960else if (!strncmp(in,"invalid",9))
961        {
962        setInvalid();
963        return in+9;
964        }
965else if (*in=='<')
966        { //unserializable object
967        setInvalid();
968        while(*in)
969                if (*in=='>')
970                        return in+1;
971                else in++;
972        return in;
973        }
974else if (*in=='^')
975        {
976        in++;
977        ExtValue ref;
978        ret=ref.parseNumber(in,TInt);
979        if (ret && (ref.getType()==TInt))
980                {
981                const ExtObject* o=tlsGetRef(ExtObject::serialization).get(ref.getInt());
982                if (o)
983                        {
984                        setObject(*o);
985                        return ret;
986                        }
987                }
988        FMprintf("ExtValue","deserialize",FMLV_ERROR,"Invalid reference: '%s'",in-1);
989        return NULL;
990        }
991else if ((ret=skipWord(in))&&(ret!=in))
992        {
993        SString clsname(in,(int)(ret-in));
994        ExtValue tmp;
995        ret=tmp.deserialize(ret);
996        ParamInterface *cls=findDeserializableClass(clsname);
997        if (cls && (tmp.getType()!=TUnknown) && (tmp.getType()!=TInvalid))
998                {
999                VectorObject *vec=VectorObject::fromObject(tmp.getObject(),false);
1000                if (vec)
1001                        {
1002                        int m=cls->findId("newFromVector");
1003                        if (m>=0)
1004                                {
1005                                cls->call(m,&tmp,this);
1006                                tlsGetRef(ExtObject::serialization).replace(tmp.getObject(),getObject());
1007                                return ret;
1008                                }
1009                        }
1010                DictionaryObject *dic=DictionaryObject::fromObject(tmp.getObject(),false);
1011                if (dic)
1012                        {
1013                        int m=cls->findId("newFromDictionary");
1014                        if (m>=0)
1015                                {
1016                                cls->call(m,&tmp,this);
1017                                tlsGetRef(ExtObject::serialization).replace(tmp.getObject(),getObject());
1018                                return ret;
1019                                }
1020                        }
1021                if (tmp.getType()==TString)
1022                        {
1023                        int m=cls->findId("newFromString");
1024                        if (m>=0)
1025                                {
1026                                cls->call(m,&tmp,this);
1027                                tlsGetRef(ExtObject::serialization).replace(tmp.getObject(),getObject());
1028                                return ret;
1029                                }
1030                        }
1031                tlsGetRef(ExtObject::serialization).remove(tmp.getObject());
1032                setEmpty();
1033                }
1034        setEmpty();
1035        FMprintf("ExtValue","deserialize",FMLV_WARN,"object of class \"%s\" could not be deserialized",(const char*)clsname);
1036        return ret;
1037        }
1038FMprintf("ExtValue","deserialize",FMLV_ERROR,"Bad syntax: '%s'",in);
1039setEmpty();
1040return NULL;
1041}
1042
1043const char* ExtValue::deserialize(const char* in)
1044{
1045tlsGetRef(ExtObject::serialization).begin();
1046const char* ret=deserialize_inner(in);
1047tlsGetRef(ExtObject::serialization).end();
1048return ret;
1049}
1050
1051ExtObject ExtValue::getObject() const
1052{
1053if (type==TObj) return odata();
1054return ExtObject();
1055}
1056
1057ExtValue ExtValue::getExtType()
1058{
1059static const char* typenames[]={"null","int","float","string","","invalid"};
1060if (getType()!=TObj)
1061        return ExtValue(typenames[(int)getType()]);
1062ExtObject& o=odata();
1063return ExtValue(SString(o.isEmpty()?"":o.interfaceName()));
1064}
1065
1066SString SString::valueOf(const ExtValue& v)
1067{
1068return v.getString();
1069}
1070SString SString::valueOf(const ExtObject& v)
1071{
1072return v.toString();
1073}
Note: See TracBrowser for help on using the repository browser.