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

Last change on this file since 464 was 464, checked in by Maciej Komosinski, 8 years ago

Serialization of basic object types in JSON format

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