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

Last change on this file since 1305 was 1275, checked in by Maciej Komosinski, 15 months ago

More unification of floating point exception handling across platforms

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