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

Last change on this file since 507 was 490, checked in by Maciej Komosinski, 8 years ago

Introduced general-use ErrorObject?, fixed enumeration of mixed private/public property lists

  • Property svn:eol-style set to native
File size: 30.9 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{
36        if (subtype & 1)
37        {
38                REF_LOCK;
39                dbobject->refcount++;
40                REF_UNLOCK;
41        }
42}
43
44void ExtObject::decref() const
45{
46        if (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{
59        if (object != src.object) return false;
60        const char* n1 = interfaceName();
61        const char* n2 = src.interfaceName();
62        return (n1 == n2) || (strcmp(n1, n2) == 0);
63}
64
65bool ExtObject::makeUnique()
66{
67        if (!(subtype & 1)) return false;
68        if (dbobject->refcount == 1) return false;
69        VectorObject* v = VectorObject::fromObject(*this, false);
70        if (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        }
82        return false;
83}
84
85void* ExtObject::getTarget(const char* classname, bool through_barrier, bool warn) const
86{
87        if (!strcmp(interfaceName(), classname))
88                return getTarget();
89#ifndef NO_BARRIER
90        if (through_barrier)
91        {
92                BarrierObject *bo = BarrierObject::fromObject(*this);
93                if (bo)
94                        return bo->getSourceObject().getTarget(classname, true, warn);
95        }
96#endif
97
98        if (warn)
99        {
100                logPrintf("ExtValue", "getObjectTarget", LOG_WARN, "%s object expected, %s found", classname, interfaceName());
101        }
102
103        return NULL;
104}
105
106bool ExtObject::callDelegate(const char* delegate,ExtValue *args,ExtValue *ret)
107{
108Param tmp;
109ParamInterface *pi=getParamInterface(tmp);
110if (pi)
111        {
112        int f=pi->findId(delegate);
113        if (f>=0)
114                {
115                pi->call(f,args,ret);
116                return true;
117                }
118        }
119logPrintf("Genotype","get", LOG_ERROR, "Could not call delegate '%s.%s'", pi?pi->getName():"NULL",delegate);
120return false;
121}
122
123SString ExtObject::toString() const
124{
125        if (isEmpty()) return SString("null");
126        Param tmp_param;
127        ParamInterface *p = getParamInterface(tmp_param);
128        int tostr = p->findId("toString");
129        if (tostr >= 0)
130        {
131                return SString(p->getString(tostr));
132        }
133        else
134        {
135                SString tmp("<");
136                tmp += p->getName();
137                tmp += SString::sprintf(" object at %p>", object ? object : paraminterface);
138                return tmp;
139        }
140}
141
142THREAD_LOCAL_DEF(ExtObject::Serialization, ExtObject_serialization);
143
144void ExtObject::Serialization::begin()
145{
146        if (level == 0)
147                refs.clear();
148        level++;
149}
150
151int ExtObject::Serialization::add(const ExtObject &o)
152{
153        if (o.isEmpty()) return -1;
154        for (int i = 0; i < (int)refs.size(); i++)
155        {
156                ExtObject& r = refs[i];
157                if (r == o) return i;
158        }
159        refs.push_back(o);
160        return -1;
161}
162
163void ExtObject::Serialization::replace(const ExtObject& o, const ExtObject& other)
164{
165        if (o.isEmpty()) return;
166        for (int i = 0; i < (int)refs.size(); i++)
167        {
168                ExtObject& r = refs[i];
169                if (r == o)
170                {
171                        r = other;
172                        return;
173                }
174        }
175}
176
177void ExtObject::Serialization::remove(const ExtObject& o)
178{
179        if (o.isEmpty()) return;
180        for (int i = 0; i < (int)refs.size(); i++)
181        {
182                ExtObject& r = refs[i];
183                if (o == r) refs.erase(refs.begin() + i);
184        }
185}
186
187const ExtObject* ExtObject::Serialization::get(int ref)
188{
189        if (ref < 0) return NULL;
190        if (ref >= (int)refs.size()) return NULL;
191        return &refs[ref];
192}
193
194void ExtObject::Serialization::end()
195{
196        level--;
197        if (level == 0)
198                refs.clear();
199}
200
201SString ExtObject::serialize_inner(SerializationFormat format) const
202{
203        int ref = tlsGetRef(ExtObject_serialization).add(*this);
204        SString ret;
205
206        if (ref >= 0)
207                {
208                switch(format)
209                        {
210                        case NativeSerialization: return SString::sprintf("^%d", ref);
211                        case JSONSerialization: return SString("null");
212                        }
213                }
214
215        if (isEmpty()) return SString("null");
216        {
217        VectorObject *vec = VectorObject::fromObject(*this, false);
218        if (vec)
219                { ret=vec->serialize(format); goto finally; }
220        }
221        {
222        DictionaryObject *dic = DictionaryObject::fromObject(*this, false);
223        if (dic)
224                { ret=dic->serialize(format); goto finally; }
225        }
226        {
227        Param tmp_param;
228        ParamInterface *p = getParamInterface(tmp_param);
229        int m = p->findId("toVector");
230        if (m < 0)
231                m = p->findId("toDictionary");
232        if (m >= 0)
233        {
234                ExtObject o(p->getObject(m));
235                switch(format)
236                        {
237                        case NativeSerialization: ret=SString(interfaceName())+o.serialize(format); break;
238                        case JSONSerialization: ret=SString::sprintf("{\"class\":\"%s\",\"data\":%s}",interfaceName(),o.serialize(format).c_str()); break;
239                        }
240                goto finally;
241        }
242        m = p->findId("toString");
243        if (m >= 0)
244        {
245                SString str = p->getString(m);
246                sstringQuote(str);
247                switch(format)
248                        {
249                        case NativeSerialization: ret=SString(interfaceName())+"\"" +str+"\""; break;
250                        case JSONSerialization: ret=SString::sprintf("{\"class\":\"%s\",\"data\":\"%s\"}",interfaceName(),str.c_str()); break;
251                        }
252                goto finally;
253        }
254        }
255
256        tlsGetRef(ExtObject_serialization).remove(*this);//undo nonserializable reference
257        switch(format)
258                {
259                case NativeSerialization: return SString(interfaceName())+SString::sprintf("<%p>",object ? object : paraminterface); break;
260                case JSONSerialization: return SString::sprintf("{\"class\":\"%s\"}",interfaceName()); break;
261                }
262
263  finally: // not 100% "finally", the case of nonserializable reference (directly above) returns directly without going through finally
264
265        switch(format)
266                {
267                case JSONSerialization:
268                        tlsGetRef(ExtObject_serialization).remove(*this);//JSON only tracks recursion, does not track reuse
269                        break;
270                case NativeSerialization:; //nop (just to avoid compiler warning)
271                }
272
273        return ret;
274}
275
276SString ExtObject::serialize(SerializationFormat format) const
277{
278        tlsGetRef(ExtObject_serialization).begin();
279        SString ret = serialize_inner(format);
280        tlsGetRef(ExtObject_serialization).end();
281        return ret;
282}
283
284///////////////////////////////////////
285
286SString ExtValue::typeDescription() const
287{
288        switch (type)
289        {
290        case TInt: return SString("int");
291        case TDouble: return SString("float");
292        case TString: return SString("string");
293        case TUnknown: return SString("null");
294        case TInvalid: return SString("invalid");
295        case TObj: return getObject().isEmpty() ? SString("null") : SString(getObject().interfaceName());
296        }
297        return SString::empty();
298}
299
300SString ExtValue::typeAndValue() const
301{
302        SString msg = typeDescription();
303        switch (type)
304        {
305        case TInt: case TDouble: case TString: case TObj:
306                msg += " '";
307                msg += getString();
308                msg += "'";
309        default:;
310        }
311        return msg;
312}
313
314void *ExtValue::getObjectTarget(const char* classname, bool warn) const
315{
316        if (type != TObj)
317        {
318                if (warn)
319                {
320                        SString tmp = getString();
321                        if (tmp.len() > 30) tmp = tmp.substr(0, 30) + "...";
322                        if (type == TString) tmp = SString("\"") + tmp + SString("\"");
323                        logPrintf("ExtValue", "getObjectTarget", LOG_WARN, "%s object expected, %s found", classname, tmp.c_str());
324                }
325                return NULL;
326        }
327
328        return getObject().getTarget(classname, true, warn);
329}
330
331void ExtValue::set(const ExtValue& src)
332{
333        switch (src.type)
334        {
335        case TString: sets(src.sdata()); break;
336        case TInt: seti(src.idata()); break;
337        case TDouble: setd(src.ddata()); break;
338        case TObj: seto(src.odata()); break;
339        default:type = src.type; break;
340        }
341}
342
343void ExtValue::setEmpty()
344{
345        switch (type)
346        {
347#ifdef EXTVALUEUNION
348        case TString: sdata().~SString(); break;
349        case TObj: odata().~ExtObject(); break;
350#else
351        case TString: delete s; break;
352        case TObj: delete o; break;
353#endif
354        default:;
355        }
356        type = TUnknown;
357}
358
359void ExtValue::setError(const SString& msg)
360{
361ErrorObject *err=new ErrorObject;
362err->message=msg;
363setObject(ErrorObject::makeDynamicObject(err));
364}
365
366static ExtValue::CompareResult longsign(paInt x)
367{
368        if (x < 0) return ExtValue::ResultLower;
369        if (x > 0) return ExtValue::ResultHigher;
370        return ExtValue::ResultEqual;
371}
372
373static ExtValue::CompareResult compareNull(const ExtValue& v)
374{
375        if (v.isNull()) return ExtValue::ResultEqualUnordered;
376        if ((v.getType() == TInt) && (v.getInt() == 0)) return ExtValue::ResultUnequal_RelaxedEqual;
377        return ExtValue::ResultUnequal_RelaxedUnequal; //comparing anything else with null is valid but null is neither higher nor lower than numbers or strings
378}
379
380static ExtValue::CompareResult compareInvalid(const ExtValue& v)
381{
382        if (v.getType()==TInvalid) return ExtValue::ResultEqualUnordered;
383        if ((v.getType() == TInt) && (v.getInt() == 0)) return ExtValue::ResultUnequal_RelaxedEqual;
384        return ExtValue::ResultMismatch; //comparing anything else with invalid is invalid
385}
386
387static ExtValue::CompareResult compareFloat(double a, double b)
388{
389        double t = a - b;
390        if (t < 0) return ExtValue::ResultLower;
391        else if (t > 0) return ExtValue::ResultHigher;
392        return ExtValue::ResultEqual;
393}
394
395static ExtValue::CompareResult compareString(const SString &a, const SString &b)
396{
397        const char* s1 = a.c_str();
398        const char* s2 = b.c_str();
399        return longsign(strcmp(s1, s2));
400}
401
402ExtValue::CompareResult ExtValue::compare(const ExtValue& src) const
403{
404        if (isNull())
405                return compareNull(src);
406        else if (src.isNull())
407                return compareNull(*this);
408        if (getType()==TInvalid)
409                return compareInvalid(src);
410        else if (src.getType()==TInvalid)
411                return compareInvalid(*this);
412        switch (type)
413        {
414
415        case TInt:
416
417                if (src.getType() == TInt)
418                {
419                        paInt t = src.getInt();
420                        if (idata() > 0)
421                        {
422                                if (t > 0) return longsign(idata() - t); else return ResultHigher;
423                        }
424                        else
425                        {
426                                if (t <= 0) return longsign(idata() - t); else return ResultLower;
427                        }
428                }
429                else if (src.getType() == TDouble)
430                        return compareFloat((double)idata(), src.getDouble());
431                else
432                        return ResultMismatch;//comparing numbers with other things is invalid
433                break;
434
435        case TDouble:
436                if ((src.getType() == TDouble) || (src.getType() == TInt))
437                        return compareFloat(getDouble(), src.getDouble());
438                else
439                        return ResultMismatch;
440                break;
441
442        case TString:
443                if (src.getType() == TString)
444                        return compareString(sdata(), src.getString());
445                else
446                        return ResultMismatch;
447                break;
448
449        case TObj:
450        {
451                if (src.type == TObj)
452                        return odata() == src.odata() ? ResultEqualUnordered : ResultUnequal_RelaxedUnequal;
453                if ((src.type == TInt) && (src.getInt() == 0))
454                        return ResultMismatch_RelaxedUnequal;
455                return ResultMismatch;
456        }
457        default:;
458        }
459        return ResultMismatch;
460}
461
462const char* ExtValue::cmp_op_names[] = { "==", "!=", ">=", "<=", ">", "<", "~=", "!~", NULL };
463
464int ExtValue::interpretCompare(CmpOperator op, CompareResult result, CmpContext *context)
465{
466        CompareResult error_threshold = ResultUnequal_RelaxedEqual;//error when ResultUnequal_RelaxedEqual or higher (not comparable)
467        int ret = 0;
468        switch (op)
469        {
470        case CmpEQ: ret = (result == ResultEqual) || (result == ResultEqualUnordered); error_threshold = ResultMismatch_RelaxedUnequal; break;
471        case CmpNE: ret = !((result == ResultEqual) || (result == ResultEqualUnordered)); error_threshold = ResultMismatch_RelaxedUnequal; break;
472        case CmpGT: ret = (result == ResultHigher); error_threshold = ResultEqualUnordered; break;
473        case CmpGE: ret = (result == ResultEqual) || (result == ResultHigher); error_threshold = ResultEqualUnordered; break;
474        case CmpLT: ret = (result == ResultLower); error_threshold = ResultEqualUnordered; break;
475        case CmpLE: ret = (result == ResultEqual) || (result == ResultLower); error_threshold = ResultEqualUnordered; break;
476        case CmpREQ: ret = (result == ResultEqual) || (result == ResultEqualUnordered) || (result == ResultUnequal_RelaxedEqual); error_threshold = ResultMismatch; break;
477        case CmpRNE: ret = !((result == ResultEqual) || (result == ResultEqualUnordered) || (result == ResultUnequal_RelaxedEqual)); error_threshold = ResultMismatch; break;
478        default:;
479        }
480        if (result >= error_threshold)
481        {
482                SString msg = "Type mismatch while comparing";
483                if (context)
484                {
485                        if (context->v1 && context->v2)
486                                msg += SString::sprintf(": %s %s %s",
487                                context->v1->typeAndValue().c_str(),
488                                cmp_op_names[op - CmpFIRST],
489                                context->v2->typeAndValue().c_str());
490                }
491                logPrintf("ExtValue", "interpretCompare", LOG_ERROR, "%s", msg.c_str());
492                ret = -1;
493        }
494        return ret;
495}
496
497int ExtValue::operator==(const ExtValue& src) const
498{
499        if (type != src.type) return 0;
500        switch (type)
501        {
502        case TInt: return idata() == src.idata();
503        case TDouble: return ddata() == src.ddata();
504        case TString: return sdata() == src.sdata();
505        case TObj: return odata() == src.odata();
506        default:;
507        }
508        return 1;
509}
510
511void ExtValue::operator+=(const ExtValue& src)
512{
513        // return = ok, break = fail
514        switch (type)
515        {
516        case TInt:
517                switch (src.getType())
518                {
519                case TDouble:
520                        setDouble(double(getInt()) + src.getDouble());
521                        return;
522                case TString:
523                        break;
524                default:
525                        idata() += src.getInt();
526                        return;
527                }
528                break;
529        case TDouble:
530                switch (src.getType())
531                {
532                case TString:
533                        break;
534                default:
535                        ddata() += src.getDouble();
536                        return;
537                }
538                break;
539        case TString: sdata() += src.getString(); return;
540        case TObj:
541        {
542                VectorObject *vec = VectorObject::fromObject(getObject(), false);
543                VectorObject *vec2 = VectorObject::fromObject(src.getObject(), false);
544                if (vec && vec2)
545                {
546                        for (int i = 0; i < vec2->data.size(); i++)
547                        {
548                                ExtValue *s = (ExtValue*)vec2->data(i);
549                                ExtValue *d = s ? new ExtValue(*s) : NULL;
550                                vec->data += d;
551                        }
552                        return;
553                }
554        }
555                //NO break;
556        default:;
557        }
558        logPrintf("ExtValue", "add", LOG_ERROR, "Can't add %s to %s", src.typeAndValue().c_str(), typeAndValue().c_str());
559}
560
561void ExtValue::operator-=(const ExtValue& src)
562{
563        // return = ok, break = fail
564        switch (type)
565        {
566        case TInt:
567                switch (src.getType())
568                {
569                case TInt:
570                        idata() -= src.getInt();
571                        return;
572                case TDouble:
573                        setDouble(double(getInt()) - src.getDouble());
574                        return;
575                default:;
576                }
577                break;
578        case TDouble:
579                switch (src.getType())
580                {
581                case TDouble:
582                case TInt:
583                        ddata() -= src.getDouble();
584                        return;
585                default:;
586                }
587                break;
588        default:;
589        }
590        logPrintf("ExtValue", "subtract", LOG_ERROR, "Can't subtract %s from %s", src.typeAndValue().c_str(), typeAndValue().c_str());
591}
592
593void ExtValue::operator*=(const ExtValue& src)
594{
595        // return = ok, break = fail
596        switch (type)
597        {
598        case TInt:
599                switch (src.getType())
600                {
601                case TInt:
602                        idata() *= src.getInt();
603                        return;
604                case TDouble:
605                        setDouble(double(getInt())*src.getDouble());
606                        return;
607                default:;
608                }
609                break;
610        case TDouble:
611                switch (src.getType())
612                {
613                case TInt:
614                case TDouble:
615                        ddata() *= src.getDouble();
616                        return;
617                default:;
618                }
619                break;
620        case TString:
621                switch (src.getType())
622                {
623                case TInt: case TDouble:
624                {
625                        SString t;
626                        for (int n = src.getInt(); n > 0; n--)
627                                t += getString();
628                        setString(t);
629                        return;
630                }
631                default:;
632                }
633                break;
634        case TObj:
635        {
636                VectorObject *vec = VectorObject::fromObject(getObject(), false);
637                if (vec)
638                {
639                        int n = src.getInt();
640                        int orig_size = vec->data.size();
641                        if (n <= 0)
642                        {
643                                vec->clear(); return;
644                        }
645                        for (; n > 1; n--)
646                        {
647                                for (int i = 0; i < orig_size; i++)
648                                {
649                                        ExtValue *s = (ExtValue*)vec->data(i);
650                                        ExtValue *d = s ? new ExtValue(*s) : NULL;
651                                        vec->data += d;
652                                }
653                        }
654                        return;
655                }
656        }
657                //NO break;
658        default:;
659        }
660        logPrintf("ExtValue", "multiply", LOG_WARN, "Can't multiply %s by %s", typeAndValue().c_str(), src.typeAndValue().c_str());
661}
662
663/*#include "fpu_control.h"
664#include <signal.h>
665
666static int fpuexception;
667void mathhandler(int sig)
668{
669printf("fpu exception!\n");
670fpuexception=1;
671signal(SIGFPE,SIG_IGN);
672} */
673
674void ExtValue::divInt(paInt a)
675{
676        if (a)
677                idata() /= a;
678        else
679        {
680                logPrintf("ExtValue", "divide", LOG_ERROR, "Division by zero: %d/0", idata());
681                setInvalid();
682        }
683}
684
685void ExtValue::divDouble(double a)
686{
687        if (a == 0.0)
688        {
689                logPrintf("ExtValue", "divide", LOG_ERROR, "Division by zero: %s/0.0", getString().c_str());
690                setInvalid();
691        }
692        else
693        {
694                fpExceptDisable();
695                double tmp = getDouble() / a;
696                if (!finite(tmp))
697                {
698                        logPrintf("ExtValue", "divide", LOG_ERROR, "Overflow %s/%g", getString().c_str(), a); setInvalid();
699                }
700                else
701                        setDouble(tmp);
702                // niby dobrze ale lepiej byloby to robic bardziej systematycznie a nie tylko w dzieleniu?
703                //if (isnan(ddata())) //http://www.digitalmars.com/d/archives/c++/Traping_divide_by_zero_5728.html
704                //        { logPrintf("ExtValue","divide",LOG_ERROR,"not-a-number",(const char*)getString()); setInvalid(); }
705                fpExceptEnable();
706        }
707}
708
709void ExtValue::operator/=(const ExtValue& src)
710{
711        switch (type)
712        {
713        case TInt:
714                switch (src.getType())
715                {
716                case TInt:
717                        divInt(src.idata());
718                        return;
719                case TDouble:
720                        divDouble(src.ddata());
721                        return;
722                default:;
723                }
724                break;
725
726        case TDouble:
727                switch (src.getType())
728                {
729                case TInt:
730                        divDouble(src.getDouble());
731                        return;
732                case TDouble:
733                        divDouble(src.ddata());
734                        return;
735                default:;
736                }
737                break;
738
739        default:;
740        }
741        logPrintf("ExtValue", "divide", LOG_ERROR, "Can't divide %s by %s", typeAndValue().c_str(), src.typeAndValue().c_str());
742}
743
744SString ExtValue::format(SString& fmt, const ExtValue **values, int count)
745{
746        SString ret;
747        // "..........%.........%..........%........"
748        //  ^_cur     ^_next
749        //  ^^^^^^^^^^___sub
750        //
751        // "..........%.........%..........%........"
752        //            ^-cur     ^-next
753        //            ^^^^^^^^^^___sub
754        const char* begin = fmt.c_str(), *end = begin + fmt.len(), *curr = begin;
755        int type = 0;
756
757        class Args
758        {
759                const ExtValue **values;
760                int count;
761                int arg;
762        public:
763                Args(const ExtValue **v, int c) :values(v), count(c), arg(0) {}
764                bool finished() { return arg >= count; }
765                const ExtValue *getNext() { const ExtValue *ret = NULL; if ((arg < count) && values[arg]) ret = values[arg]; arg++; return ret; }
766        };
767        Args args(values, count);
768
769        while (curr < end)
770        {
771                const char* next = strchr(curr, '%');
772                if (!next) next = end; else if ((next == curr) && (curr > begin))
773                {
774                        next = strchr(next + 1, '%'); if (!next) next = end;
775                }
776                type = 0;
777                if (curr > begin)
778                {
779                        type = 0;
780                        for (const char* t = curr; t < next; t++)
781                                switch (*t)
782                        {
783                                case 'd': case 'x': case 'X': case 'u': case 'p': case 'c': type = 'd'; t = next; break;
784                                case 'f': case 'g': case 'e': type = 'f'; t = next; break;
785                                case 's': type = 's'; t = next; break;
786                                case 't': case 'T': type = *t; t = next; break;
787                                case '%': if (t > begin) { type = *t; t = next; } break;
788                        }
789                }
790                if (curr > begin) curr--;
791                const ExtValue *a;
792                if (args.finished() && (type != 0) && (type != '%'))
793                {
794                        ret += fmt.substr((int)(curr - begin));
795                        break;
796                }
797                SString sub = fmt.substr((int)(curr - begin), (int)(next - curr));
798                switch (type)
799                {
800                case 'd': a = args.getNext(); ret += SString::sprintf(sub.c_str(), a ? a->getInt() : 0); break;
801                case 'f': a = args.getNext(); ret += SString::sprintf(sub.c_str(), a ? a->getDouble() : 0); break;
802                case 's': {a = args.getNext(); SString tmp; if (a) tmp = a->getString(); ret += SString::sprintf(sub.c_str(), tmp.c_str()); } break;
803                case 't': case 'T':
804                {
805                        a = args.getNext();
806                        time_t ti = a ? a->getInt() : 0;
807                        struct tm tm = Convert::localtime(ti);
808                        SString timtxt;
809                        if (type == 'T')
810                                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);
811                        else
812                                timtxt = Convert::asctime(tm).c_str();
813                        ret += timtxt;
814                        ret += sub.substr(2);
815                }
816                        break;
817                case '%': ret += '%'; ret += sub.substr(2); break;
818                case 0: ret += sub; break;
819                }
820                curr = next + 1;
821        }
822        return ret;
823}
824
825
826void ExtValue::operator%=(const ExtValue& src)
827{
828        switch (type)
829        {
830        case TInt: idata() = idata() % src.getInt(); break;
831        case TDouble: ddata() = fmod(ddata(), src.getDouble()); break;
832
833        case TString:
834        {
835                VectorObject *vec = VectorObject::fromObject(src.getObject(), false);
836                if (vec)
837                        sdata() = format(sdata(), (const ExtValue**)&vec->data.getref(0), vec->data.size());
838                else
839                {
840                        const ExtValue *ptr = &src; sdata() = ExtValue::format(sdata(), &ptr, 1);
841                }
842        }
843                break;
844
845        case TObj: case TUnknown: case TInvalid:
846                logPrintf("ExtValue", "modulo", LOG_WARN, "Can't apply modulo to %s", typeDescription().c_str());
847
848        default:;
849        }
850}
851
852bool ExtValue::parseInt(const char* s, paInt &result, bool strict, bool error)
853{
854        ExtValue tmp;
855        const char* after = tmp.parseNumber(s, strict ? TInt : TUnknown);
856        if ((after == NULL) || (after[0] != 0))
857        {
858                if (error)
859                        logPrintf("ExtValue", "parseInt", LOG_ERROR, "Could not parse '%s'%s", s, strict ? " (strict)" : "");
860                return false;
861        }
862        result = tmp.getInt();
863        return true;
864}
865
866bool ExtValue::parseDouble(const char* s, double &result, bool error)
867{
868        ExtValue tmp;
869        const char* after = tmp.parseNumber(s, TDouble);
870        if ((after == NULL) || (after[0] != 0))
871        {
872                if (error)
873                        logPrintf("ExtValue", "parseDouble", LOG_ERROR, "Could not parse '%s'", s);
874                return false;
875        }
876        result = tmp.getDouble();
877        return true;
878}
879
880paInt ExtValue::getInt(const char* s, bool strict)
881{
882        paInt result;
883        if (parseInt(s, result, strict, true))
884                return result;
885        return 0;
886}
887
888double ExtValue::getDouble(const char* s)
889{
890        double result;
891        if (parseDouble(s, result, true))
892                return result;
893        return 0;
894}
895
896paInt ExtValue::getInt() const
897{
898        switch (type)
899        {
900        case TInt: return idata();
901        case TDouble: return (int)ddata();
902        case TString: return getInt(sdata().c_str());
903        case TObj:
904                logPrintf("ExtValue", "getInt", LOG_ERROR, "Getting integer value from object reference (%s)", getString().c_str());
905                return (paInt)(intptr_t)odata().param;
906        default:;
907        }
908        return 0;
909}
910
911double ExtValue::getDouble() const
912{
913        switch (type)
914        {
915        case TDouble: return ddata();
916        case TInt: return (double)idata();
917        case TString: return getDouble(sdata().c_str());
918        case TObj:
919                logPrintf("ExtValue", "getDouble", LOG_ERROR, "Getting floating point value from object reference (%s)", getString().c_str());
920                return (double)(intptr_t)odata().param;
921        default:;
922        }
923        return 0.0;
924}
925
926SString ExtValue::getString() const
927{
928        switch (type)
929        {
930        case TString: return sdata();
931        case TInt: return SString::valueOf(idata());
932        case TDouble: return SString::valueOf(ddata());
933        case TObj: return odata().toString();
934        case TInvalid:  return SString("invalid");
935        default: return SString("null");
936        }
937}
938
939const SString* ExtValue::getStringPtr() const
940{
941        if (type == TString)
942                return &sdata();
943        return NULL;
944}
945
946SString ExtValue::serialize(SerializationFormat format) const
947{
948        switch (type)
949        {
950        case TString:
951        {
952                SString q = sdata();
953                sstringQuote(q);
954                return SString("\"") + q + SString("\"");
955        }
956        case TInt:
957                return SString::valueOf(idata());
958        case TDouble:
959                return SString::valueOf(ddata());
960        case TObj:
961                return odata().serialize(format);
962        case TInvalid:
963                if (format==NativeSerialization)
964                        return SString("invalid");
965                // else null --v
966        default:
967                return SString("null");
968        }
969}
970
971/// returns the first character after the parsed number, or NULL if not a number
972/// @param strict_type = restrict the allowed return value (TUnknown = unrestricted)
973const char* ExtValue::parseNumber(const char* in, ExtPType strict_type)
974{
975        char* after;
976        if (in == NULL) return NULL;
977        if (in[0] == 0) return NULL;
978        while (isspace(*in)) in++;
979        bool minus = (in[0] == '-');
980        bool plus = (in[0] == '+');
981        if (((in[0] == '0') && ((in[1] == 'x') || (in[1] == 'X')))
982                || (((minus || plus) && (in[1] == '0') && ((in[2] == 'x') || (in[2] == 'X')))))
983        {
984                in += (minus || plus) ? 3 : 2;
985                if (isspace(*in)) return NULL;
986                errno = 0;
987                unsigned long intvalue = strtoul(in, &after, 16);
988                if ((after > in) && (errno == 0) && (intvalue <= 0xffffffff))
989                {
990                        if (strict_type == TDouble)
991                                setDouble(minus ? -(double)intvalue : (double)intvalue);
992                        else
993                                setInt(minus ? -intvalue : intvalue);
994                        return after;
995                }
996                else
997                        return NULL;
998        }
999
1000        errno = 0;
1001        double fpvalue = strtod(in, &after);
1002        if ((after > in) && (errno == 0))
1003        {
1004                if (strict_type != TDouble)
1005                {
1006                        if ((memchr(in, '.', after - in) == NULL) && (memchr(in, 'e', after - in) == NULL) && (memchr(in, 'E', after - in) == NULL) // no "special" characters
1007                                && (fpvalue == floor(fpvalue)) // value is integer
1008                                && (fpvalue >= INT_MIN) && (fpvalue <= INT_MAX)) // within limits
1009                        {
1010                                setInt(fpvalue);
1011                                return after;
1012                        }
1013                        else if (strict_type == TInt)
1014                                return NULL;
1015                }
1016                setDouble(fpvalue);
1017                return after;
1018        }
1019        return NULL;
1020}
1021
1022PtrListTempl<ParamInterface*> &ExtValue::getDeserializableClasses()
1023{
1024        static PtrListTempl<ParamInterface*> classes;
1025        return classes;
1026}
1027
1028ParamInterface *ExtValue::findDeserializableClass(const char* name)
1029{
1030        FOREACH(ParamInterface*, cls, getDeserializableClasses())
1031                if (!strcmp(cls->getName(), name))
1032                        return cls;
1033        return NULL;
1034}
1035
1036static const char* skipWord(const char* in)
1037{
1038        while (isalpha(*in) || (*in == '_'))
1039                in++;
1040        return in;
1041}
1042
1043//returns the first character after the parsed portion or NULL if invalid format
1044const char* ExtValue::deserialize_inner(const char* in)
1045{
1046        while(isspace(*in)) in++;
1047        const char* ret = parseNumber(in);
1048        if (ret)
1049                return ret;
1050        else if (*in == '\"')
1051        {
1052                ret = skipQuoteString(in + 1, NULL);
1053                SString s(in + 1, (int)(ret - (in + 1)));
1054                sstringUnquote(s);
1055                setString(s);
1056                if (*ret == '\"')
1057                        return ret + 1;
1058                else
1059                {
1060                        logPrintf("ExtValue", "deserialize", LOG_ERROR, "Missing '\"' in string: '%s'", ret);
1061                        return NULL;
1062                }
1063        }
1064        else if (*in == '[')
1065        {
1066                VectorObject *vec = new VectorObject;
1067                ExtObject o(&VectorObject::par, vec);
1068                tlsGetRef(ExtObject_serialization).add(o);
1069                const char* p = in + 1;
1070                ExtValue tmp;
1071                while (*p)
1072                {
1073                        if (isspace(*p)) p++;
1074                        if (*p == ']') { p++; break; }
1075                        ret = tmp.deserialize(p);
1076                        if (ret)
1077                        {
1078                                vec->data += new ExtValue(tmp);
1079                                p = ret;
1080                                if (*p == ',') p++;
1081                                else if (*p != ']')
1082                                {
1083                                        logPrintf("ExtValue", "deserialize", LOG_ERROR, "Missing ',' in Vector: '%s'", p);
1084                                        return NULL;
1085                                }
1086                        }
1087                        else
1088                        {
1089                                p = NULL;
1090                                break;
1091                        }
1092                }
1093                setObject(o);
1094                return p;
1095        }
1096        else if (*in == '{')
1097        {
1098                DictionaryObject *dic = new DictionaryObject;
1099                ExtObject o(&DictionaryObject::par, dic);
1100                tlsGetRef(ExtObject_serialization).add(o);
1101                const char* p = in + 1;
1102                ExtValue args[2]/*={value,key}*/, dummy_ret;
1103                while (*p)
1104                {
1105                        if (isspace(*p)) p++;
1106                        if (*p == '}') { p++; break; }
1107                        ret = args[1].deserialize(p);
1108                        if ((!ret) || (args[1].getType() != TString)) { p = NULL; break; }
1109                        p = ret;
1110                        if (*p != ':') { logPrintf("ExtValue", "deserialize", LOG_ERROR, "Missing ':' in Dictionary: '%s'", p); p = NULL; break; }
1111                        p++;
1112                        ret = args[0].deserialize(p);
1113                        if (!ret) { p = NULL; break; }
1114                        p = ret;
1115                        dic->p_set(args, &dummy_ret);
1116                        if (*p == ',') p++;
1117                        else if (*p != '}')
1118                        {
1119                                logPrintf("ExtValue", "deserialize", LOG_ERROR, "Missing ',' in Dictionary: '%s'", p);
1120                                return NULL;
1121                        }
1122                }
1123                setObject(o);
1124                return p;
1125        }
1126        else if (!strncmp(in, "null", 4))
1127        {
1128                setEmpty();
1129                return in + 4;
1130        }
1131        else if (!strncmp(in, "true", 4))
1132        {
1133                setInt(1);
1134                return in + 4;
1135        }
1136        else if (!strncmp(in, "false", 5))
1137        {
1138                setInt(0);
1139                return in + 5;
1140        }
1141        else if (!strncmp(in, "invalid", 7))
1142        {
1143                setInvalid();
1144                return in + 7;
1145        }
1146        else if (*in == '<')
1147        { //unserializable object
1148                const char* end=in+1;
1149                while (*end)
1150                        if (*end == '>')
1151                                {
1152                                setError(SString("Unserializable class: ")+SString(in+1,end-in-1));
1153                                return end+1;
1154                                }
1155                        else
1156                                end++;
1157                logPrintf("ExtValue", "deserialize", LOG_ERROR, "Missing '>'");
1158                return NULL;
1159        }
1160        else if (*in == '^')
1161        {
1162                in++;
1163                ExtValue ref;
1164                ret = ref.parseNumber(in, TInt);
1165                if (ret && (ref.getType() == TInt))
1166                {
1167                        const ExtObject* o = tlsGetRef(ExtObject_serialization).get(ref.getInt());
1168                        if (o)
1169                        {
1170                                setObject(*o);
1171                                return ret;
1172                        }
1173                }
1174                logPrintf("ExtValue", "deserialize", LOG_ERROR, "Invalid reference: '%s'", in - 1);
1175                return NULL;
1176        }
1177        else if ((ret = skipWord(in)) && (ret != in))
1178        {
1179                SString clsname(in, (int)(ret - in));
1180                ExtValue tmp;
1181                ret = tmp.deserialize(ret);
1182                ParamInterface *cls = findDeserializableClass(clsname.c_str());
1183                if (cls && (tmp.getType() != TUnknown) && (tmp.getType() != TInvalid))
1184                {
1185                        VectorObject *vec = VectorObject::fromObject(tmp.getObject(), false);
1186                        if (vec)
1187                        {
1188                                int m = cls->findId("newFromVector");
1189                                if (m >= 0)
1190                                {
1191                                        cls->call(m, &tmp, this);
1192                                        tlsGetRef(ExtObject_serialization).replace(tmp.getObject(), getObject());
1193                                        return ret;
1194                                }
1195                        }
1196                        DictionaryObject *dic = DictionaryObject::fromObject(tmp.getObject(), false);
1197                        if (dic)
1198                        {
1199                                int m = cls->findId("newFromDictionary");
1200                                if (m >= 0)
1201                                {
1202                                        cls->call(m, &tmp, this);
1203                                        tlsGetRef(ExtObject_serialization).replace(tmp.getObject(), getObject());
1204                                        return ret;
1205                                }
1206                        }
1207                        if (tmp.getType() == TString)
1208                        {
1209                                int m = cls->findId("newFromString");
1210                                if (m >= 0)
1211                                {
1212                                        cls->call(m, &tmp, this);
1213                                        tlsGetRef(ExtObject_serialization).replace(tmp.getObject(), getObject());
1214                                        return ret;
1215                                }
1216                        }
1217                        tlsGetRef(ExtObject_serialization).remove(tmp.getObject());
1218                        setEmpty();
1219                }
1220                setEmpty();
1221                logPrintf("ExtValue", "deserialize", LOG_WARN, "object of class \"%s\" could not be deserialized", clsname.c_str());
1222                return ret;
1223        }
1224        logPrintf("ExtValue", "deserialize", LOG_ERROR, "Bad syntax: '%s'", in);
1225        setEmpty();
1226        return NULL;
1227}
1228
1229const char* ExtValue::deserialize(const char* in)
1230{
1231        tlsGetRef(ExtObject_serialization).begin();
1232        const char* ret = deserialize_inner(in);
1233        if (ret) while(isspace(*ret)) ret++;
1234        tlsGetRef(ExtObject_serialization).end();
1235        return ret;
1236}
1237
1238ExtObject ExtValue::getObject() const
1239{
1240        if (type == TObj) return odata();
1241        return ExtObject();
1242}
1243
1244ExtValue ExtValue::getExtType()
1245{
1246        static const char* typenames[] = { "null", "int", "float", "string", "", "invalid" };
1247        if (getType() != TObj)
1248                return ExtValue(typenames[(int)getType()]);
1249        ExtObject& o = odata();
1250        return ExtValue(SString(o.isEmpty() ? "" : o.interfaceName()));
1251}
1252
1253SString SString::valueOf(const ExtValue& v)
1254{
1255        return v.getString();
1256}
1257SString SString::valueOf(const ExtObject& v)
1258{
1259        return v.toString();
1260}
1261
1262#define FIELDSTRUCT ErrorObject
1263static ParamEntry errorobject_paramtab[]=
1264{
1265{"Error",1,3,"Error",},
1266{"message",0,0,"Message","s",FIELD(message),},
1267{"newFromString",0,0,"create new object","p oError(s message)",PROCEDURE(p_newfromstring),},
1268{"toString",0,PARAM_READONLY | PARAM_NOSTATIC,"Textual form","s",GETONLY(toString),},
1269{0,0,0,},
1270};
1271#undef FIELDSTRUCT
1272
1273Param& ErrorObject::getParam()
1274{
1275        static Param param(errorobject_paramtab);
1276        return param;
1277}
1278
1279ExtObject ErrorObject::makeDynamicObject(ErrorObject* e)
1280{
1281        return ExtObject(&getParam(), (DestrBase*)e);
1282}
1283
1284const SString ErrorObject::TO_STRING_PREFIX="Error: ";
1285
1286void ErrorObject::get_toString(ExtValue* ret)
1287{
1288ret->setString(TO_STRING_PREFIX+message);
1289}
1290
1291void ErrorObject::p_newfromstring(ExtValue *args, ExtValue *ret)
1292{
1293ErrorObject *err=new ErrorObject();
1294err->message=args[0].getString();
1295if (err->message.startsWith(TO_STRING_PREFIX.c_str()))
1296        err->message=err->message.substr(TO_STRING_PREFIX.len());
1297*ret = makeDynamicObject(err);
1298}
1299
1300REGISTER_DESERIALIZABLE(ErrorObject)
Note: See TracBrowser for help on using the repository browser.