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

bblanchon / ArduinoJson / 17341271098

30 Aug 2025 07:55AM UTC coverage: 99.349% (+0.02%) from 99.332%
17341271098

push

github

bblanchon
Extract `ArrayImpl`, `CollectionImpl`, and `ObjectImpl`

278 of 282 new or added lines in 24 files covered. (98.58%)

2 existing lines in 2 files now uncovered.

3970 of 3996 relevant lines covered (99.35%)

10398.68 hits per line

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

99.34
/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) {
3,194✔
22
  if (n > tinyStringMaxLength)
3,194✔
23
    return false;
2,562✔
24
  bool containsNul = false;
632✔
25
  for (uint8_t i = 0; i < uint8_t(n); i++)
1,781✔
26
    containsNul |= !s[i];
1,149✔
27
  return !containsNul;
632✔
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,779✔
38
    return p;
74,779✔
39
  }
40

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

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

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

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

53
  template <typename TVisitor>
54
  typename TVisitor::result_type accept(TVisitor& visit,
139,340✔
55
                                        ResourceManager* resources) {
56
#if ARDUINOJSON_USE_8_BYTE_POOL
57
    auto eightByteValue = getEightByte(resources);
139,340✔
58
#else
59
    (void)resources;  // silence warning
60
#endif
61
    switch (type_) {
139,340✔
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:
982✔
71
        return visit.visit(asArray(resources));
982✔
72

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

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

79
      case VariantType::LongString:
1,783✔
80
        return visit.visit(JsonString(content_.asStringNode->data,
3,566✔
81
                                      content_.asStringNode->length));
3,566✔
82

83
      case VariantType::RawString:
129✔
84
        return visit.visit(RawString(content_.asStringNode->data,
258✔
85
                                     content_.asStringNode->length));
258✔
86

87
      case VariantType::Int32:
1,184✔
88
        return visit.visit(static_cast<JsonInteger>(content_.asInt32));
1,184✔
89

90
      case VariantType::Uint32:
256✔
91
        return visit.visit(static_cast<JsonUInt>(content_.asUint32));
256✔
92

93
#if ARDUINOJSON_USE_LONG_LONG
94
      case VariantType::Int64:
7✔
95
        return visit.visit(eightByteValue->asInt64);
7✔
96

97
      case VariantType::Uint64:
8✔
98
        return visit.visit(eightByteValue->asUint64);
8✔
99
#endif
100

101
      case VariantType::Boolean:
607✔
102
        return visit.visit(content_.asBoolean != 0);
607✔
103

104
      default:
131,227✔
105
        return visit.visit(nullptr);
131,227✔
106
    }
107
  }
108

109
  template <typename TVisitor>
110
  static typename TVisitor::result_type accept(VariantData* var,
726✔
111
                                               ResourceManager* resources,
112
                                               TVisitor& visit) {
113
    if (var != 0)
726✔
114
      return var->accept(visit, resources);
723✔
115
    else
116
      return visit.visit(nullptr);
3✔
117
  }
118

119
  VariantData* addElement(ResourceManager* resources) {
159✔
120
    auto array = isNull() ? toArray(resources) : asArray(resources);
159✔
121
    return array.addElement();
318✔
122
  }
123

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

130
  template <typename T>
131
  bool addValue(const T& value, ResourceManager* resources) {
132✔
132
    auto array = isNull() ? toArray(resources) : asArray(resources);
132✔
133
    return array.addValue(value);
264✔
134
  }
135

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

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

174
  ArrayImpl asArray(ResourceManager* resources) {
1,279✔
175
    return ArrayImpl(isArray() ? &content_.asCollection : nullptr, resources);
1,279✔
176
  }
177

178
  static ArrayImpl asArray(VariantData* var, ResourceManager* resources) {
547✔
179
    return ArrayImpl(
1,088✔
180
        var && var->isArray() ? &var->content_.asCollection : nullptr,
541✔
181
        resources);
547✔
182
  }
183

184
  CollectionImpl asCollection(ResourceManager* resources) {
69,429✔
185
    return CollectionImpl(isCollection() ? &content_.asCollection : nullptr,
69,429✔
186
                          resources);
69,429✔
187
  }
188

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

227
    ARDUINOJSON_ASSERT(str != nullptr);
228
    return parseNumber<T>(str);
3✔
229
  }
230

231
  template <typename T>
232
  T asIntegral(const ResourceManager* resources) const {
198✔
233
    static_assert(is_integral<T>::value, "T must be an integral type");
234
#if ARDUINOJSON_USE_8_BYTE_POOL
235
    auto eightByteValue = getEightByte(resources);
198✔
236
#else
237
    (void)resources;  // silence warning
238
#endif
239
    const char* str = nullptr;
198✔
240
    switch (type_) {
198✔
241
      case VariantType::Boolean:
2✔
242
        return content_.asBoolean;
2✔
243
      case VariantType::Uint32:
66✔
244
        return convertNumber<T>(content_.asUint32);
66✔
245
      case VariantType::Int32:
83✔
246
        return convertNumber<T>(content_.asInt32);
83✔
247
#if ARDUINOJSON_USE_LONG_LONG
248
      case VariantType::Uint64:
10✔
249
        return convertNumber<T>(eightByteValue->asUint64);
10✔
250
      case VariantType::Int64:
11✔
251
        return convertNumber<T>(eightByteValue->asInt64);
11✔
252
#endif
253
      case VariantType::TinyString:
3✔
254
        str = content_.asTinyString;
3✔
255
        break;
3✔
256
      case VariantType::LongString:
6✔
257
        str = content_.asStringNode->data;
6✔
258
        break;
6✔
259
      case VariantType::Float:
5✔
260
        return convertNumber<T>(content_.asFloat);
5✔
261
#if ARDUINOJSON_USE_DOUBLE
262
      case VariantType::Double:
10✔
263
        return convertNumber<T>(eightByteValue->asDouble);
10✔
264
#endif
265
      default:
2✔
266
        return 0;
2✔
267
    }
268

269
    ARDUINOJSON_ASSERT(str != nullptr);
270
    return parseNumber<T>(str);
9✔
271
  }
272

273
  ObjectImpl asObject(ResourceManager* resources) {
5,487✔
274
    return ObjectImpl(isObject() ? &content_.asCollection : nullptr, resources);
5,487✔
275
  }
276

277
  static ObjectImpl asObject(VariantData* var, ResourceManager* resources) {
184✔
278
    return ObjectImpl(
364✔
279
        var && var->isObject() ? &var->content_.asCollection : nullptr,
180✔
280
        resources);
184✔
281
  }
282

283
  JsonString asRawString() const {
46✔
284
    switch (type_) {
46✔
285
      case VariantType::RawString:
26✔
286
        return JsonString(content_.asStringNode->data,
52✔
287
                          content_.asStringNode->length);
26✔
288
      default:
20✔
289
        return JsonString();
20✔
290
    }
291
  }
292

293
  JsonString asString() const {
12,767✔
294
    switch (type_) {
12,767✔
295
      case VariantType::TinyString:
2,792✔
296
        return JsonString(content_.asTinyString);
2,792✔
297
      case VariantType::LongString:
9,545✔
298
        return JsonString(content_.asStringNode->data,
19,090✔
299
                          content_.asStringNode->length);
9,545✔
300
      default:
430✔
301
        return JsonString();
430✔
302
    }
303
  }
304

305
#if ARDUINOJSON_USE_8_BYTE_POOL
306
  const EightByteValue* getEightByte(const ResourceManager* resources) const;
307
#endif
308

309
  VariantData* getElement(size_t index, ResourceManager* resources) {
3✔
310
    return asArray(resources).getElement(index);
3✔
311
  }
312

313
  static VariantData* getElement(VariantData* var, size_t index,
75✔
314
                                 ResourceManager* resources) {
315
    if (!var)
75✔
316
      return nullptr;
3✔
317
    return var->asArray(resources).getElement(index);
72✔
318
  }
319

320
  template <typename TAdaptedString>
321
  VariantData* getMember(TAdaptedString key, ResourceManager* resources) {
2,198✔
322
    return asObject(resources).getMember(key);
2,198✔
323
  }
324

325
  template <typename TAdaptedString>
326
  static VariantData* getMember(VariantData* var, TAdaptedString key,
2,186✔
327
                                ResourceManager* resources) {
328
    if (!var)
2,186✔
329
      return 0;
13✔
330
    return var->getMember(key, resources);
2,173✔
331
  }
332

333
  VariantData* getOrAddElement(size_t index, ResourceManager* resources) {
152✔
334
    auto array = isNull() ? toArray(resources) : asArray(resources);
152✔
335
    return array.getOrAddElement(index);
304✔
336
  }
337

338
  template <typename TAdaptedString>
339
  VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources) {
947✔
340
    if (key.isNull())
947✔
341
      return nullptr;
1✔
342
    auto obj = isNull() ? toObject(resources) : asObject(resources);
946✔
343
    return obj.getOrAddMember(key);
946✔
344
  }
345

346
  bool isArray() const {
1,939✔
347
    return type_ == VariantType::Array;
1,939✔
348
  }
349

350
  bool isBoolean() const {
35✔
351
    return type_ == VariantType::Boolean;
35✔
352
  }
353

354
  bool isCollection() const {
69,429✔
355
    return type_ & VariantTypeBits::CollectionMask;
69,429✔
356
  }
357

358
  bool isFloat() const {
406✔
359
    return type_ & VariantTypeBits::NumberBit;
406✔
360
  }
361

362
  template <typename T>
363
  bool isInteger(const ResourceManager* resources) const {
151✔
364
#if ARDUINOJSON_USE_LONG_LONG
365
    auto eightByteValue = getEightByte(resources);
148✔
366
#else
367
    (void)resources;  // silence warning
368
#endif
369
    switch (type_) {
151✔
370
      case VariantType::Uint32:
11✔
371
        return canConvertNumber<T>(content_.asUint32);
11✔
372

373
      case VariantType::Int32:
65✔
374
        return canConvertNumber<T>(content_.asInt32);
65✔
375

376
#if ARDUINOJSON_USE_LONG_LONG
377
      case VariantType::Uint64:
3✔
378
        return canConvertNumber<T>(eightByteValue->asUint64);
3✔
379

380
      case VariantType::Int64:
4✔
381
        return canConvertNumber<T>(eightByteValue->asInt64);
4✔
382
#endif
383

384
      default:
68✔
385
        return false;
68✔
386
    }
387
  }
388

389
  bool isNull() const {
1,943✔
390
    return type_ == VariantType::Null;
1,943✔
391
  }
392

393
  static bool isNull(const VariantData* var) {
1,288✔
394
    if (!var)
1,288✔
395
      return true;
737✔
396
    return var->isNull();
551✔
397
  }
398

399
  bool isObject() const {
6,055✔
400
    return type_ == VariantType::Object;
6,055✔
401
  }
402

403
  bool isString() const {
80✔
404
    return type_ == VariantType::LongString || type_ == VariantType::TinyString;
80✔
405
  }
406

407
  size_t nesting(ResourceManager* resources) {
28✔
408
    return asCollection(resources).nesting();
28✔
409
  }
410

411
  static size_t nesting(VariantData* var, ResourceManager* resources) {
8✔
412
    if (!var)
8✔
413
      return 0;
2✔
414
    return var->nesting(resources);
6✔
415
  }
416

417
  void removeElement(size_t index, ResourceManager* resources) {
9✔
418
    asArray(resources).removeElement(index);
9✔
419
  }
9✔
420

421
  static void removeElement(VariantData* var, size_t index,
10✔
422
                            ResourceManager* resources) {
423
    if (!var)
10✔
424
      return;
1✔
425
    var->removeElement(index, resources);
9✔
426
  }
427

428
  template <typename TAdaptedString>
429
  void removeMember(TAdaptedString key, ResourceManager* resources) {
16✔
430
    asObject(resources).removeMember(key);
16✔
431
  }
16✔
432

433
  template <typename TAdaptedString>
434
  static void removeMember(VariantData* var, TAdaptedString key,
17✔
435
                           ResourceManager* resources) {
436
    if (!var)
17✔
437
      return;
1✔
438
    var->removeMember(key, resources);
16✔
439
  }
440

441
  void reset() {  // TODO: remove
1,937✔
442
    type_ = VariantType::Null;
1,937✔
443
  }
1,937✔
444

445
  void setBoolean(bool value) {
330✔
446
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
447
    type_ = VariantType::Boolean;
330✔
448
    content_.asBoolean = value;
330✔
449
  }
330✔
450

451
  template <typename T>
452
  enable_if_t<sizeof(T) == 4, bool> setFloat(T value, ResourceManager*) {
90✔
453
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
454
    type_ = VariantType::Float;
90✔
455
    content_.asFloat = value;
90✔
456
    return true;
90✔
457
  }
458

459
  template <typename T>
460
  enable_if_t<sizeof(T) == 8, bool> setFloat(T value, ResourceManager*);
461

462
  template <typename T>
463
  enable_if_t<is_signed<T>::value, bool> setInteger(T value,
464
                                                    ResourceManager* resources);
465

466
  template <typename T>
467
  enable_if_t<is_unsigned<T>::value, bool> setInteger(
468
      T value, ResourceManager* resources);
469

470
  void setRawString(StringNode* s) {
87✔
471
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
472
    ARDUINOJSON_ASSERT(s);
473
    type_ = VariantType::RawString;
87✔
474
    content_.asStringNode = s;
87✔
475
  }
87✔
476

477
  template <typename T>
478
  void setRawString(SerializedValue<T> value, ResourceManager* resources);
479

480
  template <typename T>
481
  static void setRawString(VariantData* var, SerializedValue<T> value,
36✔
482
                           ResourceManager* resources) {
483
    if (!var)
36✔
484
      return;
2✔
485
    var->clear(resources);
34✔
486
    var->setRawString(value, resources);
34✔
487
  }
488

489
  template <typename TAdaptedString>
490
  bool setString(TAdaptedString value, ResourceManager* resources);
491

492
  template <typename TAdaptedString>
493
  static void setString(VariantData* var, TAdaptedString value,
66,121✔
494
                        ResourceManager* resources) {
495
    if (!var)
66,121✔
496
      return;
5✔
497
    var->clear(resources);
66,116✔
498
    var->setString(value, resources);
66,116✔
499
  }
500

501
  template <typename TAdaptedString>
502
  void setTinyString(const TAdaptedString& s) {
619✔
503
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
504
    ARDUINOJSON_ASSERT(s.size() <= tinyStringMaxLength);
505

506
    type_ = VariantType::TinyString;
619✔
507

508
    auto n = uint8_t(s.size());
619✔
509
    for (uint8_t i = 0; i < n; i++) {
1,729✔
510
      char c = s[i];
1,110✔
511
      ARDUINOJSON_ASSERT(c != 0);  // no NUL in tiny string
512
      content_.asTinyString[i] = c;
1,110✔
513
    }
514

515
    content_.asTinyString[n] = 0;
619✔
516
  }
619✔
517

518
  void setLongString(StringNode* s) {
2,563✔
519
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
520
    ARDUINOJSON_ASSERT(s);
521
    type_ = VariantType::LongString;
2,563✔
522
    content_.asStringNode = s;
2,563✔
523
  }
2,563✔
524

525
  size_t size(ResourceManager* resources) {
43✔
526
    if (isObject())
43✔
527
      return asObject(resources).size();
14✔
528

529
    if (isArray())
29✔
530
      return asArray(resources).size();
16✔
531

532
    return 0;
13✔
533
  }
534

535
  static size_t size(VariantData* var, ResourceManager* resources) {
30✔
536
    return var != 0 ? var->size(resources) : 0;
30✔
537
  }
538

539
  ArrayImpl toArray(ResourceManager* resources) {
1,269✔
540
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
541
    type_ = VariantType::Array;
1,269✔
542
    return ArrayImpl(new (&content_.asCollection) CollectionData(), resources);
2,538✔
543
  }
544

545
  static ArrayImpl toArray(VariantData* var, ResourceManager* resources) {
256✔
546
    if (!var)
256✔
NEW
547
      return ArrayImpl(nullptr, resources);
×
548
    var->clear(resources);
256✔
549
    return var->toArray(resources);
256✔
550
  }
551

552
  ObjectImpl toObject(ResourceManager* resources) {
1,336✔
553
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
554
    type_ = VariantType::Object;
1,336✔
555
    return ObjectImpl(new (&content_.asCollection) CollectionData(), resources);
2,672✔
556
  }
557

558
  static ObjectImpl toObject(VariantData* var, ResourceManager* resources) {
307✔
559
    if (!var)
307✔
NEW
560
      return ObjectImpl();
×
561
    var->clear(resources);
307✔
562
    return var->toObject(resources);
307✔
563
  }
564

565
  VariantType type() const {
5✔
566
    return type_;
5✔
567
  }
568

569
  // Release the resources used by this variant and set it to null.
570
  void clear(ResourceManager* resources);
571

572
  static void clear(VariantData* var, ResourceManager* resources) {
540✔
573
    if (!var)
540✔
574
      return;
1✔
575
    var->clear(resources);
539✔
576
  }
577
};
578

579
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