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

Last change on this file since 372 was 372, checked in by sz, 9 years ago

Renamed some classes and functions to make their purpose more obvious:

All MessageHandlers? must now be given the explicit "Enable" argument if you want them to automatically become active. This makes side effects clearly visible.

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