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

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

Protected against overflow when converting floating point value to integer value

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