Coverage Report

Created: 2026-06-07 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/poco/Foundation/src/Var.cpp
Line
Count
Source
1
//
2
// Var.cpp
3
//
4
// Library: Foundation
5
// Package: Core
6
// Module:  Var
7
//
8
// Copyright (c) 2007, Applied Informatics Software Engineering GmbH.
9
// and Contributors.
10
//
11
// SPDX-License-Identifier: BSL-1.0
12
//
13
14
15
#include "Poco/Dynamic/Var.h"
16
#include "Poco/Dynamic/Struct.h"
17
#include <algorithm>
18
#include <cctype>
19
#include <vector>
20
#include <list>
21
#include <deque>
22
23
24
namespace Poco::Dynamic {
25
26
27
Var::Var()
28
3.24M
{
29
3.24M
}
30
31
32
Var::Var(const char* pVal)
33
0
{
34
0
  construct(std::string(pVal));
35
0
}
36
37
38
Var::Var(const Var& other)
39
35.3M
{
40
35.3M
  if ((this != &other) && !other.isEmpty())
41
35.3M
      construct(other);
42
35.3M
}
43
44
45
Var::~Var()
46
52.2M
{
47
52.2M
  destruct();
48
52.2M
}
49
50
51
Var& Var::operator = (const Var& rhs)
52
3.48M
{
53
3.48M
  if (this == &rhs) return *this;
54
3.48M
  clear();
55
3.48M
  if (!rhs.isEmpty()) construct(rhs);
56
3.48M
  return *this;
57
3.48M
}
58
59
60
const Var Var::operator + (const Var& other) const
61
0
{
62
0
  if (isInteger())
63
0
  {
64
0
    if (isSigned())
65
0
      return add<Poco::Int64>(other);
66
0
    else
67
0
      return add<Poco::UInt64>(other);
68
0
  }
69
0
  else if (isNumeric())
70
0
    return add<double>(other);
71
0
  else if (isString())
72
0
    return add<std::string>(other);
73
0
  else
74
0
    throw InvalidArgumentException("Invalid operation for this data type.");
75
0
}
76
77
78
Var& Var::operator += (const Var& other)
79
0
{
80
0
  if (isInteger())
81
0
  {
82
0
    if (isSigned())
83
0
      return *this = add<Poco::Int64>(other);
84
0
    else
85
0
      return *this = add<Poco::UInt64>(other);
86
0
  }
87
0
  else if (isNumeric())
88
0
    return *this = add<double>(other);
89
0
  else if (isString())
90
0
    return *this = add<std::string>(other);
91
0
  else
92
0
    throw InvalidArgumentException("Invalid operation for this data type.");
93
0
}
94
95
96
const Var Var::operator - (const Var& other) const
97
0
{
98
0
  if (isInteger())
99
0
  {
100
0
    if (isSigned())
101
0
      return subtract<Poco::Int64>(other);
102
0
    else
103
0
      return subtract<Poco::UInt64>(other);
104
0
  }
105
0
  else if (isNumeric())
106
0
    return subtract<double>(other);
107
0
  else
108
0
    throw InvalidArgumentException("Invalid operation for this data type.");
109
0
}
110
111
112
Var& Var::operator -= (const Var& other)
113
0
{
114
0
  if (isInteger())
115
0
  {
116
0
    if (isSigned())
117
0
      return *this = subtract<Poco::Int64>(other);
118
0
    else
119
0
      return *this = subtract<Poco::UInt64>(other);
120
0
  }
121
0
  else if (isNumeric())
122
0
    return *this = subtract<double>(other);
123
0
  else
124
0
    throw InvalidArgumentException("Invalid operation for this data type.");
125
0
}
126
127
128
const Var Var::operator * (const Var& other) const
129
0
{
130
0
  if (isInteger())
131
0
  {
132
0
    if (isSigned())
133
0
      return multiply<Poco::Int64>(other);
134
0
    else
135
0
      return multiply<Poco::UInt64>(other);
136
0
  }
137
0
  else if (isNumeric())
138
0
    return multiply<double>(other);
139
0
  else
140
0
    throw InvalidArgumentException("Invalid operation for this data type.");
141
0
}
142
143
144
Var& Var::operator *= (const Var& other)
145
0
{
146
0
  if (isInteger())
147
0
  {
148
0
    if (isSigned())
149
0
      return *this = multiply<Poco::Int64>(other);
150
0
    else
151
0
      return *this = multiply<Poco::UInt64>(other);
152
0
  }
153
0
  else if (isNumeric())
154
0
    return *this = multiply<double>(other);
155
0
  else
156
0
    throw InvalidArgumentException("Invalid operation for this data type.");
157
0
}
158
159
160
const Var Var::operator / (const Var& other) const
161
0
{
162
0
  if (isInteger())
163
0
  {
164
0
    if (isSigned())
165
0
      return divide<Poco::Int64>(other);
166
0
    else
167
0
      return divide<Poco::UInt64>(other);
168
0
  }
169
0
  else if (isNumeric())
170
0
    return divide<double>(other);
171
0
  else
172
0
    throw InvalidArgumentException("Invalid operation for this data type.");
173
0
}
174
175
176
Var& Var::operator /= (const Var& other)
177
0
{
178
0
  if (isInteger())
179
0
  {
180
0
    if (isSigned())
181
0
      return *this = divide<Poco::Int64>(other);
182
0
    else
183
0
      return *this = divide<Poco::UInt64>(other);
184
0
  }
185
0
  else if (isNumeric())
186
0
    return *this = divide<double>(other);
187
0
  else
188
0
    throw InvalidArgumentException("Invalid operation for this data type.");
189
0
}
190
191
192
Var& Var::operator ++ ()
193
0
{
194
0
  if (!isInteger())
195
0
    throw InvalidArgumentException("Invalid operation for this data type.");
196
197
0
  return *this = *this + 1;
198
0
}
199
200
201
const Var Var::operator ++ (int)
202
0
{
203
0
  if (!isInteger())
204
0
    throw InvalidArgumentException("Invalid operation for this data type.");
205
206
0
  Var tmp(*this);
207
0
  *this += 1;
208
0
  return tmp;
209
0
}
210
211
212
Var& Var::operator -- ()
213
0
{
214
0
  if (!isInteger())
215
0
    throw InvalidArgumentException("Invalid operation for this data type.");
216
217
0
  return *this = *this - 1;
218
0
}
219
220
221
const Var Var::operator -- (int)
222
0
{
223
0
  if (!isInteger())
224
0
    throw InvalidArgumentException("Invalid operation for this data type.");
225
226
0
  Var tmp(*this);
227
0
  *this -= 1;
228
0
  return tmp;
229
0
}
230
231
232
bool Var::operator == (const Var& other) const
233
0
{
234
0
  if (isEmpty() != other.isEmpty()) return false;
235
0
  if (isEmpty() && other.isEmpty()) return true;
236
0
  return convert<std::string>() == other.convert<std::string>();
237
0
}
238
239
240
bool Var::operator == (const char* other) const
241
0
{
242
0
  if (isEmpty()) return false;
243
0
  return convert<std::string>() == other;
244
0
}
245
246
247
bool Var::operator != (const Var& other) const
248
0
{
249
0
  if (isEmpty() && other.isEmpty()) return false;
250
0
  else if (isEmpty() || other.isEmpty()) return true;
251
252
0
  return convert<std::string>() != other.convert<std::string>();
253
0
}
254
255
256
bool Var::operator != (const char* other) const
257
0
{
258
0
  if (isEmpty()) return true;
259
0
  return convert<std::string>() != other;
260
0
}
261
262
263
bool Var::operator < (const Var& other) const
264
0
{
265
0
  if (isEmpty() || other.isEmpty()) return false;
266
0
  return convert<std::string>() < other.convert<std::string>();
267
0
}
268
269
270
bool Var::operator <= (const Var& other) const
271
0
{
272
0
  if (isEmpty() || other.isEmpty()) return false;
273
0
  return convert<std::string>() <= other.convert<std::string>();
274
0
}
275
276
277
bool Var::operator > (const Var& other) const
278
0
{
279
0
  if (isEmpty() || other.isEmpty()) return false;
280
0
  return convert<std::string>() > other.convert<std::string>();
281
0
}
282
283
284
bool Var::operator >= (const Var& other) const
285
0
{
286
0
  if (isEmpty() || other.isEmpty()) return false;
287
0
  return convert<std::string>() >= other.convert<std::string>();
288
0
}
289
290
291
bool Var::operator || (const Var& other) const
292
0
{
293
0
  if (isEmpty() || other.isEmpty()) return false;
294
0
  return convert<bool>() || other.convert<bool>();
295
0
}
296
297
298
bool Var::operator && (const Var& other) const
299
0
{
300
0
  if (isEmpty() || other.isEmpty()) return false;
301
0
  return convert<bool>() && other.convert<bool>();
302
0
}
303
304
305
void Var::empty()
306
0
{
307
0
  _placeholder.erase();
308
0
}
309
310
311
void Var::clear()
312
3.55M
{
313
3.55M
  _placeholder.erase();
314
3.55M
}
315
316
317
Var& Var::getAt(std::size_t n)
318
0
{
319
0
  if (isVector())
320
0
    return holderImpl<std::vector<Var>,
321
0
      InvalidAccessException>("Not a vector.")->operator[](n);
322
0
  else if (isList())
323
0
    return holderImpl<std::list<Var>,
324
0
      InvalidAccessException>("Not a list.")->operator[](n);
325
0
  else if (isDeque())
326
0
    return holderImpl<std::deque<Var>,
327
0
      InvalidAccessException>("Not a deque.")->operator[](n);
328
0
  else if (isStruct())
329
0
  {
330
0
    if (isOrdered())
331
0
      return structIndexOperator(holderImpl<Struct<int, OrderedMap<int, Var>, OrderedSet<int>>,
332
0
        InvalidAccessException>("Not a struct."), static_cast<int>(n));
333
0
    else
334
0
      return structIndexOperator(holderImpl<Struct<int, std::map<int, Var>, std::set<int>>,
335
0
        InvalidAccessException>("Not a struct."), static_cast<int>(n));
336
0
  }
337
0
  else if (!isString() && !isEmpty() && (n == 0))
338
0
    return *this;
339
340
0
  throw RangeException("Index out of bounds.");
341
0
}
342
343
344
char& Var::at(std::size_t n)
345
0
{
346
0
  if (isString())
347
0
  {
348
0
    return holderImpl<std::string,
349
0
      InvalidAccessException>("Not a string.")->operator[](n);
350
0
  }
351
352
0
  throw InvalidAccessException("Not a string.");
353
0
}
354
355
356
Var& Var::getAt(const std::string& name)
357
0
{
358
0
  if (isStruct())
359
0
  {
360
0
    if (isOrdered())
361
0
      return structIndexOperator(holderImpl<OrderedDynamicStruct, InvalidAccessException>("Not a struct."), name);
362
0
    else
363
0
      return structIndexOperator(holderImpl<DynamicStruct, InvalidAccessException>("Not a struct."), name);
364
0
  }
365
366
0
  throw InvalidAccessException("Not a struct.");
367
0
}
368
369
370
std::string Var::toString() const
371
0
{
372
0
  VarHolder* pHolder = content();
373
374
0
  if (!pHolder)
375
0
    throw InvalidAccessException("Can not convert empty value.");
376
377
0
  if (typeid(std::string) == pHolder->type())
378
0
    return extract<std::string>();
379
0
  else
380
0
  {
381
0
    std::string result;
382
0
    pHolder->convert(result);
383
0
    return result;
384
0
  }
385
0
}
386
387
388
Var Var::parse(const std::string& val)
389
0
{
390
0
  std::string::size_type t = 0;
391
0
  return parse(val, t);
392
0
}
393
394
395
Var Var::parse(const std::string& val, std::string::size_type& pos)
396
0
{
397
  // { -> an Object==DynamicStruct
398
  // [ -> an array
399
  // '/" -> a string (strip '/")
400
  // other: also treat as string
401
0
  skipWhiteSpace(val, pos);
402
0
  if (pos < val.size())
403
0
  {
404
0
    switch (val[pos])
405
0
    {
406
0
    case '{':
407
0
      return parseObject(val, pos);
408
0
    case '[':
409
0
      return parseArray(val, pos);
410
0
    case '"':
411
0
      return parseJSONString(val, pos);
412
0
    default:
413
0
      {
414
0
        std::string str = parseString(val, pos);
415
0
        if (str == "false")
416
0
          return false;
417
0
        else if (str == "true")
418
0
          return true;
419
0
        else if (str == "null")
420
0
          return Var();
421
422
0
        bool isNumber = false;
423
0
        bool isSigned = false;
424
0
        int separators = 0;
425
0
        int frac = 0;
426
0
        int index = 0;
427
0
        size_t size = str.size();
428
0
        for (size_t i = 0; i < size ; ++i)
429
0
        {
430
0
          int ch = str[i];
431
0
          if ((ch == '-' || ch == '+') && index == 0)
432
0
          {
433
0
            if (ch == '-')
434
0
              isSigned = true;
435
0
          }
436
0
          else if (Ascii::isDigit(ch))
437
0
          {
438
0
            isNumber |= true;
439
0
          }
440
0
          else if (ch == '.' || ch == ',')
441
0
          {
442
0
            frac = ch;
443
0
            ++separators;
444
0
            if (separators > 1)
445
0
              return str;
446
0
          }
447
0
          else
448
0
            return str;
449
450
0
          ++index;
451
0
        }
452
453
0
        if (frac && isNumber)
454
0
        {
455
0
          const double number = NumberParser::parseFloat(str, frac);
456
0
          return Var(number);
457
0
        }
458
0
        else if (frac == 0 && isNumber && isSigned)
459
0
        {
460
0
          const Poco::Int64 number = NumberParser::parse64(str);
461
0
          return number;
462
0
        }
463
0
        else if (frac == 0 && isNumber && !isSigned)
464
0
        {
465
0
          const Poco::UInt64 number = NumberParser::parseUnsigned64(str);
466
0
          return number;
467
0
        }
468
469
0
        return str;
470
0
      }
471
0
    }
472
0
  }
473
0
  std::string empty;
474
0
  return empty;
475
0
}
476
477
478
Var Var::parseObject(const std::string& val, std::string::size_type& pos)
479
0
{
480
0
  poco_assert_dbg (pos < val.size() && val[pos] == '{');
481
0
  ++pos;
482
0
  skipWhiteSpace(val, pos);
483
0
  DynamicStruct aStruct;
484
0
  while (val[pos] != '}' && pos < val.size())
485
0
  {
486
0
    std::string key = parseString(val, pos);
487
0
    skipWhiteSpace(val, pos);
488
0
    if (val[pos] != ':')
489
0
      throw DataFormatException("Incorrect object, must contain: key : value pairs");
490
0
    ++pos; // skip past :
491
0
    Var value = parse(val, pos);
492
0
    aStruct.insert(key, value);
493
0
    skipWhiteSpace(val, pos);
494
0
    if (val[pos] == ',')
495
0
    {
496
0
      ++pos;
497
0
      skipWhiteSpace(val, pos);
498
0
    }
499
0
  }
500
0
  if (val[pos] != '}')
501
0
    throw DataFormatException("Unterminated object");
502
0
  ++pos;
503
0
  return aStruct;
504
0
}
505
506
507
Var Var::parseArray(const std::string& val, std::string::size_type& pos)
508
0
{
509
0
  poco_assert_dbg (pos < val.size() && val[pos] == '[');
510
0
  ++pos;
511
0
  skipWhiteSpace(val, pos);
512
0
  std::vector<Var> result;
513
0
  while (val[pos] != ']' && pos < val.size())
514
0
  {
515
0
    result.push_back(parse(val, pos));
516
0
    skipWhiteSpace(val, pos);
517
0
    if (val[pos] == ',')
518
0
    {
519
0
      ++pos;
520
0
      skipWhiteSpace(val, pos);
521
0
    }
522
0
  }
523
0
  if (val[pos] != ']')
524
0
    throw DataFormatException("Unterminated array");
525
0
  ++pos;
526
0
  return result;
527
0
}
528
529
530
std::string Var::parseString(const std::string& val, std::string::size_type& pos)
531
0
{
532
0
  poco_assert_dbg (pos < val.size());
533
0
  if (val[pos] == '"')
534
0
  {
535
0
    return parseJSONString(val, pos);
536
0
  }
537
0
  else
538
0
  {
539
0
    std::string result;
540
0
    while (pos < val.size()
541
0
      && !Poco::Ascii::isSpace(val[pos])
542
0
      && val[pos] != ','
543
0
      && val[pos] != ']'
544
0
      && val[pos] != '}')
545
0
    {
546
0
      result += val[pos++];
547
0
    }
548
0
    return result;
549
0
  }
550
0
}
551
552
553
std::string Var::parseJSONString(const std::string& val, std::string::size_type& pos)
554
0
{
555
0
  poco_assert_dbg (pos < val.size() && val[pos] == '"');
556
0
  ++pos;
557
0
  std::string result;
558
0
  bool done = false;
559
0
  while (pos < val.size() && !done)
560
0
  {
561
0
    switch (val[pos])
562
0
    {
563
0
    case '"':
564
0
      done = true;
565
0
      ++pos;
566
0
      break;
567
0
    case '\\':
568
0
      if (pos < val.size() - 1)
569
0
      {
570
0
        ++pos;
571
0
        switch (val[pos])
572
0
        {
573
0
        case 'b':
574
0
          result += '\b';
575
0
          break;
576
0
        case 'f':
577
0
          result += '\f';
578
0
          break;
579
0
        case 'n':
580
0
          result += '\n';
581
0
          break;
582
0
        case 'r':
583
0
          result += '\r';
584
0
          break;
585
0
        case 't':
586
0
          result += '\t';
587
0
          break;
588
0
        default:
589
0
          result += val[pos];
590
0
          break;
591
0
        }
592
0
      }
593
0
      else
594
0
      {
595
0
        result += val[pos];
596
0
      }
597
0
      ++pos;
598
0
      break;
599
0
    default:
600
0
      result += val[pos++];
601
0
      break;
602
0
    }
603
0
  }
604
0
  if (!done) throw Poco::DataFormatException("unterminated JSON string");
605
0
  return result;
606
0
}
607
608
609
void Var::skipWhiteSpace(const std::string& val, std::string::size_type& pos)
610
0
{
611
0
  poco_assert_dbg (pos < val.size());
612
0
  while (std::isspace(val[pos]) && pos < val.size())
613
0
    ++pos;
614
0
}
615
616
617
std::string Var::toString(const Var& any)
618
0
{
619
0
  std::string res;
620
0
  Impl::appendJSONValue(res, any);
621
0
  return res;
622
0
}
623
624
} // namespace Poco::Dynamic
625