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

bblanchon / ArduinoJson / 16241432057

11 Jul 2025 04:36PM UTC coverage: 99.266% (-0.001%) from 99.267%
16241432057

push

github

bblanchon
CollectionIterator: remove friendship with `VariantImpl`

6 of 6 new or added lines in 2 files covered. (100.0%)

2 existing lines in 2 files now uncovered.

3924 of 3953 relevant lines covered (99.27%)

11114.7 hits per line

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

99.66
/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,454✔
26

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

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

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

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

43
#if ARDUINOJSON_USE_8_BYTE_POOL
44
    auto eightByteValue = getEightByte();
139,350✔
45
#endif
46
    switch (data_->type) {
139,350✔
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:
983✔
56
        return visit.visitArray(VariantImpl(data_, resources_));
983✔
57

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

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

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

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

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

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

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

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

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

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

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

97
  VariantData* addElement();
98

99
  template <typename T>
100
  bool addValue(const T& value);
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 = data_->content.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 = data_->content.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 {
50✔
226
    switch (type()) {
50✔
227
      case VariantType::RawString:
26✔
228
        return JsonString(data_->content.asOwnedString->data,
26✔
229
                          data_->content.asOwnedString->length);
26✔
230
      default:
24✔
231
        return JsonString();
24✔
232
    }
233
  }
234

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

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

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

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

266
  iterator createIterator() const;
267

268
  VariantData* getElement(size_t index) const;
269

270
  VariantData* getOrAddElement(size_t index);
271

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

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

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

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

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

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

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

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

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

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

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

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

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

323
  bool isUnbound() const {
167,333✔
324
    return !data_;
167,333✔
325
  }
326

327
  bool isNull() const {
2,930✔
328
    return type() == VariantType::Null;
2,930✔
329
  }
330

331
  bool isObject() const {
5,803✔
332
    return type() == VariantType::Object;
5,803✔
333
  }
334

335
  bool isString() const {
96✔
336
    return data_ && data_->isString();
96✔
337
  }
338

339
  size_t nesting() const;
340

341
  void removeElement(iterator it);
342

343
  void removeElement(size_t index);
344

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

348
  void removeMember(iterator it);
349

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

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

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

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

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

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

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

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

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

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

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

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

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

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

449
  bool setLinkedString(const char* s);
450

451
  void empty();
452

453
  size_t size() const;
454

455
  VariantType type() const {
232,611✔
456
    return data_ ? data_->type : VariantType::Null;
232,611✔
457
  }
458

459
  // Release the resources used by this variant and set it to null.
460
  void clear();
461

462
 private:
463
  template <typename TAdaptedString>
464
  iterator findKey(TAdaptedString key) const;
465

466
  iterator at(size_t index) const;
467

468
  void appendOne(Slot<VariantData> slot);
469
  void appendPair(Slot<VariantData> key, Slot<VariantData> value);
470

471
  void removeOne(iterator it);
472
  void removePair(iterator it);
473

474
  VariantData* getVariant(SlotId id) const {
70,038✔
475
    ARDUINOJSON_ASSERT(resources_ != nullptr);
476
    return resources_->getVariant(id);
70,038✔
477
  }
478

479
  void freeVariant(Slot<VariantData> slot) {
368✔
480
    ARDUINOJSON_ASSERT(resources_ != nullptr);
481
    resources_->freeVariant(slot);
368✔
482
  }
368✔
483

484
  Slot<VariantData> allocVariant() {
74,220✔
485
    ARDUINOJSON_ASSERT(resources_ != nullptr);
486
    return resources_->allocVariant();
74,220✔
487
  }
488

489
  Slot<VariantData> getPreviousSlot(VariantData*) const;
490

491
  CollectionData* getCollectionData() const {
79,641✔
492
    ARDUINOJSON_ASSERT(data_ != nullptr);
493
    ARDUINOJSON_ASSERT(data_->isCollection());
494
    return &data_->content.asCollection;
79,641✔
495
  }
496
};
497

498
template <typename T>
499
inline void VariantImpl::setRawString(SerializedValue<T> value) {
36✔
500
  if (!data_)
36✔
501
    return;
2✔
502
  auto dup = resources_->saveString(adaptString(value.data(), value.size()));
34✔
503
  if (dup)
34✔
504
    data_->setRawString(dup);
30✔
505
}
506

507
inline bool VariantImpl::setLinkedString(const char* s) {
1,161✔
508
  ARDUINOJSON_ASSERT(isNull());  // must call clear() first
509
  ARDUINOJSON_ASSERT(s);
510

511
  auto slotId = resources_->saveStaticString(s);
1,161✔
512
  if (slotId == NULL_SLOT)
1,161✔
513
    return false;
2✔
514

515
  data_->type = VariantType::LinkedString;
1,159✔
516
  data_->content.asSlotId = slotId;
1,159✔
517
  return true;
1,159✔
518
}
519

520
template <typename TAdaptedString>
521
inline bool VariantImpl::setString(TAdaptedString value) {
67,055✔
522
  ARDUINOJSON_ASSERT(isNull());  // must call clear() first
523

524
  if (!data_)
67,055✔
525
    return false;
5✔
526

527
  if (value.isNull())
67,050✔
528
    return false;
65,553✔
529

530
  if (value.isStatic())
1,497✔
531
    return setLinkedString(value.data());
1,161✔
532

533
  if (isTinyString(value, value.size())) {
336✔
534
    data_->setTinyString(value);
73✔
535
    return true;
73✔
536
  }
537

538
  auto dup = resources_->saveString(value);
263✔
539
  if (dup) {
263✔
540
    data_->setOwnedString(dup);
251✔
541
    return true;
251✔
542
  }
543

544
  return false;
12✔
545
}
546

547
inline void VariantImpl::clear() {
69,459✔
548
  if (!data_)
69,459✔
549
    return;
27✔
550

551
  if (data_->type & VariantTypeBits::OwnedStringBit)
69,432✔
552
    resources_->dereferenceString(data_->content.asOwnedString->data);
24✔
553

554
#if ARDUINOJSON_USE_8_BYTE_POOL
555
  if (data_->type & VariantTypeBits::EightByteBit)
69,432✔
556
    resources_->freeEightByte(data_->content.asSlotId);
3✔
557
#endif
558

559
  if (data_->type & VariantTypeBits::CollectionMask)
69,432✔
560
    empty();
17✔
561

562
  data_->type = VariantType::Null;
69,432✔
563
}
564

565
inline void VariantImpl::empty() {
67✔
566
  auto coll = getCollectionData();
67✔
567

568
  auto next = coll->head;
67✔
569
  while (next != NULL_SLOT) {
355✔
570
    auto currId = next;
288✔
571
    auto slot = getVariant(next);
288✔
572
    next = slot->next;
288✔
573
    freeVariant({slot, currId});
288✔
574
  }
575

576
  coll->head = NULL_SLOT;
67✔
577
  coll->tail = NULL_SLOT;
67✔
578
}
67✔
579

580
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