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

Last change on this file since 465 was 465, checked in by Maciej Komosinski, 6 years ago

Avoid compilation warning

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