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

bblanchon / ArduinoJson / 15910025763

26 Jun 2025 06:48PM UTC coverage: 99.35%. Remained the same
15910025763

push

github

bblanchon
Move `VariantImpl` definition to `VariantImpl.hpp`

279 of 280 new or added lines in 1 file covered. (99.64%)

3971 of 3997 relevant lines covered (99.35%)

11305.4 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 VariantImpl {
17
 public:
18
  VariantImpl() : data_(nullptr), resources_(nullptr) {}
501✔
19

20
  VariantImpl(VariantData* data, ResourceManager* resources)
386,458✔
21
      : data_(data), resources_(resources) {}
386,458✔
22

23
  VariantData* getData() const {
149,243✔
24
    return data_;
149,243✔
25
  }
26

27
  ResourceManager* getResourceManager() const {
216,147✔
28
    return resources_;
216,147✔
29
  }
30

31
  template <typename TVisitor>
32
  typename TVisitor::result_type accept(TVisitor& visit) {
139,520✔
33
    if (!data_)
139,520✔
34
      return visit.visit(nullptr);
170✔
35

36
#if ARDUINOJSON_USE_8_BYTE_POOL
37
    auto eightByteValue = getEightByte();
139,350✔
38
#endif
39
    switch (data_->type) {
139,350✔
40
      case VariantType::Float:
121✔
41
        return visit.visit(data_->content.asFloat);
121✔
42

43
#if ARDUINOJSON_USE_DOUBLE
44
      case VariantType::Double:
16✔
45
        return visit.visit(eightByteValue->asDouble);
16✔
46
#endif
47

48
      case VariantType::Array:
983✔
49
        return visit.visit(asArray());
983✔
50

51
      case VariantType::Object:
2,681✔
52
        return visit.visit(asObject());
2,681✔
53

54
      case VariantType::TinyString:
142✔
55
        return visit.visit(JsonString(data_->content.asTinyString));
142✔
56

57
      case VariantType::LinkedString:
1,155✔
58
        return visit.visit(JsonString(asLinkedString(), true));
1,155✔
59

60
      case VariantType::OwnedString:
833✔
61
        return visit.visit(JsonString(data_->content.asOwnedString->data,
1,666✔
62
                                      data_->content.asOwnedString->length));
1,666✔
63

64
      case VariantType::RawString:
129✔
65
        return visit.visit(RawString(data_->content.asOwnedString->data,
258✔
66
                                     data_->content.asOwnedString->length));
258✔
67

68
      case VariantType::Int32:
1,185✔
69
        return visit.visit(static_cast<JsonInteger>(data_->content.asInt32));
1,185✔
70

71
      case VariantType::Uint32:
256✔
72
        return visit.visit(static_cast<JsonUInt>(data_->content.asUint32));
256✔
73

74
#if ARDUINOJSON_USE_LONG_LONG
75
      case VariantType::Int64:
7✔
76
        return visit.visit(eightByteValue->asInt64);
7✔
77

78
      case VariantType::Uint64:
8✔
79
        return visit.visit(eightByteValue->asUint64);
8✔
80
#endif
81

82
      case VariantType::Boolean:
607✔
83
        return visit.visit(data_->content.asBoolean != 0);
607✔
84

85
      default:
131,227✔
86
        return visit.visit(nullptr);
131,227✔
87
    }
88
  }
89

90
  VariantData* addElement() {
165✔
91
    auto array = isNull() ? toArray() : asArray();
165✔
92
    return array.addElement();
330✔
93
  }
94

95
  template <typename T>
96
  bool addValue(const T& value) {
139✔
97
    auto array = isNull() ? toArray() : asArray();
139✔
98
    return array.addValue(value);
278✔
99
  }
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
  ArrayImpl asArray() {
1,831✔
133
    return ArrayImpl(isArray() ? &data_->content.asCollection : nullptr,
1,831✔
134
                     resources_);
1,831✔
135
  }
136

137
  CollectionImpl asCollection() {
69,462✔
138
    return CollectionImpl(
69,462✔
139
        isCollection() ? &data_->content.asCollection : nullptr, resources_);
138,924✔
140
  }
141

142
  template <typename T>
143
  T asFloat() const {
55✔
144
    if (!data_)
55✔
145
      return 0.0;
1✔
146

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

184
    ARDUINOJSON_ASSERT(str != nullptr);
185
    return parseNumber<T>(str);
3✔
186
  }
187

188
  template <typename T>
189
  T asIntegral() const {
203✔
190
    if (!data_)
203✔
191
      return 0;
5✔
192

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

230
    ARDUINOJSON_ASSERT(str != nullptr);
231
    return parseNumber<T>(str);
9✔
232
  }
233

234
  ObjectImpl asObject() {
5,697✔
235
    return ObjectImpl(isObject() ? &data_->content.asCollection : nullptr,
5,697✔
236
                      resources_);
5,697✔
237
  }
238

239
  JsonString asRawString() const {
50✔
240
    switch (type()) {
50✔
241
      case VariantType::RawString:
26✔
242
        return JsonString(data_->content.asOwnedString->data,
26✔
243
                          data_->content.asOwnedString->length);
26✔
244
      default:
24✔
245
        return JsonString();
24✔
246
    }
247
  }
248

249
  const char* asLinkedString() const {
5,825✔
250
    ARDUINOJSON_ASSERT(type() == VariantType::LinkedString);
251
    return resources_->getStaticString(data_->content.asSlotId);
5,825✔
252
  }
253

254
  JsonString asString() const {
12,783✔
255
    switch (type()) {
12,783✔
256
      case VariantType::TinyString:
798✔
257
        return JsonString(data_->content.asTinyString);
798✔
258
      case VariantType::LinkedString:
4,663✔
259
        return JsonString(asLinkedString(), true);
4,663✔
260
      case VariantType::OwnedString:
6,877✔
261
        return JsonString(data_->content.asOwnedString->data,
6,877✔
262
                          data_->content.asOwnedString->length);
6,877✔
263
      default:
445✔
264
        return JsonString();
445✔
265
    }
266
  }
267

268
#if ARDUINOJSON_USE_8_BYTE_POOL
269
  const EightByteValue* getEightByte() const {
140,305✔
270
    return type() & VariantTypeBits::EightByteBit
140,305✔
271
               ? resources_->getEightByte(data_->content.asSlotId)
140,305✔
272
               : 0;
140,305✔
273
  }
274
#endif
275

276
  VariantData* getElement(size_t index) {
471✔
277
    return asArray().getElement(index);
471✔
278
  }
279

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

285
  VariantData* getOrAddElement(size_t index) {
153✔
286
    auto array = isNull() ? toArray() : asArray();
153✔
287
    return array.getOrAddElement(index);
306✔
288
  }
289

290
  template <typename TAdaptedString>
291
  VariantData* getOrAddMember(TAdaptedString key) {
962✔
292
    if (key.isNull())
962✔
293
      return nullptr;
1✔
294
    auto obj = isNull() ? toObject() : asObject();
961✔
295
    return obj.getOrAddMember(key);
961✔
296
  }
297

298
  bool isArray() const {
1,863✔
299
    return type() == VariantType::Array;
1,863✔
300
  }
301

302
  bool isBoolean() const {
303
    return type() == VariantType::Boolean;
304
  }
305

306
  bool isCollection() const {
69,462✔
307
    return type() & VariantTypeBits::CollectionMask;
69,462✔
308
  }
309

310
  bool isFloat() const {
311
    return data_ && data_->isFloat();
312
  }
313

314
  template <typename T>
315
  bool isInteger() const {
167✔
316
    if (!data_)
167✔
317
      return false;
16✔
318

319
#if ARDUINOJSON_USE_LONG_LONG
320
    auto eightByteValue = getEightByte();
148✔
321
#endif
322
    switch (data_->type) {
151✔
323
      case VariantType::Uint32:
11✔
324
        return canConvertNumber<T>(data_->content.asUint32);
11✔
325

326
      case VariantType::Int32:
65✔
327
        return canConvertNumber<T>(data_->content.asInt32);
65✔
328

329
#if ARDUINOJSON_USE_LONG_LONG
330
      case VariantType::Uint64:
3✔
331
        return canConvertNumber<T>(eightByteValue->asUint64);
3✔
332

333
      case VariantType::Int64:
4✔
334
        return canConvertNumber<T>(eightByteValue->asInt64);
4✔
335
#endif
336

337
      default:
68✔
338
        return false;
68✔
339
    }
340
  }
341

342
  bool isNull() const {
2,708✔
343
    return type() == VariantType::Null;
2,708✔
344
  }
345

346
  bool isObject() const {
5,743✔
347
    return type() == VariantType::Object;
5,743✔
348
  }
349

350
  bool isString() const {
351
    return data_ && data_->isString();
352
  }
353

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

358
  void removeElement(size_t index) {
10✔
359
    asArray().removeElement(index);
10✔
360
  }
10✔
361

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

367
  bool setBoolean(bool value) {
250✔
368
    if (!data_)
250✔
369
      return false;
2✔
370
    data_->setBoolean(value);
248✔
371
    return true;
248✔
372
  }
373

374
  template <typename T>
375
  enable_if_t<sizeof(T) == 4, bool> setFloat(T value) {
90✔
376
    ARDUINOJSON_ASSERT(type() == VariantType::Null);  // must call clear() first
377
    if (!data_)
90✔
NEW
378
      return false;
×
379
    data_->type = VariantType::Float;
90✔
380
    data_->content.asFloat = value;
90✔
381
    return true;
90✔
382
  }
383

384
  template <typename T>
385
  enable_if_t<sizeof(T) == 8, bool> setFloat(T value) {
80✔
386
    ARDUINOJSON_ASSERT(isNull());  // must call clear() first
387

388
    if (!data_)
80✔
389
      return false;
1✔
390

391
    float valueAsFloat = static_cast<float>(value);
79✔
392

393
#if ARDUINOJSON_USE_DOUBLE
394
    if (value == valueAsFloat) {
77✔
395
      data_->type = VariantType::Float;
32✔
396
      data_->content.asFloat = valueAsFloat;
32✔
397
    } else {
398
      auto slot = resources_->allocEightByte();
45✔
399
      if (!slot)
45✔
400
        return false;
3✔
401
      data_->type = VariantType::Double;
42✔
402
      data_->content.asSlotId = slot.id();
42✔
403
      slot->asDouble = value;
42✔
404
    }
405
#else
406
    data_->type = VariantType::Float;
2✔
407
    data_->content.asFloat = valueAsFloat;
2✔
408
#endif
409
    return true;
76✔
410
  }
411

412
  template <typename T>
413
  enable_if_t<is_signed<T>::value, bool> setInteger(T value) {
1,568✔
414
    ARDUINOJSON_ASSERT(isNull());  // must call clear() first
415

416
    if (!data_)
1,568✔
417
      return false;
15✔
418

419
    if (canConvertNumber<int32_t>(value)) {
1,553✔
420
      data_->type = VariantType::Int32;
1,536✔
421
      data_->content.asInt32 = static_cast<int32_t>(value);
1,536✔
422
    }
423
#if ARDUINOJSON_USE_LONG_LONG
424
    else {
425
      auto slot = resources_->allocEightByte();
17✔
426
      if (!slot)
17✔
427
        return false;
3✔
428
      data_->type = VariantType::Int64;
14✔
429
      data_->content.asSlotId = slot.id();
14✔
430
      slot->asInt64 = value;
14✔
431
    }
432
#endif
433
    return true;
1,550✔
434
  }
435

436
  template <typename T>
437
  enable_if_t<is_unsigned<T>::value, bool> setInteger(T value) {
2,300✔
438
    ARDUINOJSON_ASSERT(isNull());  // must call clear() first
439

440
    if (!data_)
2,300✔
441
      return false;
1✔
442

443
    if (canConvertNumber<uint32_t>(value)) {
2,299✔
444
      data_->type = VariantType::Uint32;
2,282✔
445
      data_->content.asUint32 = static_cast<uint32_t>(value);
2,282✔
446
    }
447
#if ARDUINOJSON_USE_LONG_LONG
448
    else {
449
      auto slot = resources_->allocEightByte();
16✔
450
      if (!slot)
16✔
451
        return false;
3✔
452
      data_->type = VariantType::Uint64;
13✔
453
      data_->content.asSlotId = slot.id();
13✔
454
      slot->asUint64 = value;
13✔
455
    }
456
#endif
457
    return true;
2,296✔
458
  }
459

460
  template <typename T>
461
  void setRawString(SerializedValue<T> value);
462

463
  template <typename TAdaptedString>
464
  bool setString(TAdaptedString value);
465

466
  bool setLinkedString(const char* s);
467

468
  size_t size() {
46✔
469
    if (isObject())
46✔
470
      return asObject().size();
14✔
471

472
    if (isArray())
32✔
473
      return asArray().size();
16✔
474

475
    return 0;
16✔
476
  }
477

478
  ArrayImpl toArray() {
1,283✔
479
    ARDUINOJSON_ASSERT(type() == VariantType::Null);  // must call clear() first
480
    if (!data_)
1,283✔
481
      return ArrayImpl();
13✔
482
    data_->type = VariantType::Array;
1,270✔
483
    return ArrayImpl(new (&data_->content.asCollection) CollectionData(),
2,540✔
484
                     resources_);
1,270✔
485
  }
486

487
  ObjectImpl toObject() {
1,351✔
488
    ARDUINOJSON_ASSERT(type() == VariantType::Null);  // must call clear() first
489
    if (!data_)
1,351✔
490
      return ObjectImpl();
3✔
491
    data_->type = VariantType::Object;
1,348✔
492
    return ObjectImpl(new (&data_->content.asCollection) CollectionData(),
2,696✔
493
                      resources_);
1,348✔
494
  }
495

496
  VariantType type() const {
232,914✔
497
    return data_ ? data_->type : VariantType::Null;
232,914✔
498
  }
499

500
  // Release the resources used by this variant and set it to null.
501
  void clear();
502

503
 private:
504
  VariantData* data_;
505
  ResourceManager* resources_;
506
};
507

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

517
inline bool VariantImpl::setLinkedString(const char* s) {
1,161✔
518
  ARDUINOJSON_ASSERT(isNull());  // must call clear() first
519
  ARDUINOJSON_ASSERT(s);
520

521
  auto slotId = resources_->saveStaticString(s);
1,161✔
522
  if (slotId == NULL_SLOT)
1,161✔
523
    return false;
2✔
524

525
  data_->type = VariantType::LinkedString;
1,159✔
526
  data_->content.asSlotId = slotId;
1,159✔
527
  return true;
1,159✔
528
}
529

530
template <typename TAdaptedString>
531
inline bool VariantImpl::setString(TAdaptedString value) {
67,054✔
532
  ARDUINOJSON_ASSERT(isNull());  // must call clear() first
533

534
  if (!data_)
67,054✔
535
    return false;
5✔
536

537
  if (value.isNull())
67,049✔
538
    return false;
65,552✔
539

540
  if (value.isStatic())
1,497✔
541
    return setLinkedString(value.data());
1,161✔
542

543
  if (isTinyString(value, value.size())) {
336✔
544
    data_->setTinyString(value);
73✔
545
    return true;
73✔
546
  }
547

548
  auto dup = resources_->saveString(value);
263✔
549
  if (dup) {
263✔
550
    data_->setOwnedString(dup);
251✔
551
    return true;
251✔
552
  }
553

554
  return false;
12✔
555
}
556

557
inline void VariantImpl::clear() {
69,459✔
558
  if (!data_)
69,459✔
559
    return;
27✔
560

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

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

569
  asCollection().clear();
69,432✔
570

571
  data_->type = VariantType::Null;
69,432✔
572
}
573

574
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

© 2026 Coveralls, Inc