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

bblanchon / ArduinoJson / 15844355798

24 Jun 2025 07:39AM UTC coverage: 99.272%. Remained the same
15844355798

push

github

bblanchon
JsonObject: replace `ObjectData*` member with `VariantData*`

28 of 28 new or added lines in 3 files covered. (100.0%)

3 existing lines in 1 file now uncovered.

4090 of 4120 relevant lines covered (99.27%)

10837.89 hits per line

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

99.09
/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,057✔
22
  if (n > tinyStringMaxLength)
2,057✔
23
    return false;
1,765✔
24
  bool containsNul = false;
292✔
25
  for (uint8_t i = 0; i < uint8_t(n); i++)
934✔
26
    containsNul |= !s[i];
642✔
27
  return !containsNul;
292✔
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,877✔
44

45
  SlotId next() const {
292,498✔
46
    return next_;
292,498✔
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,350✔
55
      TVisitor& visit, const ResourceManager* resources) const {
56
#if ARDUINOJSON_USE_8_BYTE_POOL
57
    auto eightByteValue = getEightByte(resources);
139,350✔
58
#else
59
    (void)resources;  // silence warning
60
#endif
61
    switch (type_) {
139,350✔
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(eightByteValue->asDouble);
16✔
68
#endif
69

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

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

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

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

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

86
      case VariantType::RawString:
129✔
87
        return visit.visit(RawString(content_.asOwnedString->data,
258✔
88
                                     content_.asOwnedString->length));
258✔
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(eightByteValue->asInt64);
7✔
99

100
      case VariantType::Uint64:
8✔
101
        return visit.visit(eightByteValue->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,
730✔
114
                                               const ResourceManager* resources,
115
                                               TVisitor& visit) {
116
    if (var != 0)
730✔
117
      return var->accept(visit, resources);
727✔
118
    else
119
      return visit.visit(nullptr);
3✔
120
  }
121

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

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

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

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

147
  bool asBoolean(const ResourceManager* resources) const {
555✔
148
#if ARDUINOJSON_USE_8_BYTE_POOL
149
    auto eightByteValue = getEightByte(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 eightByteValue->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 eightByteValue->asUint64 != 0;
2✔
171
#endif
172
      default:
230✔
173
        return true;
230✔
174
    }
175
  }
176

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

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

185
  static ArrayData* asArray(VariantData* var) {
29✔
186
    return var ? var->asArray() : 0;
29✔
187
  }
188

189
  static const ArrayData* asArray(const VariantData* var) {
545✔
190
    return var ? var->asArray() : 0;
545✔
191
  }
192

193
  CollectionData* asCollection() {
69,476✔
194
    return isCollection() ? &content_.asCollection : 0;
69,476✔
195
  }
196

197
  const CollectionData* asCollection() const {
44✔
198
    return const_cast<VariantData*>(this)->asCollection();
44✔
199
  }
200

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

242
    ARDUINOJSON_ASSERT(str != nullptr);
243
    return parseNumber<T>(str);
3✔
244
  }
245

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

287
    ARDUINOJSON_ASSERT(str != nullptr);
288
    return parseNumber<T>(str);
9✔
289
  }
290

291
  ObjectData* asObject() {
3,610✔
292
    return isObject() ? &content_.asObject : 0;
3,610✔
293
  }
294

295
  const ObjectData* asObject() const {
2,939✔
296
    return const_cast<VariantData*>(this)->asObject();
2,939✔
297
  }
298

299
  static ObjectData* asObject(VariantData* var) {
56✔
300
    return var ? var->asObject() : 0;
56✔
301
  }
302

303
  static const ObjectData* asObject(const VariantData* var) {
291✔
304
    return var ? var->asObject() : 0;
291✔
305
  }
306

307
  JsonString asRawString() const {
46✔
308
    switch (type_) {
46✔
309
      case VariantType::RawString:
26✔
310
        return JsonString(content_.asOwnedString->data,
26✔
311
                          content_.asOwnedString->length);
26✔
312
      default:
20✔
313
        return JsonString();
20✔
314
    }
315
  }
316

317
  const char* asLinkedString(const ResourceManager* resources) const;
318

319
  JsonString asString(const ResourceManager* resources) const {
12,771✔
320
    switch (type_) {
12,771✔
321
      case VariantType::TinyString:
798✔
322
        return JsonString(content_.asTinyString);
798✔
323
      case VariantType::LinkedString:
4,663✔
324
        return JsonString(asLinkedString(resources), true);
4,663✔
325
      case VariantType::OwnedString:
6,877✔
326
        return JsonString(content_.asOwnedString->data,
6,877✔
327
                          content_.asOwnedString->length);
6,877✔
328
      default:
433✔
329
        return JsonString();
433✔
330
    }
331
  }
332

333
#if ARDUINOJSON_USE_8_BYTE_POOL
334
  const EightByteValue* getEightByte(const ResourceManager* resources) const;
335
#endif
336

337
  VariantData* getElement(size_t index,
472✔
338
                          const ResourceManager* resources) const {
339
    return ArrayData::getElement(asArray(), index, resources);
472✔
340
  }
341

342
  static VariantData* getElement(const VariantData* var, size_t index,
475✔
343
                                 const ResourceManager* resources) {
344
    return var != 0 ? var->getElement(index, resources) : 0;
475✔
345
  }
346

347
  template <typename TAdaptedString>
348
  VariantData* getMember(TAdaptedString key,
2,649✔
349
                         const ResourceManager* resources) const {
350
    return ObjectData::getMember(asObject(), key, resources);
2,649✔
351
  }
352

353
  template <typename TAdaptedString>
354
  static VariantData* getMember(const VariantData* var, TAdaptedString key,
2,638✔
355
                                const ResourceManager* resources) {
356
    if (!var)
2,638✔
357
      return 0;
13✔
358
    return var->getMember(key, resources);
2,625✔
359
  }
360

361
  VariantData* getOrAddElement(size_t index, ResourceManager* resources) {
152✔
362
    auto array = isNull() ? &toArray() : asArray();
152✔
363
    if (!array)
152✔
364
      return nullptr;
1✔
365
    return array->getOrAddElement(index, resources);
151✔
366
  }
367

368
  template <typename TAdaptedString>
369
  VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources) {
959✔
370
    if (key.isNull())
959✔
371
      return nullptr;
1✔
372
    auto obj = isNull() ? &toObject() : asObject();
958✔
373
    if (!obj)
958✔
UNCOV
374
      return nullptr;
×
375
    return obj->getOrAddMember(key, resources);
958✔
376
  }
377

378
  bool isArray() const {
68,848✔
379
    return type_ == VariantType::Array;
68,848✔
380
  }
381

382
  bool isBoolean() const {
35✔
383
    return type_ == VariantType::Boolean;
35✔
384
  }
385

386
  bool isCollection() const {
69,476✔
387
    return type_ & VariantTypeBits::CollectionMask;
69,476✔
388
  }
389

390
  bool isFloat() const {
406✔
391
    return type_ & VariantTypeBits::NumberBit;
406✔
392
  }
393

394
  template <typename T>
395
  bool isInteger(const ResourceManager* resources) const {
151✔
396
#if ARDUINOJSON_USE_LONG_LONG
397
    auto eightByteValue = getEightByte(resources);
148✔
398
#else
399
    (void)resources;  // silence warning
400
#endif
401
    switch (type_) {
151✔
402
      case VariantType::Uint32:
11✔
403
        return canConvertNumber<T>(content_.asUint32);
11✔
404

405
      case VariantType::Int32:
65✔
406
        return canConvertNumber<T>(content_.asInt32);
65✔
407

408
#if ARDUINOJSON_USE_LONG_LONG
409
      case VariantType::Uint64:
3✔
410
        return canConvertNumber<T>(eightByteValue->asUint64);
3✔
411

412
      case VariantType::Int64:
4✔
413
        return canConvertNumber<T>(eightByteValue->asInt64);
4✔
414
#endif
415

416
      default:
68✔
417
        return false;
68✔
418
    }
419
  }
420

421
  bool isNull() const {
68,570✔
422
    return type_ == VariantType::Null;
68,570✔
423
  }
424

425
  static bool isNull(const VariantData* var) {
1,290✔
426
    if (!var)
1,290✔
427
      return true;
737✔
428
    return var->isNull();
553✔
429
  }
430

431
  bool isObject() const {
5,097✔
432
    return type_ == VariantType::Object;
5,097✔
433
  }
434

435
  bool isString() const {
80✔
436
    return type_ == VariantType::LinkedString ||
136✔
437
           type_ == VariantType::OwnedString ||
123✔
438
           type_ == VariantType::TinyString;
123✔
439
  }
440

441
  size_t nesting(const ResourceManager* resources) const {
44✔
442
    auto collection = asCollection();
44✔
443
    if (collection)
44✔
444
      return collection->nesting(resources);
30✔
445
    else
446
      return 0;
14✔
447
  }
448

449
  static size_t nesting(const VariantData* var,
28✔
450
                        const ResourceManager* resources) {
451
    if (!var)
28✔
452
      return 0;
6✔
453
    return var->nesting(resources);
22✔
454
  }
455

456
  void removeElement(size_t index, ResourceManager* resources) {
14✔
457
    ArrayData::removeElement(asArray(), index, resources);
14✔
458
  }
14✔
459

460
  static void removeElement(VariantData* var, size_t index,
16✔
461
                            ResourceManager* resources) {
462
    if (!var)
16✔
463
      return;
2✔
464
    var->removeElement(index, resources);
14✔
465
  }
466

467
  template <typename TAdaptedString>
468
  void removeMember(TAdaptedString key, ResourceManager* resources) {
29✔
469
    ObjectData::removeMember(asObject(), key, resources);
29✔
470
  }
29✔
471

472
  template <typename TAdaptedString>
473
  static void removeMember(VariantData* var, TAdaptedString key,
31✔
474
                           ResourceManager* resources) {
475
    if (!var)
31✔
476
      return;
2✔
477
    var->removeMember(key, resources);
29✔
478
  }
479

480
  void reset() {  // TODO: remove
1,951✔
481
    type_ = VariantType::Null;
1,951✔
482
  }
1,951✔
483

484
  void setBoolean(bool value) {
330✔
485
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
486
    type_ = VariantType::Boolean;
330✔
487
    content_.asBoolean = value;
330✔
488
  }
330✔
489

490
  template <typename T>
491
  enable_if_t<sizeof(T) == 4, bool> setFloat(T value, ResourceManager*) {
90✔
492
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
493
    type_ = VariantType::Float;
90✔
494
    content_.asFloat = value;
90✔
495
    return true;
90✔
496
  }
497

498
  template <typename T>
499
  enable_if_t<sizeof(T) == 8, bool> setFloat(T value, ResourceManager*);
500

501
  template <typename T>
502
  enable_if_t<is_signed<T>::value, bool> setInteger(T value,
503
                                                    ResourceManager* resources);
504

505
  template <typename T>
506
  enable_if_t<is_unsigned<T>::value, bool> setInteger(
507
      T value, ResourceManager* resources);
508

509
  void setRawString(StringNode* s) {
87✔
510
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
511
    ARDUINOJSON_ASSERT(s);
512
    type_ = VariantType::RawString;
87✔
513
    content_.asOwnedString = s;
87✔
514
  }
87✔
515

516
  template <typename T>
517
  void setRawString(SerializedValue<T> value, ResourceManager* resources);
518

519
  template <typename T>
520
  static void setRawString(VariantData* var, SerializedValue<T> value,
36✔
521
                           ResourceManager* resources) {
522
    if (!var)
36✔
523
      return;
2✔
524
    var->clear(resources);
34✔
525
    var->setRawString(value, resources);
34✔
526
  }
527

528
  template <typename TAdaptedString>
529
  bool setString(TAdaptedString value, ResourceManager* resources);
530

531
  template <typename TAdaptedString>
532
  static void setString(VariantData* var, TAdaptedString value,
66,133✔
533
                        ResourceManager* resources) {
534
    if (!var)
66,133✔
535
      return;
5✔
536
    var->clear(resources);
66,128✔
537
    var->setString(value, resources);
66,128✔
538
  }
539

540
  bool setLinkedString(const char* s, ResourceManager* resources);
541

542
  template <typename TAdaptedString>
543
  void setTinyString(const TAdaptedString& s) {
280✔
544
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
545
    ARDUINOJSON_ASSERT(s.size() <= tinyStringMaxLength);
546

547
    type_ = VariantType::TinyString;
280✔
548

549
    auto n = uint8_t(s.size());
280✔
550
    for (uint8_t i = 0; i < n; i++) {
886✔
551
      char c = s[i];
606✔
552
      ARDUINOJSON_ASSERT(c != 0);  // no NUL in tiny string
553
      content_.asTinyString[i] = c;
606✔
554
    }
555

556
    content_.asTinyString[n] = 0;
280✔
557
  }
280✔
558

559
  void setOwnedString(StringNode* s) {
1,765✔
560
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
561
    ARDUINOJSON_ASSERT(s);
562
    type_ = VariantType::OwnedString;
1,765✔
563
    content_.asOwnedString = s;
1,765✔
564
  }
1,765✔
565

566
  size_t size(const ResourceManager* resources) const {
359✔
567
    if (isObject())
359✔
568
      return content_.asObject.size(resources);
252✔
569

570
    if (isArray())
107✔
571
      return content_.asArray.size(resources);
94✔
572

573
    return 0;
13✔
574
  }
575

576
  static size_t size(const VariantData* var, const ResourceManager* resources) {
30✔
577
    return var != 0 ? var->size(resources) : 0;
30✔
578
  }
579

580
  ArrayData& toArray() {
1,270✔
581
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
582
    type_ = VariantType::Array;
1,270✔
583
    new (&content_.asArray) ArrayData();
2,540✔
584
    return content_.asArray;
1,270✔
585
  }
586

587
  static VariantData* toArray(VariantData* var, ResourceManager* resources) {
256✔
588
    if (!var)
256✔
UNCOV
589
      return 0;
×
590
    var->clear(resources);
256✔
591
    var->toArray();
256✔
592
    return var;
256✔
593
  }
594

595
  ObjectData& toObject() {
1,348✔
596
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
597
    type_ = VariantType::Object;
1,348✔
598
    new (&content_.asObject) ObjectData();
2,696✔
599
    return content_.asObject;
1,348✔
600
  }
601

602
  static VariantData* toObject(VariantData* var, ResourceManager* resources) {
317✔
603
    if (!var)
317✔
UNCOV
604
      return 0;
×
605
    var->clear(resources);
317✔
606
    var->toObject();
317✔
607
    return var;
317✔
608
  }
609

610
  VariantType type() const {
5✔
611
    return type_;
5✔
612
  }
613

614
  // Release the resources used by this variant and set it to null.
615
  void clear(ResourceManager* resources);
616

617
  static void clear(VariantData* var, ResourceManager* resources) {
544✔
618
    if (!var)
544✔
619
      return;
1✔
620
    var->clear(resources);
543✔
621
  }
622
};
623

624
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