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

bblanchon / ArduinoJson / 18216370889

03 Oct 2025 07:46AM UTC coverage: 99.371%. First build
18216370889

push

github

bblanchon
Extract `VariantImpl`

414 of 415 new or added lines in 28 files covered. (99.76%)

3947 of 3972 relevant lines covered (99.37%)

10355.52 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,873✔
145
      : data_(data), resources_(resources) {}
223,873✔
146

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

151
  ResourceManager* getResourceManager() const {
215,986✔
152
    return resources_;
215,986✔
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
  JsonString asRawString() const {
351
    if (!data_)
352
      return JsonString();
353
    return data_->asRawString();
354
  }
355

356
  JsonString asString() const {
583✔
357
    if (!data_)
583✔
358
      return JsonString();
12✔
359
    return data_->asString();
571✔
360
  }
361

362
#if ARDUINOJSON_USE_8_BYTE_POOL
363
  static const EightByteValue* getEightByte(VariantData*, ResourceManager*);
364
#endif
365

366
  VariantData* getElement(size_t index) {
471✔
367
    return asArray().getElement(index);
471✔
368
  }
369

370
  template <typename TAdaptedString>
371
  VariantData* getMember(TAdaptedString key) {
2,211✔
372
    return asObject().getMember(key);
2,211✔
373
  }
374

375
  VariantData* getOrAddElement(size_t index) {
153✔
376
    auto array = isNull() ? toArray() : asArray();
153✔
377
    return array.getOrAddElement(index);
306✔
378
  }
379

380
  template <typename TAdaptedString>
381
  VariantData* getOrAddMember(TAdaptedString key) {
950✔
382
    return getOrAddMember(key, data_, resources_);
950✔
383
  }
384

385
  template <typename TAdaptedString>
386
  static VariantData* getOrAddMember(TAdaptedString key, VariantData* data,
950✔
387
                                     ResourceManager* resources) {
388
    if (key.isNull())
950✔
389
      return nullptr;
1✔
390
    if (!data)
949✔
391
      return nullptr;
3✔
392
    auto obj = data->type == VariantType::Null ? toObject(data, resources)
946✔
393
                                               : asObject(data, resources);
580✔
394
    return obj.getOrAddMember(key);
946✔
395
  }
396

397
  bool isArray() const {
32✔
398
    return type() == VariantType::Array;
32✔
399
  }
400

401
  bool isBoolean() const {
402
    return type() == VariantType::Boolean;
403
  }
404

405
  bool isCollection() const {
30✔
406
    return type() & VariantTypeBits::CollectionMask;
30✔
407
  }
408

409
  bool isFloat() const {
410
    return data_ && data_->isFloat();
411
  }
412

413
  template <typename T>
414
  bool isInteger() const {
167✔
415
    return isInteger<T>(data_, resources_);
167✔
416
  }
417

418
  template <typename T>
419
  static bool isInteger(VariantData* data, ResourceManager* resources) {
167✔
420
    if (!data)
167✔
421
      return false;
16✔
422

423
#if ARDUINOJSON_USE_LONG_LONG
424
    auto eightByteValue = getEightByte(data, resources);
148✔
425
#else
426
    (void)resources;
427
#endif
428
    switch (data->type) {
151✔
429
      case VariantType::Uint32:
11✔
430
        return canConvertNumber<T>(data->content.asUint32);
11✔
431

432
      case VariantType::Int32:
65✔
433
        return canConvertNumber<T>(data->content.asInt32);
65✔
434

435
#if ARDUINOJSON_USE_LONG_LONG
436
      case VariantType::Uint64:
3✔
437
        return canConvertNumber<T>(eightByteValue->asUint64);
3✔
438

439
      case VariantType::Int64:
4✔
440
        return canConvertNumber<T>(eightByteValue->asInt64);
4✔
441
#endif
442

443
      default:
68✔
444
        return false;
68✔
445
    }
446
  }
447

448
  bool isNull() const {
1,441✔
449
    return type() == VariantType::Null;
1,441✔
450
  }
451

452
  bool isObject() const {
46✔
453
    return type() == VariantType::Object;
46✔
454
  }
455

456
  bool isString() const {
457
    return data_ && data_->isString();
458
  }
459

460
  size_t nesting() {
30✔
461
    return asCollection().nesting();
30✔
462
  }
463

464
  void removeElement(size_t index) {
10✔
465
    asArray().removeElement(index);
10✔
466
  }
10✔
467

468
  template <typename TAdaptedString>
469
  void removeMember(TAdaptedString key) {
17✔
470
    asObject().removeMember(key);
17✔
471
  }
17✔
472

473
  bool setBoolean(bool value) {
250✔
474
    if (!data_)
250✔
475
      return false;
2✔
476
    clear(data_, resources_);
248✔
477
    data_->setBoolean(value);
248✔
478
    return true;
248✔
479
  }
480

481
  template <typename T>
482
  bool setFloat(T value) {
73✔
483
    if (!data_)
73✔
484
      return false;
1✔
485
    clear(data_, resources_);
72✔
486
    return setFloat(value, data_, resources_);
72✔
487
  }
488

489
  template <typename T>
490
  static enable_if_t<sizeof(T) == 4, bool> setFloat(T value, VariantData* data,
90✔
491
                                                    ResourceManager*) {
492
    ARDUINOJSON_ASSERT(data != nullptr);
493
    ARDUINOJSON_ASSERT(data->type == VariantType::Null);
494
    data->type = VariantType::Float;
90✔
495
    data->content.asFloat = value;
90✔
496
    return true;
90✔
497
  }
498

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

503
  template <typename T>
504
  bool setInteger(T value) {
1,433✔
505
    if (!data_)
1,433✔
506
      return false;
16✔
507
    clear(data_, resources_);
1,417✔
508
    return setInteger(value, data_, resources_);
1,417✔
509
  }
510

511
  template <typename T>
512
  static enable_if_t<is_signed<T>::value, bool> setInteger(T value,
513
                                                           VariantData*,
514
                                                           ResourceManager*);
515

516
  template <typename T>
517
  static enable_if_t<is_unsigned<T>::value, bool> setInteger(T value,
518
                                                             VariantData*,
519
                                                             ResourceManager*);
520

521
  template <typename T>
522
  void setRawString(SerializedValue<T> value);
523

524
  template <typename TAdaptedString>
525
  bool setString(TAdaptedString value) {
66,121✔
526
    if (!data_)
66,121✔
527
      return false;
5✔
528
    clear(data_, resources_);
66,116✔
529
    return setString(value, data_, resources_);
66,116✔
530
  }
531

532
  template <typename TAdaptedString>
533
  static bool setString(TAdaptedString value, VariantData*, ResourceManager*);
534

535
  size_t size() {
46✔
536
    if (isObject())
46✔
537
      return asObject().size();
14✔
538

539
    if (isArray())
32✔
540
      return asArray().size();
16✔
541

542
    return 0;
16✔
543
  }
544

545
  ArrayImpl toArray() {
340✔
546
    if (!data_)
340✔
547
      return ArrayImpl();
1✔
548
    clear(data_, resources_);
339✔
549
    return toArray(data_, resources_);
339✔
550
  }
551

552
  static ArrayImpl toArray(VariantData* data, ResourceManager* resources) {
1,269✔
553
    ARDUINOJSON_ASSERT(data != nullptr);
554
    ARDUINOJSON_ASSERT(resources != nullptr);
555
    return ArrayImpl(data->toArray(), resources);
1,269✔
556
  }
557

558
  ObjectImpl toObject() {
307✔
559
    if (!data_)
307✔
NEW
560
      return ObjectImpl();
×
561
    clear(data_, resources_);
307✔
562
    return toObject(data_, resources_);
307✔
563
  }
564

565
  static ObjectImpl toObject(VariantData* data, ResourceManager* resources) {
1,336✔
566
    ARDUINOJSON_ASSERT(data != nullptr);
567
    ARDUINOJSON_ASSERT(resources != nullptr);
568
    return ObjectImpl(data->toObject(), resources);
1,336✔
569
  }
570

571
  VariantType type() const {
1,549✔
572
    return data_ ? data_->type : VariantType::Null;
1,549✔
573
  }
574

575
  // Release the resources used by this variant and set it to null.
576
  void clear() {
540✔
577
    if (data_)
540✔
578
      clear(data_, resources_);
539✔
579
  }
540✔
580

581
  static void clear(VariantData*, ResourceManager*);
582

583
 private:
584
  VariantData* data_;
585
  ResourceManager* resources_;
586
};
587

588
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