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

Last change on this file since 547 was 522, checked in by Maciej Komosinski, 9 years ago

Code formatting

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