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

bblanchon / ArduinoJson / 18473245878

13 Oct 2025 05:13PM UTC coverage: 99.398%. First build
18473245878

push

github

bblanchon
Merge `ArrayImpl` into `VariantImpl`

56 of 57 new or added lines in 10 files covered. (98.25%)

3965 of 3989 relevant lines covered (99.4%)

10437.25 hits per line

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

99.35
/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/Object/ObjectData.hpp>
11
#include <ArduinoJson/Strings/JsonString.hpp>
12
#include <ArduinoJson/Strings/StringAdapters.hpp>
13
#include <ArduinoJson/Variant/VariantData.hpp>
14

15
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
16

17
// HACK: large functions are implemented in static function to give opportunity
18
// to the compiler to optimize the `this` pointer away.
19
class VariantImpl {
20
 public:
21
  using iterator = CollectionIterator;
22

23
  VariantImpl() : data_(nullptr), resources_(nullptr) {}
530✔
24

25
  VariantImpl(VariantData* data, ResourceManager* resources)
223,062✔
26
      : data_(data), resources_(resources) {}
223,062✔
27

28
  VariantData* getData() const {
149,034✔
29
    return data_;
149,034✔
30
  }
31

32
  ResourceManager* getResourceManager() const {
221,490✔
33
    return resources_;
221,490✔
34
  }
35

36
  template <typename TVisitor>
37
  typename TVisitor::result_type accept(TVisitor& visit) {
5,329✔
38
    return accept(visit, data_, resources_);
5,329✔
39
  }
40

41
  template <typename TVisitor>
42
  static typename TVisitor::result_type accept(TVisitor& visit,
139,510✔
43
                                               VariantData* data,
44
                                               ResourceManager* resources) {
45
    if (!data)
139,510✔
46
      return visit.visit(nullptr);
170✔
47

48
#if ARDUINOJSON_USE_8_BYTE_POOL
49
    auto eightByteValue = getEightByte(data, resources);
139,340✔
50
#endif
51
    switch (data->type) {
139,340✔
52
      case VariantType::Float:
121✔
53
        return visit.visit(data->content.asFloat);
121✔
54

55
#if ARDUINOJSON_USE_DOUBLE
56
      case VariantType::Double:
16✔
57
        return visit.visit(eightByteValue->asDouble);
16✔
58
#endif
59

60
      case VariantType::Array:
982✔
61
        return visit.visitArray(data);
982✔
62

63
      case VariantType::Object:
2,679✔
64
        return visit.visitObject(data);
2,679✔
65

66
      case VariantType::TinyString:
341✔
67
        return visit.visit(JsonString(data->content.asTinyString));
341✔
68

69
      case VariantType::LongString:
1,783✔
70
        return visit.visit(JsonString(data->content.asStringNode->data,
3,566✔
71
                                      data->content.asStringNode->length));
3,566✔
72

73
      case VariantType::RawString:
129✔
74
        return visit.visit(RawString(data->content.asStringNode->data,
258✔
75
                                     data->content.asStringNode->length));
258✔
76

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

80
      case VariantType::Uint32:
256✔
81
        return visit.visit(static_cast<JsonUInt>(data->content.asUint32));
256✔
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() {
611✔
100
    if (!isArray())
611✔
101
      return nullptr;
8✔
102
    return addElement(data_, resources_);
603✔
103
  }
104

105
  static VariantData* addElement(VariantData*, ResourceManager*);
106

107
  template <typename T>
108
  bool addValue(const T& value) {
66,686✔
109
    if (!isArray())
66,686✔
110
      return false;
9✔
111
    return addValue(value, data_, resources_);
66,677✔
112
  }
113

114
  template <typename T>
115
  static bool addValue(const T& value, VariantData*, ResourceManager*);
116

117
  bool asBoolean() const {
1,254✔
118
    return asBoolean(data_, resources_);
1,254✔
119
  }
120

121
  static bool asBoolean(VariantData* data, ResourceManager* resources) {
1,254✔
122
    if (!data)
1,254✔
123
      return false;
699✔
124

125
#if ARDUINOJSON_USE_8_BYTE_POOL
126
    auto eightByteValue = getEightByte(data, resources);
555✔
127
#endif
128
    switch (data->type) {
555✔
129
      case VariantType::Boolean:
305✔
130
        return data->content.asBoolean;
305✔
131
      case VariantType::Uint32:
6✔
132
      case VariantType::Int32:
133
        return data->content.asUint32 != 0;
6✔
134
      case VariantType::Float:
2✔
135
        return data->content.asFloat != 0;
2✔
136
#if ARDUINOJSON_USE_DOUBLE
137
      case VariantType::Double:
4✔
138
        return eightByteValue->asDouble != 0;
4✔
139
#endif
140
      case VariantType::Null:
6✔
141
        return false;
6✔
142
#if ARDUINOJSON_USE_LONG_LONG
143
      case VariantType::Uint64:
2✔
144
      case VariantType::Int64:
145
        return eightByteValue->asUint64 != 0;
2✔
146
#endif
147
      default:
230✔
148
        return true;
230✔
149
    }
150
  }
151

152
  CollectionImpl asCollection() const {
1,238✔
153
    return CollectionImpl(data_, resources_);
1,238✔
154
  }
155

156
  template <typename T>
157
  T asFloat() const {
55✔
158
    return asFloat<T>(data_, resources_);
55✔
159
  }
160

161
  template <typename T>
162
  static T asFloat(VariantData* data, ResourceManager* resources) {
55✔
163
    if (!data)
55✔
164
      return 0.0;
1✔
165

166
    static_assert(is_floating_point<T>::value, "T must be a floating point");
167
#if ARDUINOJSON_USE_8_BYTE_POOL
168
    auto eightByteValue = getEightByte(data, resources);
54✔
169
#endif
170
    const char* str = nullptr;
54✔
171
    switch (data->type) {
54✔
172
      case VariantType::Boolean:
2✔
173
        return static_cast<T>(data->content.asBoolean);
2✔
174
      case VariantType::Uint32:
2✔
175
        return static_cast<T>(data->content.asUint32);
2✔
176
      case VariantType::Int32:
4✔
177
        return static_cast<T>(data->content.asInt32);
4✔
178
#if ARDUINOJSON_USE_LONG_LONG
179
      case VariantType::Uint64:
1✔
180
        return static_cast<T>(eightByteValue->asUint64);
1✔
181
      case VariantType::Int64:
1✔
182
        return static_cast<T>(eightByteValue->asInt64);
1✔
183
#endif
184
      case VariantType::TinyString:
2✔
185
        str = data->content.asTinyString;
2✔
186
        break;
2✔
187
      case VariantType::LongString:
1✔
188
        str = data->content.asStringNode->data;
1✔
189
        break;
1✔
190
      case VariantType::Float:
18✔
191
        return static_cast<T>(data->content.asFloat);
18✔
192
#if ARDUINOJSON_USE_DOUBLE
193
      case VariantType::Double:
21✔
194
        return static_cast<T>(eightByteValue->asDouble);
21✔
195
#endif
196
      default:
2✔
197
        return 0.0;
2✔
198
    }
199

200
    ARDUINOJSON_ASSERT(str != nullptr);
201
    return parseNumber<T>(str);
3✔
202
  }
203

204
  template <typename T>
205
  T asIntegral() const {
203✔
206
    return asIntegral<T>(data_, resources_);
203✔
207
  }
208

209
  template <typename T>
210
  static T asIntegral(VariantData* data, ResourceManager* resources) {
203✔
211
    if (!data)
203✔
212
      return 0;
5✔
213

214
    static_assert(is_integral<T>::value, "T must be an integral type");
215
#if ARDUINOJSON_USE_8_BYTE_POOL
216
    auto eightByteValue = getEightByte(data, resources);
198✔
217
#endif
218
    const char* str = nullptr;
198✔
219
    switch (data->type) {
198✔
220
      case VariantType::Boolean:
2✔
221
        return data->content.asBoolean;
2✔
222
      case VariantType::Uint32:
66✔
223
        return convertNumber<T>(data->content.asUint32);
66✔
224
      case VariantType::Int32:
83✔
225
        return convertNumber<T>(data->content.asInt32);
83✔
226
#if ARDUINOJSON_USE_LONG_LONG
227
      case VariantType::Uint64:
10✔
228
        return convertNumber<T>(eightByteValue->asUint64);
10✔
229
      case VariantType::Int64:
11✔
230
        return convertNumber<T>(eightByteValue->asInt64);
11✔
231
#endif
232
      case VariantType::TinyString:
3✔
233
        str = data->content.asTinyString;
3✔
234
        break;
3✔
235
      case VariantType::LongString:
6✔
236
        str = data->content.asStringNode->data;
6✔
237
        break;
6✔
238
      case VariantType::Float:
5✔
239
        return convertNumber<T>(data->content.asFloat);
5✔
240
#if ARDUINOJSON_USE_DOUBLE
241
      case VariantType::Double:
10✔
242
        return convertNumber<T>(eightByteValue->asDouble);
10✔
243
#endif
244
      default:
2✔
245
        return 0;
2✔
246
    }
247

248
    ARDUINOJSON_ASSERT(str != nullptr);
249
    return parseNumber<T>(str);
9✔
250
  }
251

252
  ObjectImpl asObject() {
2,227✔
253
    return asObject(data_, resources_);
2,227✔
254
  }
255

256
  static ObjectImpl asObject(VariantData* data, ResourceManager* resources) {
2,808✔
257
    return ObjectImpl(data, resources);
2,808✔
258
  }
259

260
  iterator at(size_t index) const;
261

262
  iterator createIterator() const {
1,176✔
263
    return asCollection().createIterator();
1,176✔
264
  }
265

266
#if ARDUINOJSON_USE_8_BYTE_POOL
267
  static const EightByteValue* getEightByte(VariantData* data,
140,295✔
268
                                            ResourceManager* resources) {
269
    ARDUINOJSON_ASSERT(data != nullptr);
270
    ARDUINOJSON_ASSERT(resources != nullptr);
271
    return data->type & VariantTypeBits::EightByteBit
140,295✔
272
               ? resources->getEightByte(data->content.asSlotId)
140,295✔
273
               : 0;
140,295✔
274
  }
275
#endif
276

277
  VariantData* getOrAddElement(size_t index);
278

279
  VariantData* getElement(size_t index) const;
280

281
  template <typename TAdaptedString>
282
  VariantData* getMember(TAdaptedString key) {
2,210✔
283
    return asObject().getMember(key);
2,210✔
284
  }
285

286
  template <typename TAdaptedString>
287
  VariantData* getOrAddMember(TAdaptedString key) {
951✔
288
    return getOrAddMember(key, data_, resources_);
951✔
289
  }
290

291
  template <typename TAdaptedString>
292
  static VariantData* getOrAddMember(TAdaptedString key, VariantData* data,
951✔
293
                                     ResourceManager* resources) {
294
    if (key.isNull())
951✔
295
      return nullptr;
1✔
296
    if (!data)
950✔
297
      return nullptr;
3✔
298
    auto obj = data->type == VariantType::Null ? toObject(data, resources)
947✔
299
                                               : asObject(data, resources);
581✔
300
    return obj.getOrAddMember(key);
947✔
301
  }
302

303
  bool isArray() const {
67,790✔
304
    return type() == VariantType::Array;
67,790✔
305
  }
306

307
  bool isCollection() const {
16✔
308
    return type() & VariantTypeBits::CollectionMask;
16✔
309
  }
310

311
  template <typename T>
312
  bool isInteger() const {
167✔
313
    return isInteger<T>(data_, resources_);
167✔
314
  }
315

316
  template <typename T>
317
  static bool isInteger(VariantData* data, ResourceManager* resources) {
167✔
318
    if (!data)
167✔
319
      return false;
16✔
320

321
#if ARDUINOJSON_USE_LONG_LONG
322
    auto eightByteValue = getEightByte(data, resources);
148✔
323
#else
324
    (void)resources;
325
#endif
326
    switch (data->type) {
151✔
327
      case VariantType::Uint32:
11✔
328
        return canConvertNumber<T>(data->content.asUint32);
11✔
329

330
      case VariantType::Int32:
65✔
331
        return canConvertNumber<T>(data->content.asInt32);
65✔
332

333
#if ARDUINOJSON_USE_LONG_LONG
334
      case VariantType::Uint64:
3✔
335
        return canConvertNumber<T>(eightByteValue->asUint64);
3✔
336

337
      case VariantType::Int64:
4✔
338
        return canConvertNumber<T>(eightByteValue->asInt64);
4✔
339
#endif
340

341
      default:
68✔
342
        return false;
68✔
343
    }
344
  }
345

346
  bool isNull() const {
2,091✔
347
    return type() == VariantType::Null;
2,091✔
348
  }
349

350
  bool isObject() const {
351
    return type() == VariantType::Object;
352
  }
353

354
  size_t nesting() const {
40✔
355
    return asCollection().nesting();
40✔
356
  }
357

358
  void removeElement(size_t index);
359

360
  void remove(CollectionIterator it) {
22✔
361
    asCollection().removeOne(it);
22✔
362
  }
22✔
363

364
  template <typename TAdaptedString>
365
  void removeMember(TAdaptedString key) {
17✔
366
    asObject().removeMember(key);
17✔
367
  }
17✔
368

369
  bool setBoolean(bool value) {
250✔
370
    if (!data_)
250✔
371
      return false;
2✔
372
    clear(data_, resources_);
248✔
373
    data_->setBoolean(value);
248✔
374
    return true;
248✔
375
  }
376

377
  template <typename T>
378
  bool setFloat(T value) {
73✔
379
    if (!data_)
73✔
380
      return false;
1✔
381
    clear(data_, resources_);
72✔
382
    return setFloat(value, data_, resources_);
72✔
383
  }
384

385
  template <typename T>
386
  static enable_if_t<sizeof(T) == 4, bool> setFloat(T value, VariantData* data,
90✔
387
                                                    ResourceManager*) {
388
    ARDUINOJSON_ASSERT(data != nullptr);
389
    ARDUINOJSON_ASSERT(data->type == VariantType::Null);
390
    data->type = VariantType::Float;
90✔
391
    data->content.asFloat = value;
90✔
392
    return true;
90✔
393
  }
394

395
  template <typename T>
396
  static enable_if_t<sizeof(T) == 8, bool> setFloat(
79✔
397
      T value, VariantData* data, ResourceManager* resources) {
398
    ARDUINOJSON_ASSERT(data != nullptr);
399
    ARDUINOJSON_ASSERT(data->type == VariantType::Null);
400
    ARDUINOJSON_ASSERT(resources != nullptr);
401

402
    float valueAsFloat = static_cast<float>(value);
79✔
403

404
#if ARDUINOJSON_USE_DOUBLE
405
    if (value == valueAsFloat) {
77✔
406
      data->type = VariantType::Float;
32✔
407
      data->content.asFloat = valueAsFloat;
32✔
408
    } else {
409
      auto slot = resources->allocEightByte();
45✔
410
      if (!slot)
45✔
411
        return false;
3✔
412
      data->type = VariantType::Double;
42✔
413
      data->content.asSlotId = slot.id();
42✔
414
      slot->asDouble = value;
42✔
415
    }
416
#else
417
    data->type = VariantType::Float;
2✔
418
    data->content.asFloat = valueAsFloat;
2✔
419
#endif
420
    return true;
76✔
421
  }
422

423
  template <typename T>
424
  bool setInteger(T value) {
1,433✔
425
    if (!data_)
1,433✔
426
      return false;
16✔
427
    clear(data_, resources_);
1,417✔
428
    return setInteger(value, data_, resources_);
1,417✔
429
  }
430

431
  template <typename T>
432
  static enable_if_t<is_signed<T>::value, bool> setInteger(
1,548✔
433
      T value, VariantData* data, ResourceManager* resources) {
434
    ARDUINOJSON_ASSERT(data != nullptr);
435
    ARDUINOJSON_ASSERT(data->type == VariantType::Null);
436
    ARDUINOJSON_ASSERT(resources != nullptr);
437

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

457
  template <typename T>
458
  static enable_if_t<is_unsigned<T>::value, bool> setInteger(
2,299✔
459
      T value, VariantData* data, ResourceManager* resources) {
460
    ARDUINOJSON_ASSERT(data != nullptr);
461
    ARDUINOJSON_ASSERT(data->type == VariantType::Null);
462
    ARDUINOJSON_ASSERT(resources != nullptr);
463

464
    if (canConvertNumber<uint32_t>(value)) {
2,299✔
465
      data->type = VariantType::Uint32;
2,282✔
466
      data->content.asUint32 = static_cast<uint32_t>(value);
2,282✔
467
    }
468
#if ARDUINOJSON_USE_LONG_LONG
469
    else {
470
      auto slot = resources->allocEightByte();
16✔
471
      if (!slot)
16✔
472
        return false;
3✔
473
      data->type = VariantType::Uint64;
13✔
474
      data->content.asSlotId = slot.id();
13✔
475
      slot->asUint64 = value;
13✔
476
    }
477
#else
478
    (void)resources;
479
#endif
480
    return true;
2,296✔
481
  }
482

483
  template <typename T>
484
  void setRawString(SerializedValue<T> value) {
36✔
485
    if (!data_)
36✔
486
      return;
2✔
487
    clear(data_, resources_);
34✔
488
    auto dup = resources_->saveString(adaptString(value.data(), value.size()));
34✔
489
    if (dup)
34✔
490
      data_->setRawString(dup);
30✔
491
  }
492

493
  template <typename TAdaptedString>
494
  bool setString(TAdaptedString value) {
66,121✔
495
    if (!data_)
66,121✔
496
      return false;
5✔
497
    clear(data_, resources_);
66,116✔
498
    return setString(value, data_, resources_);
66,116✔
499
  }
500

501
  template <typename TAdaptedString>
502
  static bool setString(TAdaptedString value, VariantData* data,
67,025✔
503
                        ResourceManager* resources) {
504
    ARDUINOJSON_ASSERT(data != nullptr);
505
    ARDUINOJSON_ASSERT(data->type == VariantType::Null);
506
    ARDUINOJSON_ASSERT(resources != nullptr);
507

508
    if (value.isNull())
67,025✔
509
      return false;
65,552✔
510

511
    if (isTinyString(value, value.size())) {
1,473✔
512
      data->setTinyString(value);
412✔
513
      return true;
412✔
514
    }
515

516
    auto dup = resources->saveString(value);
1,061✔
517
    if (dup) {
1,061✔
518
      data->setLongString(dup);
1,049✔
519
      return true;
1,049✔
520
    }
521

522
    return false;
12✔
523
  }
524

525
  size_t size() const {
128✔
526
    if (!data_)
128✔
527
      return 0;
7✔
528

529
    return size(data_, resources_);
121✔
530
  }
531

532
  static size_t size(VariantData* data, ResourceManager* resources) {
140✔
533
    ARDUINOJSON_ASSERT(data != nullptr);
534
    ARDUINOJSON_ASSERT(resources != nullptr);
535

536
    size_t n = CollectionImpl(data, resources).size();
140✔
537

538
    if (data->type == VariantType::Object) {
140✔
539
      ARDUINOJSON_ASSERT((n % 2) == 0);
540
      n /= 2;
29✔
541
    }
542

543
    return n;
140✔
544
  }
545

546
  bool toArray() {
78✔
547
    if (!data_)
78✔
NEW
548
      return false;
×
549
    clear(data_, resources_);
78✔
550
    data_->toArray();
78✔
551
    return true;
78✔
552
  }
553

554
  ObjectImpl toObject() {
109✔
555
    if (!data_)
109✔
556
      return ObjectImpl();
×
557
    clear(data_, resources_);
109✔
558
    return toObject(data_, resources_);
109✔
559
  }
560

561
  static ObjectImpl toObject(VariantData* data, ResourceManager* resources) {
475✔
562
    ARDUINOJSON_ASSERT(data != nullptr);
563
    ARDUINOJSON_ASSERT(resources != nullptr);
564
    data->toObject();
475✔
565
    return ObjectImpl(data, resources);
475✔
566
  }
567

568
  VariantType type() const {
69,897✔
569
    return data_ ? data_->type : VariantType::Null;
69,897✔
570
  }
571

572
  // Release the resources used by this variant and set it to null.
573
  void clear() {
48✔
574
    if (data_)
48✔
575
      clear(data_, resources_);
47✔
576
  }
48✔
577

578
  static void clear(VariantData* data, ResourceManager* resources) {
68,533✔
579
    ARDUINOJSON_ASSERT(data != nullptr);
580
    ARDUINOJSON_ASSERT(resources != nullptr);
581

582
    if (data->type & VariantTypeBits::OwnedStringBit)
68,533✔
583
      resources->dereferenceString(data->content.asStringNode->data);
45✔
584

585
#if ARDUINOJSON_USE_8_BYTE_POOL
586
    if (data->type & VariantTypeBits::EightByteBit)
68,533✔
587
      resources->freeEightByte(data->content.asSlotId);
3✔
588
#endif
589

590
    if (data->type & VariantTypeBits::CollectionMask)
68,533✔
591
      CollectionImpl::clear(data, resources);
17✔
592

593
    data->type = VariantType::Null;
68,533✔
594
  }
68,533✔
595

596
  void empty() {
16✔
597
    if (isCollection())
16✔
598
      CollectionImpl::clear(data_, resources_);
15✔
599
  }
16✔
600

601
 private:
602
  VariantData* data_;
603
  ResourceManager* resources_;
604
};
605

606
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