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

bblanchon / ArduinoJson / 15879176461

25 Jun 2025 02:29PM UTC coverage: 99.31% (+0.04%) from 99.272%
15879176461

push

github

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

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

4032 of 4060 relevant lines covered (99.31%)

10858.47 hits per line

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

99.37
/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(TVisitor& visit,
139,350✔
55
                                        ResourceManager* resources) {
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(asArray(resources));
983✔
72

73
      case VariantType::Object:
2,681✔
74
        return visit.visit(asObject(resources));
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(VariantData* var,
730✔
114
                                               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) {
159✔
123
    auto array = isNull() ? toArray(resources) : asArray(resources);
159✔
124
    return array.addElement();
318✔
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(resources) : asArray(resources);
133✔
136
    return array.addValue(value);
266✔
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_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
  ArrayImpl asArray(ResourceManager* resources) {
1,280✔
178
    return ArrayImpl(isArray() ? &content_.asCollection : nullptr, resources);
1,280✔
179
  }
180

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

187
  CollectionImpl asCollection(ResourceManager* resources) {
69,460✔
188
    return CollectionImpl(isCollection() ? &content_.asCollection : nullptr,
69,460✔
189
                          resources);
69,460✔
190
  }
191

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

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

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

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

282
  ObjectImpl asObject(ResourceManager* resources) {
5,498✔
283
    return ObjectImpl(isObject() ? &content_.asCollection : nullptr, resources);
5,498✔
284
  }
285

286
  static ObjectImpl asObject(VariantData* var, ResourceManager* resources) {
185✔
287
    return ObjectImpl(
366✔
288
        var && var->isObject() ? &var->content_.asCollection : nullptr,
181✔
289
        resources);
185✔
290
  }
291

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

302
  const char* asLinkedString(const ResourceManager* resources) const;
303

304
  JsonString asString(const ResourceManager* resources) const {
12,771✔
305
    switch (type_) {
12,771✔
306
      case VariantType::TinyString:
798✔
307
        return JsonString(content_.asTinyString);
798✔
308
      case VariantType::LinkedString:
4,663✔
309
        return JsonString(asLinkedString(resources), true);
4,663✔
310
      case VariantType::OwnedString:
6,877✔
311
        return JsonString(content_.asOwnedString->data,
6,877✔
312
                          content_.asOwnedString->length);
6,877✔
313
      default:
433✔
314
        return JsonString();
433✔
315
    }
316
  }
317

318
#if ARDUINOJSON_USE_8_BYTE_POOL
319
  const EightByteValue* getEightByte(const ResourceManager* resources) const;
320
#endif
321

322
  VariantData* getElement(size_t index, ResourceManager* resources) {
3✔
323
    return asArray(resources).getElement(index);
3✔
324
  }
325

326
  static VariantData* getElement(VariantData* var, size_t index,
75✔
327
                                 ResourceManager* resources) {
328
    if (!var)
75✔
329
      return nullptr;
3✔
330
    return var->asArray(resources).getElement(index);
72✔
331
  }
332

333
  template <typename TAdaptedString>
334
  VariantData* getMember(TAdaptedString key, ResourceManager* resources) {
2,197✔
335
    return asObject(resources).getMember(key);
2,197✔
336
  }
337

338
  template <typename TAdaptedString>
339
  static VariantData* getMember(VariantData* var, TAdaptedString key,
2,186✔
340
                                ResourceManager* resources) {
341
    if (!var)
2,186✔
342
      return 0;
13✔
343
    return var->getMember(key, resources);
2,173✔
344
  }
345

346
  VariantData* getOrAddElement(size_t index, ResourceManager* resources) {
152✔
347
    auto array = isNull() ? toArray(resources) : asArray(resources);
152✔
348
    return array.getOrAddElement(index);
304✔
349
  }
350

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

359
  bool isArray() const {
1,940✔
360
    return type_ == VariantType::Array;
1,940✔
361
  }
362

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

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

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

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

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

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

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

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

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

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

412
  bool isObject() const {
6,067✔
413
    return type_ == VariantType::Object;
6,067✔
414
  }
415

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

422
  size_t nesting(ResourceManager* resources) {
28✔
423
    return asCollection(resources).nesting();
28✔
424
  }
425

426
  static size_t nesting(VariantData* var, ResourceManager* resources) {
8✔
427
    if (!var)
8✔
428
      return 0;
2✔
429
    return var->nesting(resources);
6✔
430
  }
431

432
  void removeElement(size_t index, ResourceManager* resources) {
9✔
433
    asArray(resources).removeElement(index);
9✔
434
  }
9✔
435

436
  static void removeElement(VariantData* var, size_t index,
10✔
437
                            ResourceManager* resources) {
438
    if (!var)
10✔
439
      return;
1✔
440
    var->removeElement(index, resources);
9✔
441
  }
442

443
  template <typename TAdaptedString>
444
  void removeMember(TAdaptedString key, ResourceManager* resources) {
16✔
445
    asObject(resources).removeMember(key);
16✔
446
  }
16✔
447

448
  template <typename TAdaptedString>
449
  static void removeMember(VariantData* var, TAdaptedString key,
17✔
450
                           ResourceManager* resources) {
451
    if (!var)
17✔
452
      return;
1✔
453
    var->removeMember(key, resources);
16✔
454
  }
455

456
  void reset() {  // TODO: remove
1,951✔
457
    type_ = VariantType::Null;
1,951✔
458
  }
1,951✔
459

460
  void setBoolean(bool value) {
330✔
461
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
462
    type_ = VariantType::Boolean;
330✔
463
    content_.asBoolean = value;
330✔
464
  }
330✔
465

466
  template <typename T>
467
  enable_if_t<sizeof(T) == 4, bool> setFloat(T value, ResourceManager*) {
90✔
468
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
469
    type_ = VariantType::Float;
90✔
470
    content_.asFloat = value;
90✔
471
    return true;
90✔
472
  }
473

474
  template <typename T>
475
  enable_if_t<sizeof(T) == 8, bool> setFloat(T value, ResourceManager*);
476

477
  template <typename T>
478
  enable_if_t<is_signed<T>::value, bool> setInteger(T value,
479
                                                    ResourceManager* resources);
480

481
  template <typename T>
482
  enable_if_t<is_unsigned<T>::value, bool> setInteger(
483
      T value, ResourceManager* resources);
484

485
  void setRawString(StringNode* s) {
87✔
486
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
487
    ARDUINOJSON_ASSERT(s);
488
    type_ = VariantType::RawString;
87✔
489
    content_.asOwnedString = s;
87✔
490
  }
87✔
491

492
  template <typename T>
493
  void setRawString(SerializedValue<T> value, ResourceManager* resources);
494

495
  template <typename T>
496
  static void setRawString(VariantData* var, SerializedValue<T> value,
36✔
497
                           ResourceManager* resources) {
498
    if (!var)
36✔
499
      return;
2✔
500
    var->clear(resources);
34✔
501
    var->setRawString(value, resources);
34✔
502
  }
503

504
  template <typename TAdaptedString>
505
  bool setString(TAdaptedString value, ResourceManager* resources);
506

507
  template <typename TAdaptedString>
508
  static void setString(VariantData* var, TAdaptedString value,
66,133✔
509
                        ResourceManager* resources) {
510
    if (!var)
66,133✔
511
      return;
5✔
512
    var->clear(resources);
66,128✔
513
    var->setString(value, resources);
66,128✔
514
  }
515

516
  bool setLinkedString(const char* s, ResourceManager* resources);
517

518
  template <typename TAdaptedString>
519
  void setTinyString(const TAdaptedString& s) {
280✔
520
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
521
    ARDUINOJSON_ASSERT(s.size() <= tinyStringMaxLength);
522

523
    type_ = VariantType::TinyString;
280✔
524

525
    auto n = uint8_t(s.size());
280✔
526
    for (uint8_t i = 0; i < n; i++) {
886✔
527
      char c = s[i];
606✔
528
      ARDUINOJSON_ASSERT(c != 0);  // no NUL in tiny string
529
      content_.asTinyString[i] = c;
606✔
530
    }
531

532
    content_.asTinyString[n] = 0;
280✔
533
  }
280✔
534

535
  void setOwnedString(StringNode* s) {
1,765✔
536
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
537
    ARDUINOJSON_ASSERT(s);
538
    type_ = VariantType::OwnedString;
1,765✔
539
    content_.asOwnedString = s;
1,765✔
540
  }
1,765✔
541

542
  size_t size(ResourceManager* resources) {
43✔
543
    if (isObject())
43✔
544
      return asObject(resources).size();
14✔
545

546
    if (isArray())
29✔
547
      return asArray(resources).size();
16✔
548

549
    return 0;
13✔
550
  }
551

552
  static size_t size(VariantData* var, ResourceManager* resources) {
30✔
553
    return var != 0 ? var->size(resources) : 0;
30✔
554
  }
555

556
  ArrayImpl toArray(ResourceManager* resources) {
1,270✔
557
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
558
    type_ = VariantType::Array;
1,270✔
559
    return ArrayImpl(new (&content_.asCollection) CollectionData(), resources);
2,540✔
560
  }
561

562
  static ArrayImpl toArray(VariantData* var, ResourceManager* resources) {
256✔
563
    if (!var)
256✔
NEW
564
      return ArrayImpl(nullptr, resources);
×
565
    var->clear(resources);
256✔
566
    return var->toArray(resources);
256✔
567
  }
568

569
  ObjectImpl toObject(ResourceManager* resources) {
1,348✔
570
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
571
    type_ = VariantType::Object;
1,348✔
572
    return ObjectImpl(new (&content_.asCollection) CollectionData(), resources);
2,696✔
573
  }
574

575
  static ObjectImpl toObject(VariantData* var, ResourceManager* resources) {
317✔
576
    if (!var)
317✔
NEW
577
      return ObjectImpl();
×
578
    var->clear(resources);
317✔
579
    return var->toObject(resources);
317✔
580
  }
581

582
  VariantType type() const {
5✔
583
    return type_;
5✔
584
  }
585

586
  // Release the resources used by this variant and set it to null.
587
  void clear(ResourceManager* resources);
588

589
  static void clear(VariantData* var, ResourceManager* resources) {
544✔
590
    if (!var)
544✔
591
      return;
1✔
592
    var->clear(resources);
543✔
593
  }
594
};
595

596
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

© 2026 Coveralls, Inc