pvariant.cxx (13278B)
1 /* 2 * 3 * C++ Portable Types Library (PTypes) 4 * Version 2.1.1 Released 27-Jun-2007 5 * 6 * Copyright (C) 2001-2007 Hovik Melikyan 7 * 8 * http://www.melikyan.com/ptypes/ 9 * 10 */ 11 12 13 #include <stdlib.h> 14 #include <stdio.h> 15 #include <limits.h> 16 17 #include "ptypes.h" 18 19 20 namespace ptypes { 21 22 23 const variant nullvar; 24 25 26 struct _varitem 27 { 28 string key; 29 variant var; 30 31 _varitem(const string& ikey, const variant& ivar): key(ikey), var(ivar) {} 32 }; 33 typedef _varitem* pvaritem; 34 35 36 class ptpublic _varray: protected tobjlist<_varitem> 37 { 38 protected: 39 int refcount; 40 41 virtual int compare(const void* key, const void* item) const; 42 43 friend class variant; 44 45 public: 46 _varray(); 47 _varray(const _varray& a); 48 virtual ~_varray(); 49 50 int get_count() { return tobjlist<_varitem>::get_count(); } 51 void clear() { tobjlist<_varitem>::clear(); } 52 void pack() { tobjlist<_varitem>::pack(); } 53 _varitem* doget(int index) const { return tobjlist<_varitem>::doget(index); } 54 const variant& get(int index) const { if (unsigned(index) < unsigned(count)) return doget(index)->var; else return nullvar; } 55 const string& getkey(int index) const { if (unsigned(index) < unsigned(count)) return doget(index)->key; else return nullstring; } 56 const variant& get(const char* key) const; 57 int put(const string& key, const variant& var); 58 void put(int index, const variant& var) { if (unsigned(index) < unsigned(count)) doget(index)->var = var; } 59 void ins(int index, const variant& var) { if (unsigned(index) < unsigned(count)) doins(index, new _varitem(nullstring, var)); } 60 int addvar(const variant& var); 61 void del(int index) { if (unsigned(index) < unsigned(count)) dodel(index); } 62 void del(const string& key) { put(key, nullstring); } 63 }; 64 typedef _varray* pvarray; 65 66 67 _varray::_varray() 68 : tobjlist<_varitem>(true), refcount(0) 69 { 70 config.sorted = true; 71 config.casesens = true; 72 } 73 74 75 _varray::_varray(const _varray& a) 76 : tobjlist<_varitem>(true), refcount(0) 77 { 78 config.sorted = true; 79 config.casesens = true; 80 set_capacity(a.count); 81 for (int i = 0; i < a.count; i++) 82 { 83 _varitem* v = a.doget(i); 84 doins(i, new _varitem(v->key, v->var)); 85 } 86 } 87 88 89 _varray::~_varray() 90 { 91 } 92 93 94 int _varray::compare(const void* key, const void* item) const 95 { 96 if (config.casesens) 97 return strcmp(pconst(key), pvaritem(item)->key); 98 else 99 return strcasecmp(pconst(key), pvaritem(item)->key); 100 } 101 102 103 const variant& _varray::get(const char* key) const 104 { 105 int index; 106 if (search(key, index)) 107 return doget(index)->var; 108 else 109 return nullvar; 110 } 111 112 113 int _varray::put(const string& key, const variant& var) 114 { 115 int index; 116 if (search(pconst(key), index)) 117 { 118 if (isnull(var)) 119 dodel(index); 120 else 121 doget(index)->var = var; 122 } 123 else if (!isnull(var)) 124 doins(index, new _varitem(key, var)); 125 return index; 126 } 127 128 129 int _varray::addvar(const variant& v) 130 { 131 int i; 132 if (count > 0 && isempty(doget(count - 1)->key)) 133 i = count; 134 else 135 i = 0; 136 doins(i, new _varitem(nullstring, v)); 137 return i; 138 } 139 140 141 static void vconverr(large v); 142 143 144 static void vfatal() 145 { 146 fatal(CRIT_FIRST + 60, "Variant data corrupt"); 147 } 148 149 150 evariant::~evariant() 151 { 152 } 153 154 155 void variant::initialize(_varray* a) 156 { 157 tag = VAR_ARRAY; 158 #ifdef PTYPES_ST 159 a->refcount++; 160 #else 161 pincrement(&a->refcount); 162 #endif 163 value.a = a; 164 } 165 166 167 void variant::initialize(component* o) 168 { 169 tag = VAR_OBJECT; 170 value.o = addref(o); 171 } 172 173 174 void variant::initialize(const variant& v) 175 { 176 switch (v.tag) 177 { 178 case VAR_NULL: 179 tag = VAR_NULL; 180 break; 181 case VAR_INT: 182 case VAR_BOOL: 183 case VAR_FLOAT: 184 tag = v.tag; 185 value = v.value; 186 break; 187 case VAR_STRING: 188 initialize(PTR_TO_STRING(v.value.s)); 189 break; 190 case VAR_ARRAY: 191 initialize(v.value.a); 192 break; 193 case VAR_OBJECT: 194 initialize(v.value.o); 195 break; 196 default: 197 vfatal(); 198 } 199 } 200 201 202 void variant::finalize() 203 { 204 if (tag >= VAR_COMPOUND) 205 { 206 switch (tag) 207 { 208 case VAR_STRING: 209 ptypes::finalize(PTR_TO_STRING(value.s)); 210 break; 211 case VAR_ARRAY: 212 #ifdef PTYPES_ST 213 if (--value.a->refcount == 0) 214 #else 215 if (pdecrement(&value.a->refcount) == 0) 216 #endif 217 delete value.a; 218 break; 219 case VAR_OBJECT: 220 release(value.o); 221 break; 222 default: 223 vfatal(); 224 } 225 } 226 tag = VAR_NULL; 227 } 228 229 230 void variant::assign(large v) { finalize(); initialize(v); } 231 void variant::assign(bool v) { finalize(); initialize(v); } 232 void variant::assign(double v) { finalize(); initialize(v); } 233 void variant::assign(const char* v) { finalize(); initialize(v); } 234 235 236 void variant::assign(const string& v) 237 { 238 if (tag == VAR_STRING) 239 PTR_TO_STRING(value.s) = v; 240 else 241 { 242 finalize(); 243 initialize(v); 244 } 245 } 246 247 248 void variant::assign(_varray* a) 249 { 250 if (tag == VAR_ARRAY && value.a == a) 251 return; 252 finalize(); 253 initialize(a); 254 } 255 256 257 void variant::assign(component* o) 258 { 259 if (tag == VAR_OBJECT) 260 { 261 if (value.o == o) 262 return; 263 else 264 release(value.o); 265 } 266 else 267 finalize(); 268 initialize(o); 269 } 270 271 272 void variant::assign(const variant& v) 273 { 274 switch (v.tag) 275 { 276 case VAR_NULL: 277 finalize(); 278 tag = VAR_NULL; 279 break; 280 case VAR_INT: 281 case VAR_BOOL: 282 case VAR_FLOAT: 283 finalize(); 284 tag = v.tag; 285 value = v.value; 286 break; 287 case VAR_STRING: 288 assign(PTR_TO_STRING(v.value.s)); 289 break; 290 case VAR_ARRAY: 291 assign(v.value.a); 292 break; 293 case VAR_OBJECT: 294 assign(v.value.o); 295 break; 296 default: 297 vfatal(); 298 } 299 } 300 301 302 void ptdecl clear(variant& v) 303 { 304 v.finalize(); 305 v.initialize(); 306 } 307 308 309 variant::operator int() const 310 { 311 large t = operator large(); 312 if (t < INT_MIN || t > INT_MAX) 313 vconverr(t); 314 return int(t); 315 } 316 317 318 variant::operator unsigned int() const 319 { 320 large t = operator large(); 321 if (t < 0 || t > UINT_MAX) 322 vconverr(t); 323 return uint(t); 324 } 325 326 327 variant::operator long() const 328 { 329 large t = operator large(); 330 if (t < LONG_MIN || t > LONG_MAX) 331 vconverr(t); 332 return int(t); 333 } 334 335 336 variant::operator unsigned long() const 337 { 338 large t = operator large(); 339 if (t < 0 || t > large(ULONG_MAX)) 340 vconverr(t); 341 return uint(t); 342 } 343 344 345 variant::operator large() const 346 { 347 switch(tag) 348 { 349 case VAR_NULL: return 0; 350 case VAR_INT: return value.i; 351 case VAR_BOOL: return int(value.b); 352 case VAR_FLOAT: return int(value.f); 353 case VAR_STRING: 354 { 355 const char* p = PTR_TO_STRING(value.s); 356 bool neg = *p == '-'; 357 if (neg) 358 p++; 359 large t = stringtoi(p); 360 if (t < 0) 361 return 0; 362 else 363 return neg ? -t : t; 364 } 365 case VAR_ARRAY: return value.a->count != 0; 366 case VAR_OBJECT: return 0; 367 default: vfatal(); 368 } 369 return 0; 370 } 371 372 373 variant::operator bool() const 374 { 375 switch(tag) 376 { 377 case VAR_NULL: return false; 378 case VAR_INT: return value.i != 0; 379 case VAR_BOOL: return value.b; 380 case VAR_FLOAT: return value.f != 0; 381 case VAR_STRING: return !isempty((PTR_TO_STRING(value.s))); 382 case VAR_ARRAY: return value.a->count != 0; 383 case VAR_OBJECT: return value.o != nil; 384 default: vfatal(); 385 } 386 return false; 387 } 388 389 390 variant::operator double() const 391 { 392 switch(tag) 393 { 394 case VAR_NULL: return 0; 395 case VAR_INT: return double(value.i); 396 case VAR_BOOL: return int(value.b); 397 case VAR_FLOAT: return value.f; 398 case VAR_STRING: 399 { 400 char* e; 401 double t = strtod(PTR_TO_STRING(value.s), &e); 402 if (*e != 0) 403 return 0; 404 else 405 return t; 406 } 407 case VAR_ARRAY: return int(value.a->count != 0); 408 case VAR_OBJECT: return 0; 409 default: vfatal(); 410 } 411 return 0; 412 } 413 414 415 void string::initialize(const variant& v) 416 { 417 switch(v.tag) 418 { 419 case VAR_NULL: initialize(); break; 420 case VAR_INT: initialize(itostring(v.value.i)); break; 421 case VAR_BOOL: if (v.value.b) initialize('1'); else initialize('0'); break; 422 case VAR_FLOAT: 423 { 424 char buf[256]; 425 sprintf(buf, "%g", v.value.f); 426 initialize(buf); 427 } 428 break; 429 case VAR_STRING: initialize(PTR_TO_STRING(v.value.s)); break; 430 case VAR_ARRAY: initialize(); break; 431 case VAR_OBJECT: initialize(); break; 432 default: vfatal(); 433 } 434 } 435 436 437 variant::operator string() const 438 { 439 // this is a 'dirty' solution to gcc 3.3 typecast problem. most compilers 440 // handle variant::operator string() pretty well, while gcc 3.3 requires 441 // to explicitly declare a constructor string::string(const variant&). 442 // ironically, the presence of both the typecast operator and the constructor 443 // confuses the MSVC compiler. so the only thing we can do to please all 444 // those compilers [that "move towards the c++ standard"] is to conditionally 445 // exclude the constructor string(const variant&). and this is not the whole 446 // story. i see you are bored with it and i let you go. nobody would ever care 447 // about this. it just works, though i'm not happy with what i wrote here: 448 string t; 449 t.initialize(*this); 450 return t; 451 } 452 453 454 variant::operator component*() const 455 { 456 if (tag == VAR_OBJECT) 457 return value.o; 458 else 459 return nil; 460 } 461 462 463 bool variant::equal(const variant& v) const 464 { 465 if (tag != v.tag) 466 return false; 467 switch (tag) 468 { 469 case VAR_NULL: return true; 470 case VAR_INT: return value.i == v.value.i; 471 case VAR_BOOL: return value.b == v.value.b; 472 case VAR_FLOAT: return value.f == v.value.f; 473 case VAR_STRING: return strcmp(value.s, v.value.s) == 0; 474 case VAR_ARRAY: return value.a == v.value.a; 475 case VAR_OBJECT: return value.o == v.value.o; 476 default: vfatal(); return false; 477 } 478 } 479 480 481 static string numkey(large key) 482 { 483 return itostring(key, 16, 16, '0'); 484 } 485 486 487 void ptdecl aclear(variant& v) 488 { 489 if (v.tag == VAR_ARRAY) 490 v.value.a->clear(); 491 else 492 { 493 v.finalize(); 494 v.initialize(new _varray()); 495 } 496 } 497 498 499 void ptdecl apack(variant& v) 500 { 501 if (v.tag == VAR_ARRAY) 502 v.value.a->pack(); 503 } 504 505 506 variant ptdecl aclone(const variant& v) 507 { 508 if (v.tag == VAR_ARRAY) 509 return variant(new _varray(*(v.value.a))); 510 else 511 return variant(new _varray()); 512 } 513 514 515 int ptdecl alength(const variant& v) 516 { 517 if (v.tag == VAR_ARRAY) 518 return v.value.a->get_count(); 519 else 520 return 0; 521 } 522 523 524 const variant& ptdecl get(const variant& v, const string& key) 525 { 526 if (v.tag == VAR_ARRAY) 527 return v.value.a->get(key); 528 else 529 return nullvar; 530 } 531 532 533 const variant& ptdecl get(const variant& v, large key) 534 { 535 return get(v, numkey(key)); 536 } 537 538 539 void ptdecl put(variant& v, const string& key, const variant& item) 540 { 541 if (v.tag != VAR_ARRAY) 542 aclear(v); 543 v.value.a->put(key, item); 544 } 545 546 547 void ptdecl put(variant& v, large key, const variant& item) 548 { 549 put(v, numkey(key), item); 550 } 551 552 553 void ptdecl del(variant& v, const string& key) 554 { 555 if (v.tag == VAR_ARRAY) 556 v.value.a->del(key); 557 } 558 559 560 void ptdecl del(variant& v, large key) 561 { 562 del(v, numkey(key)); 563 } 564 565 566 bool ptdecl anext(const variant& array, int& index, variant& item) 567 { 568 string key; 569 return anext(array, index, item, key); 570 } 571 572 573 bool ptdecl anext(const variant& array, int& index, variant& item, string& key) 574 { 575 if (array.tag != VAR_ARRAY) 576 { 577 clear(item); 578 return false; 579 } 580 if (index < 0 || index >= array.value.a->get_count()) 581 { 582 clear(item); 583 return false; 584 } 585 item = array.value.a->doget(index)->var; 586 key = array.value.a->doget(index)->key; 587 index++; 588 return true; 589 } 590 591 592 int ptdecl aadd(variant& array, const variant& item) 593 { 594 if (array.tag != VAR_ARRAY) 595 aclear(array); 596 return array.value.a->addvar(item); 597 } 598 599 600 const variant& ptdecl aget(const variant& array, int index) 601 { 602 if (array.tag == VAR_ARRAY) 603 return array.value.a->get(index); 604 else 605 return nullvar; 606 } 607 608 609 void ptdecl adel(variant& array, int index) 610 { 611 if (array.tag == VAR_ARRAY) 612 array.value.a->del(index); 613 } 614 615 616 void ptdecl aput(variant& array, int index, const variant& item) 617 { 618 if (array.tag == VAR_ARRAY) 619 array.value.a->put(index, item); 620 } 621 622 623 void ptdecl ains(variant& array, int index, const variant& item) 624 { 625 if (array.tag == VAR_ARRAY) 626 array.value.a->ins(index, item); 627 } 628 629 630 #ifdef _MSC_VER 631 // disable "unreachable code" warning for throw (known compiler bug) 632 # pragma warning (disable: 4702) 633 #endif 634 635 static void vconverr(large v) 636 { 637 throw new evariant("Value out of range: " + itostring(v)); 638 } 639 640 641 }