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

bblanchon / ArduinoJson / 18222111010

03 Oct 2025 08:13AM UTC coverage: 99.37% (-0.001%) from 99.371%
18222111010

push

github

bblanchon
Extract `VariantImpl`

409 of 410 new or added lines in 28 files covered. (99.76%)

3 existing lines in 2 files now uncovered.

3945 of 3970 relevant lines covered (99.37%)

10359.56 hits per line

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

99.65
/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
struct VariantData {
31
  VariantContent content;  // must be first to allow cast from array to variant
32
  VariantType type = VariantType::Null;
33
  SlotId next = NULL_SLOT;
34

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

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

42
  JsonString asRawString() const {
46✔
43
    switch (type) {
46✔
44
      case VariantType::RawString:
26✔
45
        return JsonString(content.asStringNode->data,
52✔
46
                          content.asStringNode->length);
26✔
47
      default:
20✔
48
        return JsonString();
20✔
49
    }
50
  }
51

52
  JsonString asString() const {
12,767✔
53
    switch (type) {
12,767✔
54
      case VariantType::TinyString:
2,792✔
55
        return JsonString(content.asTinyString);
2,792✔
56
      case VariantType::LongString:
9,545✔
57
        return JsonString(content.asStringNode->data,
19,090✔
58
                          content.asStringNode->length);
9,545✔
59
      default:
430✔
60
        return JsonString();
430✔
61
    }
62
  }
63

64
  bool isArray() const {
90✔
65
    return type == VariantType::Array;
90✔
66
  }
67

68
  bool isBoolean() const {
35✔
69
    return type == VariantType::Boolean;
35✔
70
  }
71

72
  bool isFloat() const {
406✔
73
    return type & VariantTypeBits::NumberBit;
406✔
74
  }
75

76
  bool isNull() const {
3✔
77
    return type == VariantType::Null;
3✔
78
  }
79

80
  bool isObject() const {
345✔
81
    return type == VariantType::Object;
345✔
82
  }
83

84
  bool isString() const {
80✔
85
    return type == VariantType::LongString || type == VariantType::TinyString;
80✔
86
  }
87

88
  void setBoolean(bool value) {
330✔
89
    ARDUINOJSON_ASSERT(type == VariantType::Null);
90
    type = VariantType::Boolean;
330✔
91
    content.asBoolean = value;
330✔
92
  }
330✔
93

94
  void setRawString(StringNode* s) {
87✔
95
    ARDUINOJSON_ASSERT(type == VariantType::Null);
96
    ARDUINOJSON_ASSERT(s);
97
    type = VariantType::RawString;
87✔
98
    content.asStringNode = s;
87✔
99
  }
87✔
100

101
  template <typename TAdaptedString>
102
  void setTinyString(const TAdaptedString& s) {
619✔
103
    ARDUINOJSON_ASSERT(type == VariantType::Null);
104
    ARDUINOJSON_ASSERT(s.size() <= tinyStringMaxLength);
105

106
    type = VariantType::TinyString;
619✔
107

108
    auto n = uint8_t(s.size());
619✔
109
    for (uint8_t i = 0; i < n; i++) {
1,729✔
110
      char c = s[i];
1,110✔
111
      ARDUINOJSON_ASSERT(c != 0);  // no NUL in tiny string
112
      content.asTinyString[i] = c;
1,110✔
113
    }
114

115
    content.asTinyString[n] = 0;
619✔
116
  }
619✔
117

118
  void setLongString(StringNode* s) {
2,563✔
119
    ARDUINOJSON_ASSERT(type == VariantType::Null);
120
    ARDUINOJSON_ASSERT(s);
121
    type = VariantType::LongString;
2,563✔
122
    content.asStringNode = s;
2,563✔
123
  }
2,563✔
124

125
  CollectionData* toArray() {
1,269✔
126
    ARDUINOJSON_ASSERT(type == VariantType::Null);
127
    type = VariantType::Array;
1,269✔
128
    return new (&content.asCollection) CollectionData();
2,538✔
129
  }
130

131
  CollectionData* toObject() {
1,336✔
132
    ARDUINOJSON_ASSERT(type == VariantType::Null);
133
    type = VariantType::Object;
1,336✔
134
    return new (&content.asCollection) CollectionData();
2,672✔
135
  }
136
};
137

138
// HACK: large functions are implemented in static function to give opportunity
139
// to the compiler to optimize the `this` pointer away.
140
class VariantImpl {
141
 public:
142
  VariantImpl() : data_(nullptr), resources_(nullptr) {}
500✔
143

144
  VariantImpl(VariantData* data, ResourceManager* resources)
223,290✔
145
      : data_(data), resources_(resources) {}
223,290✔
146

147
  VariantData* getData() const {
149,178✔
148
    return data_;
149,178✔
149
  }
150

151
  ResourceManager* getResourceManager() const {
215,403✔
152
    return resources_;
215,403✔
153
  }
154

155
  template <typename TVisitor>
156
  typename TVisitor::result_type accept(TVisitor& visit) {
5,329✔
157
    return accept(visit, data_, resources_);
5,329✔
158
  }
159

160
  template <typename TVisitor>
161
  static typename TVisitor::result_type accept(TVisitor&, VariantData*,
162
                                               ResourceManager*);
163

164
  VariantData* addElement() {
165✔
165
    return addElement(data_, resources_);
165✔
166
  }
167

168
  static VariantData* addElement(VariantData* data,
165✔
169
                                 ResourceManager* resources) {
170
    if (!data)
165✔
171
      return nullptr;
6✔
172
    auto array = data->type == VariantType::Null ? toArray(data, resources)
88✔
173
                                                 : asArray(data, resources);
159✔
174
    return array.addElement();
159✔
175
  }
176

177
  template <typename T>
178
  bool addValue(const T& value) {
138✔
179
    return addValue(value, data_, resources_);
138✔
180
  }
181

182
  template <typename T>
183
  static bool addValue(const T& value, VariantData* data,
138✔
184
                       ResourceManager* resources) {
185
    if (!data)
138✔
186
      return false;
6✔
187
    auto array = data->type == VariantType::Null ? toArray(data, resources)
132✔
188
                                                 : asArray(data, resources);
57✔
189
    return array.addValue(value);
132✔
190
  }
191

192
  bool asBoolean() const {
1,254✔
193
    return asBoolean(data_, resources_);
1,254✔
194
  }
195

196
  static bool asBoolean(VariantData* data, ResourceManager* resources) {
1,254✔
197
    if (!data)
1,254✔
198
      return false;
699✔
199

200
#if ARDUINOJSON_USE_8_BYTE_POOL
201
    auto eightByteValue = getEightByte(data, resources);
555✔
202
#endif
203
    switch (data->type) {
555✔
204
      case VariantType::Boolean:
305✔
205
        return data->content.asBoolean;
305✔
206
      case VariantType::Uint32:
6✔
207
      case VariantType::Int32:
208
        return data->content.asUint32 != 0;
6✔
209
      case VariantType::Float:
2✔
210
        return data->content.asFloat != 0;
2✔
211
#if ARDUINOJSON_USE_DOUBLE
212
      case VariantType::Double:
4✔
213
        return eightByteValue->asDouble != 0;
4✔
214
#endif
215
      case VariantType::Null:
6✔
216
        return false;
6✔
217
#if ARDUINOJSON_USE_LONG_LONG
218
      case VariantType::Uint64:
2✔
219
      case VariantType::Int64:
220
        return eightByteValue->asUint64 != 0;
2✔
221
#endif
222
      default:
230✔
223
        return true;
230✔
224
    }
225
  }
226

227
  ArrayImpl asArray() {
566✔
228
    return asArray(data_, resources_);
566✔
229
  }
230

231
  static ArrayImpl asArray(VariantData* data, ResourceManager* resources) {
848✔
232
    return ArrayImpl(data && data->type == VariantType::Array
848✔
233
                         ? &data->content.asCollection
234
                         : nullptr,
235
                     resources);
848✔
236
  }
237

238
  CollectionImpl asCollection() {
30✔
239
    return CollectionImpl(
30✔
240
        isCollection() ? &data_->content.asCollection : nullptr, resources_);
60✔
241
  }
242

243
  template <typename T>
244
  T asFloat() const {
55✔
245
    return asFloat<T>(data_, resources_);
55✔
246
  }
247

248
  template <typename T>
249
  static T asFloat(VariantData* data, ResourceManager* resources) {
55✔
250
    if (!data)
55✔
251
      return 0.0;
1✔
252

253
    static_assert(is_floating_point<T>::value, "T must be a floating point");
254
#if ARDUINOJSON_USE_8_BYTE_POOL
255
    auto eightByteValue = getEightByte(data, resources);
54✔
256
#endif
257
    const char* str = nullptr;
54✔
258
    switch (data->type) {
54✔
259
      case VariantType::Boolean:
2✔
260
        return static_cast<T>(data->content.asBoolean);
2✔
261
      case VariantType::Uint32:
2✔
262
        return static_cast<T>(data->content.asUint32);
2✔
263
      case VariantType::Int32:
4✔
264
        return static_cast<T>(data->content.asInt32);
4✔
265
#if ARDUINOJSON_USE_LONG_LONG
266
      case VariantType::Uint64:
1✔
267
        return static_cast<T>(eightByteValue->asUint64);
1✔
268
      case VariantType::Int64:
1✔
269
        return static_cast<T>(eightByteValue->asInt64);
1✔
270
#endif
271
      case VariantType::TinyString:
2✔
272
        str = data->content.asTinyString;
2✔
273
        break;
2✔
274
      case VariantType::LongString:
1✔
275
        str = data->content.asStringNode->data;
1✔
276
        break;
1✔
277
      case VariantType::Float:
18✔
278
        return static_cast<T>(data->content.asFloat);
18✔
279
#if ARDUINOJSON_USE_DOUBLE
280
      case VariantType::Double:
21✔
281
        return static_cast<T>(eightByteValue->asDouble);
21✔
282
#endif
283
      default:
2✔
284
        return 0.0;
2✔
285
    }
286

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

291
  template <typename T>
292
  T asIntegral() const {
203✔
293
    return asIntegral<T>(data_, resources_);
203✔
294
  }
295

296
  template <typename T>
297
  static T asIntegral(VariantData* data, ResourceManager* resources) {
203✔
298
    if (!data)
203✔
299
      return 0;
5✔
300

301
    static_assert(is_integral<T>::value, "T must be an integral type");
302
#if ARDUINOJSON_USE_8_BYTE_POOL
303
    auto eightByteValue = getEightByte(data, resources);
198✔
304
#endif
305
    const char* str = nullptr;
198✔
306
    switch (data->type) {
198✔
307
      case VariantType::Boolean:
2✔
308
        return data->content.asBoolean;
2✔
309
      case VariantType::Uint32:
66✔
310
        return convertNumber<T>(data->content.asUint32);
66✔
311
      case VariantType::Int32:
83✔
312
        return convertNumber<T>(data->content.asInt32);
83✔
313
#if ARDUINOJSON_USE_LONG_LONG
314
      case VariantType::Uint64:
10✔
315
        return convertNumber<T>(eightByteValue->asUint64);
10✔
316
      case VariantType::Int64:
11✔
317
        return convertNumber<T>(eightByteValue->asInt64);
11✔
318
#endif
319
      case VariantType::TinyString:
3✔
320
        str = data->content.asTinyString;
3✔
321
        break;
3✔
322
      case VariantType::LongString:
6✔
323
        str = data->content.asStringNode->data;
6✔
324
        break;
6✔
325
      case VariantType::Float:
5✔
326
        return convertNumber<T>(data->content.asFloat);
5✔
327
#if ARDUINOJSON_USE_DOUBLE
328
      case VariantType::Double:
10✔
329
        return convertNumber<T>(eightByteValue->asDouble);
10✔
330
#endif
331
      default:
2✔
332
        return 0;
2✔
333
    }
334

335
    ARDUINOJSON_ASSERT(str != nullptr);
336
    return parseNumber<T>(str);
9✔
337
  }
338

339
  ObjectImpl asObject() {
2,242✔
340
    return asObject(data_, resources_);
2,242✔
341
  }
342

343
  static ObjectImpl asObject(VariantData* data, ResourceManager* resources) {
3,006✔
344
    return ObjectImpl(data && data->type == VariantType::Object
3,006✔
345
                          ? &data->content.asCollection
346
                          : nullptr,
347
                      resources);
3,006✔
348
  }
349

350
#if ARDUINOJSON_USE_8_BYTE_POOL
351
  static const EightByteValue* getEightByte(VariantData*, ResourceManager*);
352
#endif
353

354
  VariantData* getElement(size_t index) {
471✔
355
    return asArray().getElement(index);
471✔
356
  }
357

358
  template <typename TAdaptedString>
359
  VariantData* getMember(TAdaptedString key) {
2,211✔
360
    return asObject().getMember(key);
2,211✔
361
  }
362

363
  VariantData* getOrAddElement(size_t index) {
153✔
364
    auto array = isNull() ? toArray() : asArray();
153✔
365
    return array.getOrAddElement(index);
306✔
366
  }
367

368
  template <typename TAdaptedString>
369
  VariantData* getOrAddMember(TAdaptedString key) {
950✔
370
    return getOrAddMember(key, data_, resources_);
950✔
371
  }
372

373
  template <typename TAdaptedString>
374
  static VariantData* getOrAddMember(TAdaptedString key, VariantData* data,
950✔
375
                                     ResourceManager* resources) {
376
    if (key.isNull())
950✔
377
      return nullptr;
1✔
378
    if (!data)
949✔
379
      return nullptr;
3✔
380
    auto obj = data->type == VariantType::Null ? toObject(data, resources)
946✔
381
                                               : asObject(data, resources);
580✔
382
    return obj.getOrAddMember(key);
946✔
383
  }
384

385
  bool isArray() const {
32✔
386
    return type() == VariantType::Array;
32✔
387
  }
388

389
  bool isCollection() const {
30✔
390
    return type() & VariantTypeBits::CollectionMask;
30✔
391
  }
392

393
  template <typename T>
394
  bool isInteger() const {
167✔
395
    return isInteger<T>(data_, resources_);
167✔
396
  }
397

398
  template <typename T>
399
  static bool isInteger(VariantData* data, ResourceManager* resources) {
167✔
400
    if (!data)
167✔
401
      return false;
16✔
402

403
#if ARDUINOJSON_USE_LONG_LONG
404
    auto eightByteValue = getEightByte(data, resources);
148✔
405
#else
406
    (void)resources;
407
#endif
408
    switch (data->type) {
151✔
409
      case VariantType::Uint32:
11✔
410
        return canConvertNumber<T>(data->content.asUint32);
11✔
411

412
      case VariantType::Int32:
65✔
413
        return canConvertNumber<T>(data->content.asInt32);
65✔
414

415
#if ARDUINOJSON_USE_LONG_LONG
416
      case VariantType::Uint64:
3✔
417
        return canConvertNumber<T>(eightByteValue->asUint64);
3✔
418

419
      case VariantType::Int64:
4✔
420
        return canConvertNumber<T>(eightByteValue->asInt64);
4✔
421
#endif
422

423
      default:
68✔
424
        return false;
68✔
425
    }
426
  }
427

428
  bool isNull() const {
1,441✔
429
    return type() == VariantType::Null;
1,441✔
430
  }
431

432
  bool isObject() const {
46✔
433
    return type() == VariantType::Object;
46✔
434
  }
435

436
  size_t nesting() {
30✔
437
    return asCollection().nesting();
30✔
438
  }
439

440
  void removeElement(size_t index) {
10✔
441
    asArray().removeElement(index);
10✔
442
  }
10✔
443

444
  template <typename TAdaptedString>
445
  void removeMember(TAdaptedString key) {
17✔
446
    asObject().removeMember(key);
17✔
447
  }
17✔
448

449
  bool setBoolean(bool value) {
250✔
450
    if (!data_)
250✔
451
      return false;
2✔
452
    clear(data_, resources_);
248✔
453
    data_->setBoolean(value);
248✔
454
    return true;
248✔
455
  }
456

457
  template <typename T>
458
  bool setFloat(T value) {
73✔
459
    if (!data_)
73✔
460
      return false;
1✔
461
    clear(data_, resources_);
72✔
462
    return setFloat(value, data_, resources_);
72✔
463
  }
464

465
  template <typename T>
466
  static enable_if_t<sizeof(T) == 4, bool> setFloat(T value, VariantData* data,
90✔
467
                                                    ResourceManager*) {
468
    ARDUINOJSON_ASSERT(data != nullptr);
469
    ARDUINOJSON_ASSERT(data->type == VariantType::Null);
470
    data->type = VariantType::Float;
90✔
471
    data->content.asFloat = value;
90✔
472
    return true;
90✔
473
  }
474

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

479
  template <typename T>
480
  bool setInteger(T value) {
1,433✔
481
    if (!data_)
1,433✔
482
      return false;
16✔
483
    clear(data_, resources_);
1,417✔
484
    return setInteger(value, data_, resources_);
1,417✔
485
  }
486

487
  template <typename T>
488
  static enable_if_t<is_signed<T>::value, bool> setInteger(T value,
489
                                                           VariantData*,
490
                                                           ResourceManager*);
491

492
  template <typename T>
493
  static enable_if_t<is_unsigned<T>::value, bool> setInteger(T value,
494
                                                             VariantData*,
495
                                                             ResourceManager*);
496

497
  template <typename T>
498
  void setRawString(SerializedValue<T> value);
499

500
  template <typename TAdaptedString>
501
  bool setString(TAdaptedString value) {
66,121✔
502
    if (!data_)
66,121✔
503
      return false;
5✔
504
    clear(data_, resources_);
66,116✔
505
    return setString(value, data_, resources_);
66,116✔
506
  }
507

508
  template <typename TAdaptedString>
509
  static bool setString(TAdaptedString value, VariantData*, ResourceManager*);
510

511
  size_t size() {
46✔
512
    if (isObject())
46✔
513
      return asObject().size();
14✔
514

515
    if (isArray())
32✔
516
      return asArray().size();
16✔
517

518
    return 0;
16✔
519
  }
520

521
  ArrayImpl toArray() {
340✔
522
    if (!data_)
340✔
523
      return ArrayImpl();
1✔
524
    clear(data_, resources_);
339✔
525
    return toArray(data_, resources_);
339✔
526
  }
527

528
  static ArrayImpl toArray(VariantData* data, ResourceManager* resources) {
1,269✔
529
    ARDUINOJSON_ASSERT(data != nullptr);
530
    ARDUINOJSON_ASSERT(resources != nullptr);
531
    return ArrayImpl(data->toArray(), resources);
1,269✔
532
  }
533

534
  ObjectImpl toObject() {
307✔
535
    if (!data_)
307✔
NEW
536
      return ObjectImpl();
×
537
    clear(data_, resources_);
307✔
538
    return toObject(data_, resources_);
307✔
539
  }
540

541
  static ObjectImpl toObject(VariantData* data, ResourceManager* resources) {
1,336✔
542
    ARDUINOJSON_ASSERT(data != nullptr);
543
    ARDUINOJSON_ASSERT(resources != nullptr);
544
    return ObjectImpl(data->toObject(), resources);
1,336✔
545
  }
546

547
  VariantType type() const {
1,549✔
548
    return data_ ? data_->type : VariantType::Null;
1,549✔
549
  }
550

551
  // Release the resources used by this variant and set it to null.
552
  void clear() {
540✔
553
    if (data_)
540✔
554
      clear(data_, resources_);
539✔
555
  }
540✔
556

557
  static void clear(VariantData*, ResourceManager*);
558

559
 private:
560
  VariantData* data_;
561
  ResourceManager* resources_;
562
};
563

564
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