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

bblanchon / ArduinoJson / 16401353174

20 Jul 2025 03:16PM UTC coverage: 99.197% (-0.02%) from 99.215%
16401353174

push

github

bblanchon
Done

32 of 35 new or added lines in 5 files covered. (91.43%)

6 existing lines in 4 files now uncovered.

3955 of 3987 relevant lines covered (99.2%)

10923.11 hits per line

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

99.68
/src/ArduinoJson/Variant/VariantImpl.hpp
1
// ArduinoJson - https://arduinojson.org
2
// Copyright © 2014-2025, Benoit BLANCHON
3
// MIT License
4

5
#pragma once
6

7
#include <ArduinoJson/Memory/ResourceManager.hpp>
8
#include <ArduinoJson/Misc/SerializedValue.hpp>
9
#include <ArduinoJson/Numbers/convertNumber.hpp>
10
#include <ArduinoJson/Strings/JsonString.hpp>
11
#include <ArduinoJson/Strings/StringAdapters.hpp>
12
#include <ArduinoJson/Variant/VariantData.hpp>
13

14
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
15

16
class CollectionIterator;
17

18
class VariantImpl {
19
  VariantData* data_;
20
  ResourceManager* resources_;
21

22
 public:
23
  using iterator = CollectionIterator;
24

25
  VariantImpl() : data_(nullptr), resources_(nullptr) {}
5,354✔
26

27
  VariantImpl(VariantData* data, ResourceManager* resources)
382,957✔
28
      : data_(data), resources_(resources) {}
382,957✔
29

30
  VariantData* data() const {
165,603✔
31
    return data_;
165,603✔
32
  }
33

34
  ResourceManager* resources() const {
229,674✔
35
    return resources_;
229,674✔
36
  }
37

38
  template <typename TVisitor>
39
  typename TVisitor::result_type accept(TVisitor& visit) const {
139,454✔
40
    if (!data_)
139,454✔
41
      return visit.visit(nullptr);
168✔
42

43
#if ARDUINOJSON_USE_8_BYTE_POOL
44
    auto eightByteValue = getEightByte();
139,286✔
45
#endif
46
    switch (data_->type) {
139,286✔
47
      case VariantType::Float:
121✔
48
        return visit.visit(data_->content.asFloat);
121✔
49

50
#if ARDUINOJSON_USE_DOUBLE
51
      case VariantType::Double:
16✔
52
        return visit.visit(eightByteValue->asDouble);
16✔
53
#endif
54

55
      case VariantType::Array:
980✔
56
        return visit.visitArray(VariantImpl(data_, resources_));
980✔
57

58
      case VariantType::Object:
2,672✔
59
        return visit.visitObject(VariantImpl(data_, resources_));
2,672✔
60

61
      case VariantType::TinyString:
142✔
62
        return visit.visit(JsonString(data_->content.asTinyString));
142✔
63

64
      case VariantType::LinkedString:
1,140✔
65
        return visit.visit(JsonString(asLinkedString(), true));
1,140✔
66

67
      case VariantType::OwnedString: {
807✔
68
        auto s = asOwnedString();
807✔
69
        return visit.visit(JsonString(s->data, s->length));
807✔
70
      }
71

72
      case VariantType::RawString: {
125✔
73
        auto s = asOwnedString();
125✔
74
        return visit.visit(RawString(s->data, s->length));
125✔
75
      }
76

77
      case VariantType::Int32:
1,179✔
78
        return visit.visit(static_cast<JsonInteger>(data_->content.asInt32));
1,179✔
79

80
      case VariantType::Uint32:
255✔
81
        return visit.visit(static_cast<JsonUInt>(data_->content.asUint32));
255✔
82

83
#if ARDUINOJSON_USE_LONG_LONG
84
      case VariantType::Int64:
7✔
85
        return visit.visit(eightByteValue->asInt64);
7✔
86

87
      case VariantType::Uint64:
8✔
88
        return visit.visit(eightByteValue->asUint64);
8✔
89
#endif
90

91
      case VariantType::Boolean:
607✔
92
        return visit.visit(data_->content.asBoolean != 0);
607✔
93

94
      default:
131,227✔
95
        return visit.visit(nullptr);
131,227✔
96
    }
97
  }
98

99
  VariantData* addElement();
100
  void addElement(Slot<VariantData> slot);
101

102
  bool asBoolean() const {
1,254✔
103
    if (!data_)
1,254✔
104
      return false;
699✔
105

106
#if ARDUINOJSON_USE_8_BYTE_POOL
107
    auto eightByteValue = getEightByte();
555✔
108
#endif
109
    switch (data_->type) {
555✔
110
      case VariantType::Boolean:
305✔
111
        return data_->content.asBoolean;
305✔
112
      case VariantType::Uint32:
6✔
113
      case VariantType::Int32:
114
        return data_->content.asUint32 != 0;
6✔
115
      case VariantType::Float:
2✔
116
        return data_->content.asFloat != 0;
2✔
117
#if ARDUINOJSON_USE_DOUBLE
118
      case VariantType::Double:
4✔
119
        return eightByteValue->asDouble != 0;
4✔
120
#endif
121
      case VariantType::Null:
6✔
122
        return false;
6✔
123
#if ARDUINOJSON_USE_LONG_LONG
124
      case VariantType::Uint64:
2✔
125
      case VariantType::Int64:
126
        return eightByteValue->asUint64 != 0;
2✔
127
#endif
128
      default:
230✔
129
        return true;
230✔
130
    }
131
  }
132

133
  template <typename T>
134
  T asFloat() const {
55✔
135
    if (!data_)
55✔
136
      return 0.0;
1✔
137

138
    static_assert(is_floating_point<T>::value, "T must be a floating point");
139
#if ARDUINOJSON_USE_8_BYTE_POOL
140
    auto eightByteValue = getEightByte();
54✔
141
#endif
142
    const char* str = nullptr;
54✔
143
    switch (data_->type) {
54✔
144
      case VariantType::Boolean:
2✔
145
        return static_cast<T>(data_->content.asBoolean);
2✔
146
      case VariantType::Uint32:
2✔
147
        return static_cast<T>(data_->content.asUint32);
2✔
148
      case VariantType::Int32:
4✔
149
        return static_cast<T>(data_->content.asInt32);
4✔
150
#if ARDUINOJSON_USE_LONG_LONG
151
      case VariantType::Uint64:
1✔
152
        return static_cast<T>(eightByteValue->asUint64);
1✔
153
      case VariantType::Int64:
1✔
154
        return static_cast<T>(eightByteValue->asInt64);
1✔
155
#endif
156
      case VariantType::TinyString:
1✔
157
        str = data_->content.asTinyString;
1✔
158
        break;
1✔
159
      case VariantType::LinkedString:
1✔
160
        str = asLinkedString();
1✔
161
        break;
1✔
162
      case VariantType::OwnedString:
1✔
163
        str = asOwnedString()->data;
1✔
164
        break;
1✔
165
      case VariantType::Float:
18✔
166
        return static_cast<T>(data_->content.asFloat);
18✔
167
#if ARDUINOJSON_USE_DOUBLE
168
      case VariantType::Double:
21✔
169
        return static_cast<T>(eightByteValue->asDouble);
21✔
170
#endif
171
      default:
2✔
172
        return 0.0;
2✔
173
    }
174

175
    ARDUINOJSON_ASSERT(str != nullptr);
176
    return parseNumber<T>(str);
3✔
177
  }
178

179
  template <typename T>
180
  T asIntegral() const {
203✔
181
    if (!data_)
203✔
182
      return 0;
5✔
183

184
    static_assert(is_integral<T>::value, "T must be an integral type");
185
#if ARDUINOJSON_USE_8_BYTE_POOL
186
    auto eightByteValue = getEightByte();
198✔
187
#endif
188
    const char* str = nullptr;
198✔
189
    switch (data_->type) {
198✔
190
      case VariantType::Boolean:
2✔
191
        return data_->content.asBoolean;
2✔
192
      case VariantType::Uint32:
66✔
193
        return convertNumber<T>(data_->content.asUint32);
66✔
194
      case VariantType::Int32:
83✔
195
        return convertNumber<T>(data_->content.asInt32);
83✔
196
#if ARDUINOJSON_USE_LONG_LONG
197
      case VariantType::Uint64:
10✔
198
        return convertNumber<T>(eightByteValue->asUint64);
10✔
199
      case VariantType::Int64:
11✔
200
        return convertNumber<T>(eightByteValue->asInt64);
11✔
201
#endif
202
      case VariantType::TinyString:
2✔
203
        str = data_->content.asTinyString;
2✔
204
        break;
2✔
205
      case VariantType::LinkedString:
6✔
206
        str = asLinkedString();
6✔
207
        break;
6✔
208
      case VariantType::OwnedString:
1✔
209
        str = asOwnedString()->data;
1✔
210
        break;
1✔
211
      case VariantType::Float:
5✔
212
        return convertNumber<T>(data_->content.asFloat);
5✔
213
#if ARDUINOJSON_USE_DOUBLE
214
      case VariantType::Double:
10✔
215
        return convertNumber<T>(eightByteValue->asDouble);
10✔
216
#endif
217
      default:
2✔
218
        return 0;
2✔
219
    }
220

221
    ARDUINOJSON_ASSERT(str != nullptr);
222
    return parseNumber<T>(str);
9✔
223
  }
224

225
  JsonString asRawString() const {
54✔
226
    switch (type()) {
54✔
227
      case VariantType::RawString: {
30✔
228
        auto s = asOwnedString();
30✔
229
        return JsonString(s->data, s->length);
30✔
230
      }
231
      default:
24✔
232
        return JsonString();
24✔
233
    }
234
  }
235

236
  const char* asLinkedString() const {
5,825✔
237
    ARDUINOJSON_ASSERT(type() == VariantType::LinkedString);
238
    return resources_->getStaticString(data_->content.asSlotId);
5,825✔
239
  }
240

241
  JsonString asString() const {
12,795✔
242
    switch (type()) {
12,795✔
243
      case VariantType::TinyString:
798✔
244
        return JsonString(data_->content.asTinyString);
798✔
245
      case VariantType::LinkedString:
4,650✔
246
        return JsonString(asLinkedString(), true);
4,650✔
247
      case VariantType::OwnedString: {
6,902✔
248
        auto s = asOwnedString();
6,902✔
249
        return JsonString(s->data, s->length);
6,902✔
250
      }
251
      default:
445✔
252
        return JsonString();
445✔
253
    }
254
  }
255

256
  StringNode* asOwnedString() const {
7,890✔
257
    ARDUINOJSON_ASSERT(type() & VariantTypeBits::OwnedStringBit);
258
    return data_->content.asOwnedString;
7,890✔
259
  }
260

261
#if ARDUINOJSON_USE_8_BYTE_POOL
262
  const EightByteValue* getEightByte() const {
140,241✔
263
    return type() & VariantTypeBits::EightByteBit
140,241✔
264
               ? resources_->getEightByte(data_->content.asSlotId)
140,241✔
265
               : 0;
140,241✔
266
  }
267
#endif
268

269
  SlotId head() const {
868✔
270
    return getCollectionData()->head;
868✔
271
  }
272

273
  iterator createIterator() const;
274

275
  VariantData* getElement(size_t index) const;
276

277
  VariantData* getOrAddElement(size_t index);
278

279
  VariantData* addPair(VariantData** value);
280

281
  template <typename TAdaptedString>
282
  VariantData* addMember(TAdaptedString key);
283

284
  template <typename TAdaptedString>
285
  VariantData* getMember(TAdaptedString key) const;
286

287
  template <typename TAdaptedString>
288
  VariantData* getOrAddMember(TAdaptedString key);
289

290
  bool isArray() const {
70,233✔
291
    return type() == VariantType::Array;
70,233✔
292
  }
293

294
  bool isBoolean() const {
295
    return type() == VariantType::Boolean;
296
  }
297

298
  bool isFloat() const {
48✔
299
    return data_ && data_->isFloat();
48✔
300
  }
301

302
  template <typename T>
303
  bool isInteger() const {
167✔
304
    if (!data_)
167✔
305
      return false;
16✔
306

307
#if ARDUINOJSON_USE_LONG_LONG
308
    auto eightByteValue = getEightByte();
148✔
309
#endif
310
    switch (data_->type) {
151✔
311
      case VariantType::Uint32:
11✔
312
        return canConvertNumber<T>(data_->content.asUint32);
11✔
313

314
      case VariantType::Int32:
65✔
315
        return canConvertNumber<T>(data_->content.asInt32);
65✔
316

317
#if ARDUINOJSON_USE_LONG_LONG
318
      case VariantType::Uint64:
3✔
319
        return canConvertNumber<T>(eightByteValue->asUint64);
3✔
320

321
      case VariantType::Int64:
4✔
322
        return canConvertNumber<T>(eightByteValue->asInt64);
4✔
323
#endif
324

325
      default:
68✔
326
        return false;
68✔
327
    }
328
  }
329

330
  bool isUnbound() const {
167,283✔
331
    return !data_;
167,283✔
332
  }
333

334
  bool isNull() const {
2,896✔
335
    return type() == VariantType::Null;
2,896✔
336
  }
337

338
  bool isObject() const {
5,707✔
339
    return type() == VariantType::Object;
5,707✔
340
  }
341

342
  bool isString() const {
96✔
343
    return data_ && data_->isString();
96✔
344
  }
345

346
  size_t nesting() const;
347

348
  void removeElement(iterator it);
349

350
  void removeElement(size_t index);
351

352
  template <typename TAdaptedString>
353
  void removeMember(TAdaptedString key);
354

355
  void removeMember(iterator it);
356

357
  bool copyVariant(const VariantImpl& src) {
94✔
358
    switch (src.type()) {
94✔
359
      case VariantType::Null:
3✔
360
        return true;
3✔
361

362
      case VariantType::Array:
3✔
363
        return copyArray(src);
3✔
364

365
      case VariantType::Object:
9✔
366
        return copyObject(src);
9✔
367

368
      case VariantType::RawString:
4✔
369
        return setRawString(adaptString(src.asRawString()));
4✔
370

371
      case VariantType::LinkedString:
28✔
372
        return setLinkedString(src.asLinkedString());
28✔
373

374
      case VariantType::OwnedString:
40✔
375
        return setOwnedString(adaptString(src.asString()));
40✔
376

377
      default:
7✔
378
        data_->content = src.data_->content;
7✔
379
        data_->type = src.data_->type;
7✔
380
        return true;
7✔
381
    }
382
  }
383

384
  bool copyArray(const VariantImpl& src);
385
  bool copyObject(const VariantImpl& src);
386

387
  bool setBoolean(bool value) {
250✔
388
    if (!data_)
250✔
389
      return false;
2✔
390
    data_->setBoolean(value);
248✔
391
    return true;
248✔
392
  }
393

394
  template <typename T>
395
  enable_if_t<sizeof(T) == 4, bool> setFloat(T value) {
90✔
396
    ARDUINOJSON_ASSERT(type() == VariantType::Null);  // must call clear() first
397
    if (!data_)
90✔
UNCOV
398
      return false;
×
399
    data_->type = VariantType::Float;
90✔
400
    data_->content.asFloat = value;
90✔
401
    return true;
90✔
402
  }
403

404
  template <typename T>
405
  enable_if_t<sizeof(T) == 8, bool> setFloat(T value) {
80✔
406
    ARDUINOJSON_ASSERT(isNull());  // must call clear() first
407

408
    if (!data_)
80✔
409
      return false;
1✔
410

411
    float valueAsFloat = static_cast<float>(value);
79✔
412

413
#if ARDUINOJSON_USE_DOUBLE
414
    if (value == valueAsFloat) {
77✔
415
      data_->type = VariantType::Float;
32✔
416
      data_->content.asFloat = valueAsFloat;
32✔
417
    } else {
418
      auto slot = resources_->allocEightByte();
45✔
419
      if (!slot)
45✔
420
        return false;
3✔
421
      data_->type = VariantType::Double;
42✔
422
      data_->content.asSlotId = slot.id();
42✔
423
      slot->asDouble = value;
42✔
424
    }
425
#else
426
    data_->type = VariantType::Float;
2✔
427
    data_->content.asFloat = valueAsFloat;
2✔
428
#endif
429
    return true;
76✔
430
  }
431

432
  template <typename T>
433
  enable_if_t<is_signed<T>::value, bool> setInteger(T value) {
1,562✔
434
    ARDUINOJSON_ASSERT(isNull());  // must call clear() first
435

436
    if (!data_)
1,562✔
437
      return false;
15✔
438

439
    if (canConvertNumber<int32_t>(value)) {
1,547✔
440
      data_->type = VariantType::Int32;
1,530✔
441
      data_->content.asInt32 = static_cast<int32_t>(value);
1,530✔
442
    }
443
#if ARDUINOJSON_USE_LONG_LONG
444
    else {
445
      auto slot = resources_->allocEightByte();
17✔
446
      if (!slot)
17✔
447
        return false;
3✔
448
      data_->type = VariantType::Int64;
14✔
449
      data_->content.asSlotId = slot.id();
14✔
450
      slot->asInt64 = value;
14✔
451
    }
452
#endif
453
    return true;
1,544✔
454
  }
455

456
  template <typename T>
457
  enable_if_t<is_unsigned<T>::value, bool> setInteger(T value) {
2,299✔
458
    ARDUINOJSON_ASSERT(isNull());  // must call clear() first
459

460
    if (!data_)
2,299✔
461
      return false;
1✔
462

463
    if (canConvertNumber<uint32_t>(value)) {
2,298✔
464
      data_->type = VariantType::Uint32;
2,281✔
465
      data_->content.asUint32 = static_cast<uint32_t>(value);
2,281✔
466
    }
467
#if ARDUINOJSON_USE_LONG_LONG
468
    else {
469
      auto slot = resources_->allocEightByte();
16✔
470
      if (!slot)
16✔
471
        return false;
3✔
472
      data_->type = VariantType::Uint64;
13✔
473
      data_->content.asSlotId = slot.id();
13✔
474
      slot->asUint64 = value;
13✔
475
    }
476
#endif
477
    return true;
2,295✔
478
  }
479

480
  template <typename TAdaptedString>
481
  bool setRawString(TAdaptedString value) {
36✔
482
    if (!data_)
36✔
483
      return false;
2✔
484
    auto dup = resources_->saveString(value);
34✔
485
    if (!dup)
34✔
486
      return false;
4✔
487
    data_->setRawString(dup);
30✔
488
    return true;
30✔
489
  }
490

491
  template <typename TAdaptedString>
492
  bool setString(TAdaptedString value) {
66,986✔
493
    ARDUINOJSON_ASSERT(isNull());  // must call clear() first
494

495
    if (!data_)
66,986✔
496
      return false;
5✔
497

498
    if (value.isNull())
66,981✔
499
      return true;
65,552✔
500

501
    if (value.isStatic())
1,429✔
502
      return setLinkedString(value.data());
1,133✔
503

504
    if (isTinyString(value, value.size())) {
296✔
505
      data_->setTinyString(value);
73✔
506
      return true;
73✔
507
    }
508

509
    return setOwnedString(value);
223✔
510
  }
511

512
  template <typename TAdaptedString>
513
  bool setOwnedString(TAdaptedString value) {
263✔
514
    auto dup = resources_->saveString(value);
263✔
515
    if (!dup)
263✔
516
      return false;
12✔
517

518
    data_->setOwnedString(dup);
251✔
519
    return true;
251✔
520
  }
521

522
  bool setLinkedString(const char* s) {
1,161✔
523
    ARDUINOJSON_ASSERT(isNull());  // must call clear() first
524
    ARDUINOJSON_ASSERT(s);
525

526
    auto slotId = resources_->saveStaticString(s);
1,161✔
527
    if (slotId == NULL_SLOT)
1,161✔
528
      return false;
2✔
529

530
    data_->type = VariantType::LinkedString;
1,159✔
531
    data_->content.asSlotId = slotId;
1,159✔
532
    return true;
1,159✔
533
  }
534

535
  void empty() {
55✔
536
    auto coll = getCollectionData();
55✔
537

538
    auto next = coll->head;
55✔
539
    while (next != NULL_SLOT) {
343✔
540
      auto currId = next;
288✔
541
      auto slot = getVariant(next);
288✔
542
      next = slot->next;
288✔
543
      freeVariant({slot, currId});
288✔
544
    }
545

546
    coll->head = NULL_SLOT;
55✔
547
    coll->tail = NULL_SLOT;
55✔
548
  }
55✔
549

550
  size_t size() const;
551

552
  VariantType type() const {
232,507✔
553
    return data_ ? data_->type : VariantType::Null;
232,507✔
554
  }
555

556
  // Release the resources used by this variant and set it to null.
557
  void clear() {
69,445✔
558
    if (!data_)
69,445✔
559
      return;
27✔
560

561
    if (data_->type & VariantTypeBits::OwnedStringBit)
69,418✔
562
      resources_->dereferenceString(asOwnedString());
24✔
563

564
#if ARDUINOJSON_USE_8_BYTE_POOL
565
    if (data_->type & VariantTypeBits::EightByteBit)
69,418✔
566
      resources_->freeEightByte(data_->content.asSlotId);
3✔
567
#endif
568

569
    if (data_->type & VariantTypeBits::CollectionMask)
69,418✔
570
      empty();
51✔
571

572
    data_->type = VariantType::Null;
69,418✔
573
  }
574

575
 private:
576
  template <typename TAdaptedString>
577
  iterator findKey(TAdaptedString key) const;
578

579
  iterator at(size_t index) const;
580

581
  void appendPair(Slot<VariantData> key, Slot<VariantData> value);
582

583
  void removeOne(iterator it);
584
  void removePair(iterator it);
585

586
  VariantData* getVariant(SlotId id) const {
70,038✔
587
    ARDUINOJSON_ASSERT(resources_ != nullptr);
588
    return resources_->getVariant(id);
70,038✔
589
  }
590

591
  void freeVariant(Slot<VariantData> slot) {
366✔
592
    ARDUINOJSON_ASSERT(resources_ != nullptr);
593
    VariantImpl(slot.ptr(), resources_).clear();
366✔
594
    resources_->freeVariant(slot);
366✔
595
  }
366✔
596

597
  Slot<VariantData> allocVariant() {
7,546✔
598
    ARDUINOJSON_ASSERT(resources_ != nullptr);
599
    return resources_->allocVariant();
7,546✔
600
  }
601

602
  Slot<VariantData> getPreviousSlot(VariantData*) const;
603

604
  CollectionData* getCollectionData() const {
79,602✔
605
    ARDUINOJSON_ASSERT(data_ != nullptr);
606
    ARDUINOJSON_ASSERT(data_->isCollection());
607
    return &data_->content.asCollection;
79,602✔
608
  }
609
};
610

611
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