• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bblanchon / ArduinoJson / 14404559249

11 Apr 2025 01:43PM UTC coverage: 99.31% (+0.001%) from 99.309%
14404559249

push

github

bblanchon
Set version to 7.4.1

4028 of 4056 relevant lines covered (99.31%)

10739.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.07
/src/ArduinoJson/Variant/VariantData.hpp
1
// ArduinoJson - https://arduinojson.org
2
// Copyright © 2014-2025, Benoit BLANCHON
3
// MIT License
4

5
#pragma once
6

7
#include <ArduinoJson/Memory/MemoryPool.hpp>
8
#include <ArduinoJson/Memory/StringNode.hpp>
9
#include <ArduinoJson/Misc/SerializedValue.hpp>
10
#include <ArduinoJson/Numbers/convertNumber.hpp>
11
#include <ArduinoJson/Strings/JsonString.hpp>
12
#include <ArduinoJson/Strings/StringAdapters.hpp>
13
#include <ArduinoJson/Variant/VariantContent.hpp>
14

15
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
16

17
template <typename T>
18
T parseNumber(const char* s);
19

20
template <typename T>
21
static bool isTinyString(const T& s, size_t n) {
2,055✔
22
  if (n > tinyStringMaxLength)
2,055✔
23
    return false;
1,762✔
24
  bool containsNul = false;
293✔
25
  for (uint8_t i = 0; i < uint8_t(n); i++)
938✔
26
    containsNul |= !s[i];
645✔
27
  return !containsNul;
293✔
28
}
29

30
class VariantData {
31
  VariantContent content_;  // must be first to allow cast from array to variant
32
  VariantType type_;
33
  SlotId next_;
34

35
 public:
36
  // Placement new
37
  static void* operator new(size_t, void* p) noexcept {
74,804✔
38
    return p;
74,804✔
39
  }
40

41
  static void operator delete(void*, void*) noexcept {}
42

43
  VariantData() : type_(VariantType::Null), next_(NULL_SLOT) {}
76,875✔
44

45
  SlotId next() const {
292,507✔
46
    return next_;
292,507✔
47
  }
48

49
  void setNext(SlotId slot) {
71,930✔
50
    next_ = slot;
71,930✔
51
  }
71,930✔
52

53
  template <typename TVisitor>
54
  typename TVisitor::result_type accept(
139,347✔
55
      TVisitor& visit, const ResourceManager* resources) const {
56
#if ARDUINOJSON_USE_EXTENSIONS
57
    auto extension = getExtension(resources);
139,347✔
58
#else
59
    (void)resources;  // silence warning
60
#endif
61
    switch (type_) {
139,347✔
62
      case VariantType::Float:
121✔
63
        return visit.visit(content_.asFloat);
121✔
64

65
#if ARDUINOJSON_USE_DOUBLE
66
      case VariantType::Double:
16✔
67
        return visit.visit(extension->asDouble);
16✔
68
#endif
69

70
      case VariantType::Array:
983✔
71
        return visit.visit(content_.asArray);
983✔
72

73
      case VariantType::Object:
2,679✔
74
        return visit.visit(content_.asObject);
2,679✔
75

76
      case VariantType::TinyString:
142✔
77
        return visit.visit(JsonString(content_.asTinyString));
142✔
78

79
      case VariantType::LinkedString:
1,158✔
80
        return visit.visit(JsonString(content_.asLinkedString, true));
1,158✔
81

82
      case VariantType::OwnedString:
829✔
83
        return visit.visit(JsonString(content_.asOwnedString->data,
829✔
84
                                      content_.asOwnedString->length));
829✔
85

86
      case VariantType::RawString:
129✔
87
        return visit.visit(RawString(content_.asOwnedString->data,
129✔
88
                                     content_.asOwnedString->length));
129✔
89

90
      case VariantType::Int32:
1,185✔
91
        return visit.visit(static_cast<JsonInteger>(content_.asInt32));
1,185✔
92

93
      case VariantType::Uint32:
256✔
94
        return visit.visit(static_cast<JsonUInt>(content_.asUint32));
256✔
95

96
#if ARDUINOJSON_USE_LONG_LONG
97
      case VariantType::Int64:
7✔
98
        return visit.visit(extension->asInt64);
7✔
99

100
      case VariantType::Uint64:
8✔
101
        return visit.visit(extension->asUint64);
8✔
102
#endif
103

104
      case VariantType::Boolean:
607✔
105
        return visit.visit(content_.asBoolean != 0);
607✔
106

107
      default:
131,227✔
108
        return visit.visit(nullptr);
131,227✔
109
    }
110
  }
111

112
  template <typename TVisitor>
113
  static typename TVisitor::result_type accept(const VariantData* var,
728✔
114
                                               const ResourceManager* resources,
115
                                               TVisitor& visit) {
116
    if (var != 0)
728✔
117
      return var->accept(visit, resources);
725✔
118
    else
119
      return visit.visit(nullptr);
3✔
120
  }
121

122
  VariantData* addElement(ResourceManager* resources) {
159✔
123
    auto array = isNull() ? &toArray() : asArray();
159✔
124
    return detail::ArrayData::addElement(array, resources);
159✔
125
  }
126

127
  static VariantData* addElement(VariantData* var, ResourceManager* resources) {
41✔
128
    if (!var)
41✔
129
      return nullptr;
6✔
130
    return var->addElement(resources);
35✔
131
  }
132

133
  template <typename T>
134
  bool addValue(const T& value, ResourceManager* resources) {
133✔
135
    auto array = isNull() ? &toArray() : asArray();
133✔
136
    return detail::ArrayData::addValue(array, value, resources);
133✔
137
  }
138

139
  template <typename T>
140
  static bool addValue(VariantData* var, const T& value,
49✔
141
                       ResourceManager* resources) {
142
    if (!var)
49✔
143
      return false;
6✔
144
    return var->addValue(value, resources);
43✔
145
  }
146

147
  bool asBoolean(const ResourceManager* resources) const {
555✔
148
#if ARDUINOJSON_USE_EXTENSIONS
149
    auto extension = getExtension(resources);
555✔
150
#else
151
    (void)resources;  // silence warning
152
#endif
153
    switch (type_) {
555✔
154
      case VariantType::Boolean:
305✔
155
        return content_.asBoolean;
305✔
156
      case VariantType::Uint32:
6✔
157
      case VariantType::Int32:
158
        return content_.asUint32 != 0;
6✔
159
      case VariantType::Float:
2✔
160
        return content_.asFloat != 0;
2✔
161
#if ARDUINOJSON_USE_DOUBLE
162
      case VariantType::Double:
4✔
163
        return extension->asDouble != 0;
4✔
164
#endif
165
      case VariantType::Null:
6✔
166
        return false;
6✔
167
#if ARDUINOJSON_USE_LONG_LONG
168
      case VariantType::Uint64:
2✔
169
      case VariantType::Int64:
170
        return extension->asUint64 != 0;
2✔
171
#endif
172
      default:
230✔
173
        return true;
230✔
174
    }
175
  }
176

177
  ArrayData* asArray() {
778✔
178
    return isArray() ? &content_.asArray : 0;
778✔
179
  }
180

181
  const ArrayData* asArray() const {
498✔
182
    return const_cast<VariantData*>(this)->asArray();
498✔
183
  }
184

185
  CollectionData* asCollection() {
69,475✔
186
    return isCollection() ? &content_.asCollection : 0;
69,475✔
187
  }
188

189
  const CollectionData* asCollection() const {
44✔
190
    return const_cast<VariantData*>(this)->asCollection();
44✔
191
  }
192

193
  template <typename T>
194
  T asFloat(const ResourceManager* resources) const {
54✔
195
    static_assert(is_floating_point<T>::value, "T must be a floating point");
196
#if ARDUINOJSON_USE_EXTENSIONS
197
    auto extension = getExtension(resources);
54✔
198
#else
199
    (void)resources;  // silence warning
200
#endif
201
    const char* str = nullptr;
54✔
202
    switch (type_) {
54✔
203
      case VariantType::Boolean:
2✔
204
        return static_cast<T>(content_.asBoolean);
2✔
205
      case VariantType::Uint32:
2✔
206
        return static_cast<T>(content_.asUint32);
2✔
207
      case VariantType::Int32:
4✔
208
        return static_cast<T>(content_.asInt32);
4✔
209
#if ARDUINOJSON_USE_LONG_LONG
210
      case VariantType::Uint64:
1✔
211
        return static_cast<T>(extension->asUint64);
1✔
212
      case VariantType::Int64:
1✔
213
        return static_cast<T>(extension->asInt64);
1✔
214
#endif
215
      case VariantType::TinyString:
1✔
216
        str = content_.asTinyString;
1✔
217
        break;
1✔
218
      case VariantType::LinkedString:
1✔
219
        str = content_.asLinkedString;
1✔
220
        break;
1✔
221
      case VariantType::OwnedString:
1✔
222
        str = content_.asOwnedString->data;
1✔
223
        break;
1✔
224
      case VariantType::Float:
18✔
225
        return static_cast<T>(content_.asFloat);
18✔
226
#if ARDUINOJSON_USE_DOUBLE
227
      case VariantType::Double:
21✔
228
        return static_cast<T>(extension->asDouble);
21✔
229
#endif
230
      default:
2✔
231
        return 0.0;
2✔
232
    }
233

234
    ARDUINOJSON_ASSERT(str != nullptr);
235
    return parseNumber<T>(str);
3✔
236
  }
237

238
  template <typename T>
239
  T asIntegral(const ResourceManager* resources) const {
198✔
240
    static_assert(is_integral<T>::value, "T must be an integral type");
241
#if ARDUINOJSON_USE_EXTENSIONS
242
    auto extension = getExtension(resources);
198✔
243
#else
244
    (void)resources;  // silence warning
245
#endif
246
    const char* str = nullptr;
198✔
247
    switch (type_) {
198✔
248
      case VariantType::Boolean:
2✔
249
        return content_.asBoolean;
2✔
250
      case VariantType::Uint32:
66✔
251
        return convertNumber<T>(content_.asUint32);
66✔
252
      case VariantType::Int32:
83✔
253
        return convertNumber<T>(content_.asInt32);
83✔
254
#if ARDUINOJSON_USE_LONG_LONG
255
      case VariantType::Uint64:
10✔
256
        return convertNumber<T>(extension->asUint64);
10✔
257
      case VariantType::Int64:
11✔
258
        return convertNumber<T>(extension->asInt64);
11✔
259
#endif
260
      case VariantType::TinyString:
2✔
261
        str = content_.asTinyString;
2✔
262
        break;
2✔
263
      case VariantType::LinkedString:
6✔
264
        str = content_.asLinkedString;
6✔
265
        break;
6✔
266
      case VariantType::OwnedString:
1✔
267
        str = content_.asOwnedString->data;
1✔
268
        break;
1✔
269
      case VariantType::Float:
5✔
270
        return convertNumber<T>(content_.asFloat);
5✔
271
#if ARDUINOJSON_USE_DOUBLE
272
      case VariantType::Double:
10✔
273
        return convertNumber<T>(extension->asDouble);
10✔
274
#endif
275
      default:
2✔
276
        return 0;
2✔
277
    }
278

279
    ARDUINOJSON_ASSERT(str != nullptr);
280
    return parseNumber<T>(str);
9✔
281
  }
282

283
  ObjectData* asObject() {
2,942✔
284
    return isObject() ? &content_.asObject : 0;
2,942✔
285
  }
286

287
  const ObjectData* asObject() const {
2,233✔
288
    return const_cast<VariantData*>(this)->asObject();
2,233✔
289
  }
290

291
  JsonString asRawString() const {
46✔
292
    switch (type_) {
46✔
293
      case VariantType::RawString:
26✔
294
        return JsonString(content_.asOwnedString->data,
26✔
295
                          content_.asOwnedString->length);
26✔
296
      default:
20✔
297
        return JsonString();
20✔
298
    }
299
  }
300

301
  JsonString asString() const {
12,771✔
302
    switch (type_) {
12,771✔
303
      case VariantType::TinyString:
800✔
304
        return JsonString(content_.asTinyString);
800✔
305
      case VariantType::LinkedString:
4,663✔
306
        return JsonString(content_.asLinkedString, true);
4,663✔
307
      case VariantType::OwnedString:
6,876✔
308
        return JsonString(content_.asOwnedString->data,
6,876✔
309
                          content_.asOwnedString->length);
6,876✔
310
      default:
432✔
311
        return JsonString();
432✔
312
    }
313
  }
314

315
#if ARDUINOJSON_USE_EXTENSIONS
316
  const VariantExtension* getExtension(const ResourceManager* resources) const;
317
#endif
318

319
  VariantData* getElement(size_t index,
465✔
320
                          const ResourceManager* resources) const {
321
    return ArrayData::getElement(asArray(), index, resources);
465✔
322
  }
323

324
  static VariantData* getElement(const VariantData* var, size_t index,
468✔
325
                                 const ResourceManager* resources) {
326
    return var != 0 ? var->getElement(index, resources) : 0;
468✔
327
  }
328

329
  template <typename TAdaptedString>
330
  VariantData* getMember(TAdaptedString key,
2,199✔
331
                         const ResourceManager* resources) const {
332
    return ObjectData::getMember(asObject(), key, resources);
2,199✔
333
  }
334

335
  template <typename TAdaptedString>
336
  static VariantData* getMember(const VariantData* var, TAdaptedString key,
2,187✔
337
                                const ResourceManager* resources) {
338
    if (!var)
2,187✔
339
      return 0;
13✔
340
    return var->getMember(key, resources);
2,174✔
341
  }
342

343
  VariantData* getOrAddElement(size_t index, ResourceManager* resources) {
152✔
344
    auto array = isNull() ? &toArray() : asArray();
152✔
345
    if (!array)
152✔
346
      return nullptr;
1✔
347
    return array->getOrAddElement(index, resources);
151✔
348
  }
349

350
  template <typename TAdaptedString>
351
  VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources) {
960✔
352
    if (key.isNull())
960✔
353
      return nullptr;
1✔
354
    auto obj = isNull() ? &toObject() : asObject();
959✔
355
    if (!obj)
959✔
356
      return nullptr;
×
357
    return obj->getOrAddMember(key, resources);
959✔
358
  }
359

360
  bool isArray() const {
897✔
361
    return type_ == VariantType::Array;
897✔
362
  }
363

364
  bool isBoolean() const {
35✔
365
    return type_ == VariantType::Boolean;
35✔
366
  }
367

368
  bool isCollection() const {
69,475✔
369
    return type_ & VariantTypeBits::CollectionMask;
69,475✔
370
  }
371

372
  bool isFloat() const {
405✔
373
    return type_ & VariantTypeBits::NumberBit;
405✔
374
  }
375

376
  template <typename T>
377
  bool isInteger(const ResourceManager* resources) const {
151✔
378
#if ARDUINOJSON_USE_LONG_LONG
379
    auto extension = getExtension(resources);
148✔
380
#else
381
    (void)resources;  // silence warning
382
#endif
383
    switch (type_) {
151✔
384
      case VariantType::Uint32:
11✔
385
        return canConvertNumber<T>(content_.asUint32);
11✔
386

387
      case VariantType::Int32:
65✔
388
        return canConvertNumber<T>(content_.asInt32);
65✔
389

390
#if ARDUINOJSON_USE_LONG_LONG
391
      case VariantType::Uint64:
3✔
392
        return canConvertNumber<T>(extension->asUint64);
3✔
393

394
      case VariantType::Int64:
4✔
395
        return canConvertNumber<T>(extension->asInt64);
4✔
396
#endif
397

398
      default:
68✔
399
        return false;
68✔
400
    }
401
  }
402

403
  bool isNull() const {
1,957✔
404
    return type_ == VariantType::Null;
1,957✔
405
  }
406

407
  static bool isNull(const VariantData* var) {
1,288✔
408
    if (!var)
1,288✔
409
      return true;
737✔
410
    return var->isNull();
551✔
411
  }
412

413
  bool isObject() const {
3,330✔
414
    return type_ == VariantType::Object;
3,330✔
415
  }
416

417
  bool isString() const {
80✔
418
    return type_ == VariantType::LinkedString ||
136✔
419
           type_ == VariantType::OwnedString ||
123✔
420
           type_ == VariantType::TinyString;
123✔
421
  }
422

423
  size_t nesting(const ResourceManager* resources) const {
44✔
424
    auto collection = asCollection();
44✔
425
    if (collection)
44✔
426
      return collection->nesting(resources);
30✔
427
    else
428
      return 0;
14✔
429
  }
430

431
  static size_t nesting(const VariantData* var,
28✔
432
                        const ResourceManager* resources) {
433
    if (!var)
28✔
434
      return 0;
6✔
435
    return var->nesting(resources);
22✔
436
  }
437

438
  void removeElement(size_t index, ResourceManager* resources) {
9✔
439
    ArrayData::removeElement(asArray(), index, resources);
9✔
440
  }
9✔
441

442
  static void removeElement(VariantData* var, size_t index,
10✔
443
                            ResourceManager* resources) {
444
    if (!var)
10✔
445
      return;
1✔
446
    var->removeElement(index, resources);
9✔
447
  }
448

449
  template <typename TAdaptedString>
450
  void removeMember(TAdaptedString key, ResourceManager* resources) {
16✔
451
    ObjectData::removeMember(asObject(), key, resources);
16✔
452
  }
16✔
453

454
  template <typename TAdaptedString>
455
  static void removeMember(VariantData* var, TAdaptedString key,
17✔
456
                           ResourceManager* resources) {
457
    if (!var)
17✔
458
      return;
1✔
459
    var->removeMember(key, resources);
16✔
460
  }
461

462
  void reset() {  // TODO: remove
1,950✔
463
    type_ = VariantType::Null;
1,950✔
464
  }
1,950✔
465

466
  void setBoolean(bool value) {
330✔
467
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
468
    type_ = VariantType::Boolean;
330✔
469
    content_.asBoolean = value;
330✔
470
  }
330✔
471

472
  template <typename T>
473
  enable_if_t<sizeof(T) == 4, bool> setFloat(T value, ResourceManager*) {
89✔
474
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
475
    type_ = VariantType::Float;
89✔
476
    content_.asFloat = value;
89✔
477
    return true;
89✔
478
  }
479

480
  template <typename T>
481
  enable_if_t<sizeof(T) == 8, bool> setFloat(T value, ResourceManager*);
482

483
  template <typename T>
484
  enable_if_t<is_signed<T>::value, bool> setInteger(T value,
485
                                                    ResourceManager* resources);
486

487
  template <typename T>
488
  enable_if_t<is_unsigned<T>::value, bool> setInteger(
489
      T value, ResourceManager* resources);
490

491
  void setRawString(StringNode* s) {
87✔
492
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
493
    ARDUINOJSON_ASSERT(s);
494
    type_ = VariantType::RawString;
87✔
495
    content_.asOwnedString = s;
87✔
496
  }
87✔
497

498
  template <typename T>
499
  void setRawString(SerializedValue<T> value, ResourceManager* resources);
500

501
  template <typename T>
502
  static void setRawString(VariantData* var, SerializedValue<T> value,
36✔
503
                           ResourceManager* resources) {
504
    if (!var)
36✔
505
      return;
2✔
506
    var->clear(resources);
34✔
507
    var->setRawString(value, resources);
34✔
508
  }
509

510
  template <typename TAdaptedString>
511
  bool setString(TAdaptedString value, ResourceManager* resources);
512

513
  template <typename TAdaptedString>
514
  static void setString(VariantData* var, TAdaptedString value,
66,136✔
515
                        ResourceManager* resources) {
516
    if (!var)
66,136✔
517
      return;
5✔
518
    var->clear(resources);
66,131✔
519
    var->setString(value, resources);
66,131✔
520
  }
521

522
  void setLinkedString(const char* s) {
1,165✔
523
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
524
    ARDUINOJSON_ASSERT(s);
525
    type_ = VariantType::LinkedString;
1,165✔
526
    content_.asLinkedString = s;
1,165✔
527
  }
1,165✔
528

529
  template <typename TAdaptedString>
530
  void setTinyString(const TAdaptedString& s) {
280✔
531
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
532
    ARDUINOJSON_ASSERT(s.size() <= tinyStringMaxLength);
533

534
    type_ = VariantType::TinyString;
280✔
535

536
    auto n = uint8_t(s.size());
280✔
537
    for (uint8_t i = 0; i < n; i++) {
886✔
538
      char c = s[i];
606✔
539
      ARDUINOJSON_ASSERT(c != 0);  // no NUL in tiny string
540
      content_.asTinyString[i] = c;
606✔
541
    }
542

543
    content_.asTinyString[n] = 0;
280✔
544
  }
280✔
545

546
  void setOwnedString(StringNode* s) {
1,763✔
547
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
548
    ARDUINOJSON_ASSERT(s);
549
    type_ = VariantType::OwnedString;
1,763✔
550
    content_.asOwnedString = s;
1,763✔
551
  }
1,763✔
552

553
  size_t size(const ResourceManager* resources) const {
43✔
554
    if (isObject())
43✔
555
      return content_.asObject.size(resources);
14✔
556

557
    if (isArray())
29✔
558
      return content_.asArray.size(resources);
16✔
559

560
    return 0;
13✔
561
  }
562

563
  static size_t size(const VariantData* var, const ResourceManager* resources) {
30✔
564
    return var != 0 ? var->size(resources) : 0;
30✔
565
  }
566

567
  ArrayData& toArray() {
1,270✔
568
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
569
    type_ = VariantType::Array;
1,270✔
570
    new (&content_.asArray) ArrayData();
2,540✔
571
    return content_.asArray;
1,270✔
572
  }
573

574
  static ArrayData* toArray(VariantData* var, ResourceManager* resources) {
256✔
575
    if (!var)
256✔
576
      return 0;
×
577
    var->clear(resources);
256✔
578
    return &var->toArray();
256✔
579
  }
580

581
  ObjectData& toObject() {
1,348✔
582
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
583
    type_ = VariantType::Object;
1,348✔
584
    new (&content_.asObject) ObjectData();
2,696✔
585
    return content_.asObject;
1,348✔
586
  }
587

588
  static ObjectData* toObject(VariantData* var, ResourceManager* resources) {
319✔
589
    if (!var)
319✔
590
      return 0;
×
591
    var->clear(resources);
319✔
592
    return &var->toObject();
319✔
593
  }
594

595
  VariantType type() const {
5✔
596
    return type_;
5✔
597
  }
598

599
  // Release the resources used by this variant and set it to null.
600
  void clear(ResourceManager* resources);
601

602
  static void clear(VariantData* var, ResourceManager* resources) {
542✔
603
    if (!var)
542✔
604
      return;
1✔
605
    var->clear(resources);
541✔
606
  }
607
};
608

609
ARDUINOJSON_END_PRIVATE_NAMESPACE
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc