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

bblanchon / ArduinoJson / 16175784963

09 Jul 2025 04:55PM UTC coverage: 99.267% (-0.08%) from 99.35%
16175784963

push

github

bblanchon
Replace `getData()/getResourceManager()` with `getImpl()`

168 of 168 new or added lines in 20 files covered. (100.0%)

7 existing lines in 5 files now uncovered.

3930 of 3959 relevant lines covered (99.27%)

10797.62 hits per line

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

99.67
/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/Collection/CollectionData.hpp>
8
#include <ArduinoJson/Memory/ResourceManager.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/VariantData.hpp>
14

15
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
16

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

21
 public:
22
  using iterator = CollectionIterator;
23

24
  VariantImpl() : data_(nullptr), resources_(nullptr) {}
794✔
25

26
  VariantImpl(VariantData* data, ResourceManager* resources)
243,690✔
27
      : data_(data), resources_(resources) {}
243,690✔
28

29
  VariantData* getData() const {
5,067✔
30
    return data_;
5,067✔
31
  }
32

33
  ResourceManager* getResourceManager() const {
73,345✔
34
    return resources_;
73,345✔
35
  }
36

37
  template <typename TVisitor>
38
  typename TVisitor::result_type accept(TVisitor& visit) {
139,520✔
39
    if (!data_)
139,520✔
40
      return visit.visit(nullptr);
170✔
41

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

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

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

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

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

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

66
      case VariantType::OwnedString:
833✔
67
        return visit.visit(JsonString(data_->content.asOwnedString->data,
1,666✔
68
                                      data_->content.asOwnedString->length));
1,666✔
69

70
      case VariantType::RawString:
129✔
71
        return visit.visit(RawString(data_->content.asOwnedString->data,
258✔
72
                                     data_->content.asOwnedString->length));
258✔
73

74
      case VariantType::Int32:
1,185✔
75
        return visit.visit(static_cast<JsonInteger>(data_->content.asInt32));
1,185✔
76

77
      case VariantType::Uint32:
256✔
78
        return visit.visit(static_cast<JsonUInt>(data_->content.asUint32));
256✔
79

80
#if ARDUINOJSON_USE_LONG_LONG
81
      case VariantType::Int64:
7✔
82
        return visit.visit(eightByteValue->asInt64);
7✔
83

84
      case VariantType::Uint64:
8✔
85
        return visit.visit(eightByteValue->asUint64);
8✔
86
#endif
87

88
      case VariantType::Boolean:
607✔
89
        return visit.visit(data_->content.asBoolean != 0);
607✔
90

91
      default:
131,227✔
92
        return visit.visit(nullptr);
131,227✔
93
    }
94
  }
95

96
  VariantData* addElement();
97

98
  template <typename T>
99
  bool addValue(const T& value);
100

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

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

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

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

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

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

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

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

224
  JsonString asRawString() const {
50✔
225
    switch (type()) {
50✔
226
      case VariantType::RawString:
26✔
227
        return JsonString(data_->content.asOwnedString->data,
26✔
228
                          data_->content.asOwnedString->length);
26✔
229
      default:
24✔
230
        return JsonString();
24✔
231
    }
232
  }
233

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

239
  JsonString asString() const {
12,783✔
240
    switch (type()) {
12,783✔
241
      case VariantType::TinyString:
798✔
242
        return JsonString(data_->content.asTinyString);
798✔
243
      case VariantType::LinkedString:
4,663✔
244
        return JsonString(asLinkedString(), true);
4,663✔
245
      case VariantType::OwnedString:
6,877✔
246
        return JsonString(data_->content.asOwnedString->data,
6,877✔
247
                          data_->content.asOwnedString->length);
6,877✔
248
      default:
445✔
249
        return JsonString();
445✔
250
    }
251
  }
252

253
#if ARDUINOJSON_USE_8_BYTE_POOL
254
  const EightByteValue* getEightByte() const {
140,305✔
255
    return type() & VariantTypeBits::EightByteBit
140,305✔
256
               ? resources_->getEightByte(data_->content.asSlotId)
140,305✔
257
               : 0;
140,305✔
258
  }
259
#endif
260

261
  SlotId head() const {
868✔
262
    return getCollectionData()->head;
868✔
263
  }
264

265
  iterator createIterator() const;
266

267
  VariantData* getElement(size_t index) const;
268

269
  VariantData* getOrAddElement(size_t index);
270

271
  VariantData* addPair(VariantData** value);
272

273
  template <typename TAdaptedString>
274
  VariantData* addMember(TAdaptedString key);
275

276
  template <typename TAdaptedString>
277
  VariantData* getMember(TAdaptedString key) const;
278

279
  template <typename TAdaptedString>
280
  VariantData* getOrAddMember(TAdaptedString key);
281

282
  bool isArray() const {
70,253✔
283
    return type() == VariantType::Array;
70,253✔
284
  }
285

286
  bool isBoolean() const {
287
    return type() == VariantType::Boolean;
288
  }
289

290
  bool isFloat() const {
48✔
291
    return data_ && data_->isFloat();
48✔
292
  }
293

294
  template <typename T>
295
  bool isInteger() const {
167✔
296
    if (!data_)
167✔
297
      return false;
16✔
298

299
#if ARDUINOJSON_USE_LONG_LONG
300
    auto eightByteValue = getEightByte();
148✔
301
#endif
302
    switch (data_->type) {
151✔
303
      case VariantType::Uint32:
11✔
304
        return canConvertNumber<T>(data_->content.asUint32);
11✔
305

306
      case VariantType::Int32:
65✔
307
        return canConvertNumber<T>(data_->content.asInt32);
65✔
308

309
#if ARDUINOJSON_USE_LONG_LONG
310
      case VariantType::Uint64:
3✔
311
        return canConvertNumber<T>(eightByteValue->asUint64);
3✔
312

313
      case VariantType::Int64:
4✔
314
        return canConvertNumber<T>(eightByteValue->asInt64);
4✔
315
#endif
316

317
      default:
68✔
318
        return false;
68✔
319
    }
320
  }
321

322
  bool isNull() const {
2,930✔
323
    return type() == VariantType::Null;
2,930✔
324
  }
325

326
  bool isObject() const {
5,803✔
327
    return type() == VariantType::Object;
5,803✔
328
  }
329

330
  bool isString() const {
96✔
331
    return data_ && data_->isString();
96✔
332
  }
333

334
  size_t nesting() const;
335

336
  void removeElement(iterator it) {
22✔
337
    if (!isArray())
22✔
338
      return;
3✔
339
    removeOne(it);
19✔
340
  }
341

342
  void removeElement(size_t index);
343

344
  template <typename TAdaptedString>
345
  void removeMember(TAdaptedString key);
346

347
  void removeMember(iterator it) {
35✔
348
    removePair(it);
35✔
349
  }
35✔
350

351
  bool setBoolean(bool value) {
250✔
352
    if (!data_)
250✔
353
      return false;
2✔
354
    data_->setBoolean(value);
248✔
355
    return true;
248✔
356
  }
357

358
  template <typename T>
359
  enable_if_t<sizeof(T) == 4, bool> setFloat(T value) {
90✔
360
    ARDUINOJSON_ASSERT(type() == VariantType::Null);  // must call clear() first
361
    if (!data_)
90✔
UNCOV
362
      return false;
×
363
    data_->type = VariantType::Float;
90✔
364
    data_->content.asFloat = value;
90✔
365
    return true;
90✔
366
  }
367

368
  template <typename T>
369
  enable_if_t<sizeof(T) == 8, bool> setFloat(T value) {
80✔
370
    ARDUINOJSON_ASSERT(isNull());  // must call clear() first
371

372
    if (!data_)
80✔
373
      return false;
1✔
374

375
    float valueAsFloat = static_cast<float>(value);
79✔
376

377
#if ARDUINOJSON_USE_DOUBLE
378
    if (value == valueAsFloat) {
77✔
379
      data_->type = VariantType::Float;
32✔
380
      data_->content.asFloat = valueAsFloat;
32✔
381
    } else {
382
      auto slot = resources_->allocEightByte();
45✔
383
      if (!slot)
45✔
384
        return false;
3✔
385
      data_->type = VariantType::Double;
42✔
386
      data_->content.asSlotId = slot.id();
42✔
387
      slot->asDouble = value;
42✔
388
    }
389
#else
390
    data_->type = VariantType::Float;
2✔
391
    data_->content.asFloat = valueAsFloat;
2✔
392
#endif
393
    return true;
76✔
394
  }
395

396
  template <typename T>
397
  enable_if_t<is_signed<T>::value, bool> setInteger(T value) {
1,568✔
398
    ARDUINOJSON_ASSERT(isNull());  // must call clear() first
399

400
    if (!data_)
1,568✔
401
      return false;
15✔
402

403
    if (canConvertNumber<int32_t>(value)) {
1,553✔
404
      data_->type = VariantType::Int32;
1,536✔
405
      data_->content.asInt32 = static_cast<int32_t>(value);
1,536✔
406
    }
407
#if ARDUINOJSON_USE_LONG_LONG
408
    else {
409
      auto slot = resources_->allocEightByte();
17✔
410
      if (!slot)
17✔
411
        return false;
3✔
412
      data_->type = VariantType::Int64;
14✔
413
      data_->content.asSlotId = slot.id();
14✔
414
      slot->asInt64 = value;
14✔
415
    }
416
#endif
417
    return true;
1,550✔
418
  }
419

420
  template <typename T>
421
  enable_if_t<is_unsigned<T>::value, bool> setInteger(T value) {
2,300✔
422
    ARDUINOJSON_ASSERT(isNull());  // must call clear() first
423

424
    if (!data_)
2,300✔
425
      return false;
1✔
426

427
    if (canConvertNumber<uint32_t>(value)) {
2,299✔
428
      data_->type = VariantType::Uint32;
2,282✔
429
      data_->content.asUint32 = static_cast<uint32_t>(value);
2,282✔
430
    }
431
#if ARDUINOJSON_USE_LONG_LONG
432
    else {
433
      auto slot = resources_->allocEightByte();
16✔
434
      if (!slot)
16✔
435
        return false;
3✔
436
      data_->type = VariantType::Uint64;
13✔
437
      data_->content.asSlotId = slot.id();
13✔
438
      slot->asUint64 = value;
13✔
439
    }
440
#endif
441
    return true;
2,296✔
442
  }
443

444
  template <typename T>
445
  void setRawString(SerializedValue<T> value);
446

447
  template <typename TAdaptedString>
448
  bool setString(TAdaptedString value);
449

450
  bool setLinkedString(const char* s);
451

452
  void empty();
453

454
  size_t size() const {
388✔
455
    if (!data_)
388✔
456
      return 0;
10✔
457

458
    size_t count = 0;
378✔
459

460
    for (auto it = createIterator(); !it.done(); it.next(resources_))
132,507✔
461
      count++;
132,129✔
462

463
    if (data_->type == VariantType::Object)
378✔
464
      count /= 2;  // TODO: do this in JsonObject?
267✔
465

466
    return count;
378✔
467
  }
468

469
  VariantType type() const {
232,611✔
470
    return data_ ? data_->type : VariantType::Null;
232,611✔
471
  }
472

473
  // Release the resources used by this variant and set it to null.
474
  void clear();
475

476
 private:
477
  template <typename TAdaptedString>
478
  iterator findKey(TAdaptedString key) const;
479

480
  iterator at(size_t index) const;
481

482
  void appendOne(Slot<VariantData> slot);
483
  void appendPair(Slot<VariantData> key, Slot<VariantData> value);
484

485
  void removeOne(iterator it);
486
  void removePair(iterator it);
487

488
  VariantData* getVariant(SlotId id) const {
76,717✔
489
    ARDUINOJSON_ASSERT(resources_ != nullptr);
490
    return resources_->getVariant(id);
76,717✔
491
  }
492

493
  void freeVariant(Slot<VariantData> slot) {
368✔
494
    ARDUINOJSON_ASSERT(resources_ != nullptr);
495
    resources_->freeVariant(slot);
368✔
496
  }
368✔
497

498
  Slot<VariantData> allocVariant() {
74,220✔
499
    ARDUINOJSON_ASSERT(resources_ != nullptr);
500
    return resources_->allocVariant();
74,220✔
501
  }
502

503
  Slot<VariantData> getPreviousSlot(VariantData*) const;
504

505
  CollectionData* getCollectionData() const {
79,641✔
506
    ARDUINOJSON_ASSERT(data_ != nullptr);
507
    ARDUINOJSON_ASSERT(data_->isCollection());
508
    return &data_->content.asCollection;
79,641✔
509
  }
510
};
511

512
template <typename T>
513
inline void VariantImpl::setRawString(SerializedValue<T> value) {
36✔
514
  if (!data_)
36✔
515
    return;
2✔
516
  auto dup = resources_->saveString(adaptString(value.data(), value.size()));
34✔
517
  if (dup)
34✔
518
    data_->setRawString(dup);
30✔
519
}
520

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

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

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

534
template <typename TAdaptedString>
535
inline bool VariantImpl::setString(TAdaptedString value) {
67,055✔
536
  ARDUINOJSON_ASSERT(isNull());  // must call clear() first
537

538
  if (!data_)
67,055✔
539
    return false;
5✔
540

541
  if (value.isNull())
67,050✔
542
    return false;
65,553✔
543

544
  if (value.isStatic())
1,497✔
545
    return setLinkedString(value.data());
1,161✔
546

547
  if (isTinyString(value, value.size())) {
336✔
548
    data_->setTinyString(value);
73✔
549
    return true;
73✔
550
  }
551

552
  auto dup = resources_->saveString(value);
263✔
553
  if (dup) {
263✔
554
    data_->setOwnedString(dup);
251✔
555
    return true;
251✔
556
  }
557

558
  return false;
12✔
559
}
560

561
inline void VariantImpl::clear() {
69,459✔
562
  if (!data_)
69,459✔
563
    return;
27✔
564

565
  if (data_->type & VariantTypeBits::OwnedStringBit)
69,432✔
566
    resources_->dereferenceString(data_->content.asOwnedString->data);
24✔
567

568
#if ARDUINOJSON_USE_8_BYTE_POOL
569
  if (data_->type & VariantTypeBits::EightByteBit)
69,432✔
570
    resources_->freeEightByte(data_->content.asSlotId);
3✔
571
#endif
572

573
  if (data_->type & VariantTypeBits::CollectionMask)
69,432✔
574
    empty();
17✔
575

576
  data_->type = VariantType::Null;
69,432✔
577
}
578

579
inline void VariantImpl::empty() {
67✔
580
  auto coll = getCollectionData();
67✔
581

582
  auto next = coll->head;
67✔
583
  while (next != NULL_SLOT) {
355✔
584
    auto currId = next;
288✔
585
    auto slot = getVariant(next);
288✔
586
    next = slot->next;
288✔
587
    freeVariant({slot, currId});
288✔
588
  }
589

590
  coll->head = NULL_SLOT;
67✔
591
  coll->tail = NULL_SLOT;
67✔
592
}
67✔
593

594
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