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

bblanchon / ArduinoJson / 18014952124

25 Sep 2025 05:01PM UTC coverage: 99.32% (-0.1%) from 99.418%
18014952124

push

github

bblanchon
Rewrite all converters

Before: 9802, 8486, 9630, 12418, 9654
After:  9802, 8446, 9630, 12242, 9660
Target: 9800, 8458, 9634, 12290, 9702

37 of 39 new or added lines in 1 file covered. (94.87%)

4 existing lines in 3 files now uncovered.

3945 of 3972 relevant lines covered (99.32%)

10265.19 hits per line

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

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

5
#pragma once
6

7
#include <ArduinoJson/Memory/MemoryPool.hpp>
8
#include <ArduinoJson/Memory/StringNode.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/VariantContent.hpp>
14

15
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
16

17
template <typename T>
18
T parseNumber(const char* s);
19

20
template <typename T>
21
static bool isTinyString(const T& s, size_t n) {
3,194✔
22
  if (n > tinyStringMaxLength)
3,194✔
23
    return false;
2,562✔
24
  bool containsNul = false;
632✔
25
  for (uint8_t i = 0; i < uint8_t(n); i++)
1,781✔
26
    containsNul |= !s[i];
1,149✔
27
  return !containsNul;
632✔
28
}
29

30
struct VariantData {
31
  VariantContent content;  // must be first to allow cast from array to variant
32
  VariantType type = VariantType::Null;
33
  SlotId next = NULL_SLOT;
34

35
  // Placement new
36
  static void* operator new(size_t, void* p) noexcept {
74,779✔
37
    return p;
74,779✔
38
  }
39

40
  static void operator delete(void*, void*) noexcept {}
41

42
  void setBoolean(bool value) {
330✔
43
    ARDUINOJSON_ASSERT(type == VariantType::Null);
44
    type = VariantType::Boolean;
330✔
45
    content.asBoolean = value;
330✔
46
  }
330✔
47

48
  template <typename TAdaptedString>
49
  void setTinyString(const TAdaptedString& s) {
619✔
50
    ARDUINOJSON_ASSERT(type == VariantType::Null);
51
    ARDUINOJSON_ASSERT(s.size() <= tinyStringMaxLength);
52

53
    type = VariantType::TinyString;
619✔
54

55
    auto n = uint8_t(s.size());
619✔
56
    for (uint8_t i = 0; i < n; i++) {
1,729✔
57
      char c = s[i];
1,110✔
58
      ARDUINOJSON_ASSERT(c != 0);  // no NUL in tiny string
59
      content.asTinyString[i] = c;
1,110✔
60
    }
61

62
    content.asTinyString[n] = 0;
619✔
63
  }
619✔
64

65
  void setLongString(StringNode* s) {
2,563✔
66
    ARDUINOJSON_ASSERT(type == VariantType::Null);
67
    ARDUINOJSON_ASSERT(s);
68
    type = VariantType::LongString;
2,563✔
69
    content.asStringNode = s;
2,563✔
70
  }
2,563✔
71

72
  void setRawString(StringNode* s) {
87✔
73
    ARDUINOJSON_ASSERT(type == VariantType::Null);
74
    ARDUINOJSON_ASSERT(s);
75
    type = VariantType::RawString;
87✔
76
    content.asStringNode = s;
87✔
77
  }
87✔
78

79
  bool isFloat() const {
406✔
80
    return type & VariantTypeBits::NumberBit;
406✔
81
  }
82

83
  bool isString() const {
80✔
84
    return type == VariantType::LongString || type == VariantType::TinyString;
80✔
85
  }
86
};
87

88
class VariantImpl {
89
 public:
90
  VariantImpl() : data_(nullptr), resources_(nullptr) {}
500✔
91

92
  VariantImpl(VariantData* data, ResourceManager* resources)
173,484✔
93
      : data_(data), resources_(resources) {}
173,484✔
94

95
  VariantData* getData() const {
149,178✔
96
    return data_;
149,178✔
97
  }
98

99
  ResourceManager* getResourceManager() const {
216,036✔
100
    return resources_;
216,036✔
101
  }
102

103
  template <typename TVisitor>
104
  typename TVisitor::result_type accept(TVisitor& visit) {
105
    return accept(visit, data_, resources_);
106
  }
107

108
  template <typename TVisitor>
109
  static typename TVisitor::result_type accept(TVisitor& visit,
110
                                               VariantData* data_,
111
                                               ResourceManager* resources_);
112

113
  VariantData* addElement() {
165✔
114
    return addElement(data_, resources_);
165✔
115
  }
116

117
  static VariantData* addElement(VariantData* data_,
165✔
118
                                 ResourceManager* resources_) {
119
    if (!data_)
165✔
120
      return nullptr;
6✔
121
    auto array = data_->type == VariantType::Null ? toArray(data_, resources_)
88✔
122
                                                  : asArray(data_, resources_);
159✔
123
    return array.addElement();
159✔
124
  }
125

126
  template <typename T>
127
  bool addValue(const T& value) {
138✔
128
    return addValue(value, data_, resources_);
138✔
129
  }
130

131
  template <typename T>
132
  static bool addValue(const T& value, VariantData* data_,
138✔
133
                       ResourceManager* resources_) {
134
    if (!data_)
138✔
135
      return false;
6✔
136
    auto array = data_->type == VariantType::Null ? toArray(data_, resources_)
132✔
137
                                                  : asArray(data_, resources_);
57✔
138
    return array.addValue(value);
132✔
139
  }
140

141
  bool asBoolean() const {
1,254✔
142
    if (!data_)
1,254✔
143
      return false;
699✔
144

145
#if ARDUINOJSON_USE_8_BYTE_POOL
146
    auto eightByteValue = getEightByte();
555✔
147
#endif
148
    switch (data_->type) {
555✔
149
      case VariantType::Boolean:
305✔
150
        return data_->content.asBoolean;
305✔
151
      case VariantType::Uint32:
6✔
152
      case VariantType::Int32:
153
        return data_->content.asUint32 != 0;
6✔
154
      case VariantType::Float:
2✔
155
        return data_->content.asFloat != 0;
2✔
156
#if ARDUINOJSON_USE_DOUBLE
157
      case VariantType::Double:
4✔
158
        return eightByteValue->asDouble != 0;
4✔
159
#endif
160
      case VariantType::Null:
6✔
161
        return false;
6✔
162
#if ARDUINOJSON_USE_LONG_LONG
163
      case VariantType::Uint64:
2✔
164
      case VariantType::Int64:
165
        return eightByteValue->asUint64 != 0;
2✔
166
#endif
167
      default:
230✔
168
        return true;
230✔
169
    }
170
  }
171

172
  ArrayImpl asArray() {
566✔
173
    return asArray(data_, resources_);
566✔
174
  }
175

176
  static ArrayImpl asArray(VariantData* data_, ResourceManager* resources_) {
1,830✔
177
    return ArrayImpl(data_ && data_->type == VariantType::Array
1,830✔
178
                         ? &data_->content.asCollection
179
                         : nullptr,
180
                     resources_);
1,830✔
181
  }
182

183
  CollectionImpl asCollection() {
30✔
184
    return CollectionImpl(
30✔
185
        isCollection() ? &data_->content.asCollection : nullptr, resources_);
60✔
186
  }
187

188
  template <typename T>
189
  T asFloat() const {
55✔
190
    if (!data_)
55✔
191
      return 0.0;
1✔
192

193
    static_assert(is_floating_point<T>::value, "T must be a floating point");
194
#if ARDUINOJSON_USE_8_BYTE_POOL
195
    auto eightByteValue = getEightByte();
54✔
196
#endif
197
    const char* str = nullptr;
54✔
198
    switch (data_->type) {
54✔
199
      case VariantType::Boolean:
2✔
200
        return static_cast<T>(data_->content.asBoolean);
2✔
201
      case VariantType::Uint32:
2✔
202
        return static_cast<T>(data_->content.asUint32);
2✔
203
      case VariantType::Int32:
4✔
204
        return static_cast<T>(data_->content.asInt32);
4✔
205
#if ARDUINOJSON_USE_LONG_LONG
206
      case VariantType::Uint64:
1✔
207
        return static_cast<T>(eightByteValue->asUint64);
1✔
208
      case VariantType::Int64:
1✔
209
        return static_cast<T>(eightByteValue->asInt64);
1✔
210
#endif
211
      case VariantType::TinyString:
2✔
212
        str = data_->content.asTinyString;
2✔
213
        break;
2✔
214
      case VariantType::LongString:
1✔
215
        str = data_->content.asStringNode->data;
1✔
216
        break;
1✔
217
      case VariantType::Float:
18✔
218
        return static_cast<T>(data_->content.asFloat);
18✔
219
#if ARDUINOJSON_USE_DOUBLE
220
      case VariantType::Double:
21✔
221
        return static_cast<T>(eightByteValue->asDouble);
21✔
222
#endif
223
      default:
2✔
224
        return 0.0;
2✔
225
    }
226

227
    ARDUINOJSON_ASSERT(str != nullptr);
228
    return parseNumber<T>(str);
3✔
229
  }
230

231
  template <typename T>
232
  T asIntegral() const {
203✔
233
    if (!data_)
203✔
234
      return 0;
5✔
235

236
    static_assert(is_integral<T>::value, "T must be an integral type");
237
#if ARDUINOJSON_USE_8_BYTE_POOL
238
    auto eightByteValue = getEightByte();
198✔
239
#endif
240
    const char* str = nullptr;
198✔
241
    switch (data_->type) {
198✔
242
      case VariantType::Boolean:
2✔
243
        return data_->content.asBoolean;
2✔
244
      case VariantType::Uint32:
66✔
245
        return convertNumber<T>(data_->content.asUint32);
66✔
246
      case VariantType::Int32:
83✔
247
        return convertNumber<T>(data_->content.asInt32);
83✔
248
#if ARDUINOJSON_USE_LONG_LONG
249
      case VariantType::Uint64:
10✔
250
        return convertNumber<T>(eightByteValue->asUint64);
10✔
251
      case VariantType::Int64:
11✔
252
        return convertNumber<T>(eightByteValue->asInt64);
11✔
253
#endif
254
      case VariantType::TinyString:
3✔
255
        str = data_->content.asTinyString;
3✔
256
        break;
3✔
257
      case VariantType::LongString:
6✔
258
        str = data_->content.asStringNode->data;
6✔
259
        break;
6✔
260
      case VariantType::Float:
5✔
261
        return convertNumber<T>(data_->content.asFloat);
5✔
262
#if ARDUINOJSON_USE_DOUBLE
263
      case VariantType::Double:
10✔
264
        return convertNumber<T>(eightByteValue->asDouble);
10✔
265
#endif
266
      default:
2✔
267
        return 0;
2✔
268
    }
269

270
    ARDUINOJSON_ASSERT(str != nullptr);
271
    return parseNumber<T>(str);
9✔
272
  }
273

274
  ObjectImpl asObject() {
2,396✔
275
    return asObject(data_, resources_);
2,396✔
276
  }
277

278
  static ObjectImpl asObject(VariantData* data, ResourceManager* resources) {
5,685✔
279
    return ObjectImpl(data && data->type == VariantType::Object
5,685✔
280
                          ? &data->content.asCollection
281
                          : nullptr,
282
                      resources);
5,685✔
283
  }
284

285
  JsonString asRawString() const {
50✔
286
    switch (type()) {
50✔
287
      case VariantType::RawString:
26✔
288
        return JsonString(data_->content.asStringNode->data,
52✔
289
                          data_->content.asStringNode->length);
26✔
290
      default:
24✔
291
        return JsonString();
24✔
292
    }
293
  }
294

295
  JsonString asString() const {
1,066✔
296
    if (!data_)
1,066✔
297
      return JsonString();
12✔
298
    return asString(data_, resources_);
1,054✔
299
  }
300

301
  static JsonString asString(VariantData* data_, ResourceManager* resources_) {
12,767✔
302
    ARDUINOJSON_ASSERT(data_ != nullptr);
303
    ARDUINOJSON_ASSERT(resources_ != nullptr);
304

305
    switch (data_->type) {
12,767✔
306
      case VariantType::TinyString:
2,792✔
307
        return JsonString(data_->content.asTinyString);
2,792✔
308
      case VariantType::LongString:
9,545✔
309
        return JsonString(data_->content.asStringNode->data,
19,090✔
310
                          data_->content.asStringNode->length);
9,545✔
311
      default:
430✔
312
        return JsonString();
430✔
313
    }
314
  }
315

316
#if ARDUINOJSON_USE_8_BYTE_POOL
317
  const EightByteValue* getEightByte() const;
318
#endif
319

320
  VariantData* getElement(size_t index) {
471✔
321
    return asArray().getElement(index);
471✔
322
  }
323

324
  template <typename TAdaptedString>
325
  VariantData* getMember(TAdaptedString key) {
2,186✔
326
    return asObject().getMember(key);
2,186✔
327
  }
328

329
  VariantData* getOrAddElement(size_t index) {
153✔
330
    auto array = isNull() ? toArray() : asArray();
153✔
331
    return array.getOrAddElement(index);
306✔
332
  }
333

334
  template <typename TAdaptedString>
335
  VariantData* getOrAddMember(TAdaptedString key) {
950✔
336
    return getOrAddMember(key, data_, resources_);
950✔
337
  }
338

339
  template <typename TAdaptedString>
340
  static VariantData* getOrAddMember(TAdaptedString key, VariantData* data,
950✔
341
                                     ResourceManager* resources) {
342
    if (key.isNull())
950✔
343
      return nullptr;
1✔
344
    if (!data)
949✔
345
      return nullptr;
3✔
346
    auto obj = data->type == VariantType::Null ? toObject(data, resources)
946✔
347
                                               : asObject(data, resources);
580✔
348
    return obj.getOrAddMember(key);
946✔
349
  }
350

351
  bool isArray() const {
32✔
352
    return type() == VariantType::Array;
32✔
353
  }
354

355
  bool isBoolean() const {
356
    return type() == VariantType::Boolean;
357
  }
358

359
  bool isCollection() const {
30✔
360
    return type() & VariantTypeBits::CollectionMask;
30✔
361
  }
362

363
  bool isFloat() const {
364
    return data_ && data_->isFloat();
365
  }
366

367
  template <typename T>
368
  bool isInteger() const {
167✔
369
    if (!data_)
167✔
370
      return false;
16✔
371

372
#if ARDUINOJSON_USE_LONG_LONG
373
    auto eightByteValue = getEightByte();
148✔
374
#endif
375
    switch (data_->type) {
151✔
376
      case VariantType::Uint32:
11✔
377
        return canConvertNumber<T>(data_->content.asUint32);
11✔
378

379
      case VariantType::Int32:
65✔
380
        return canConvertNumber<T>(data_->content.asInt32);
65✔
381

382
#if ARDUINOJSON_USE_LONG_LONG
383
      case VariantType::Uint64:
3✔
384
        return canConvertNumber<T>(eightByteValue->asUint64);
3✔
385

386
      case VariantType::Int64:
4✔
387
        return canConvertNumber<T>(eightByteValue->asInt64);
4✔
388
#endif
389

390
      default:
68✔
391
        return false;
68✔
392
    }
393
  }
394

395
  bool isNull() const {
1,441✔
396
    return type() == VariantType::Null;
1,441✔
397
  }
398

399
  bool isObject() const {
46✔
400
    return type() == VariantType::Object;
46✔
401
  }
402

403
  bool isString() const {
404
    return data_ && data_->isString();
405
  }
406

407
  size_t nesting() {
30✔
408
    return asCollection().nesting();
30✔
409
  }
410

411
  void removeElement(size_t index) {
10✔
412
    asArray().removeElement(index);
10✔
413
  }
10✔
414

415
  template <typename TAdaptedString>
416
  void removeMember(TAdaptedString key) {
12✔
417
    asObject().removeMember(key);
12✔
418
  }
12✔
419

420
  bool setBoolean(bool value) {
421
    if (!data_)
422
      return false;
423
    data_->setBoolean(value);
424
    return true;
425
  }
426

427
  template <typename T>
428
  bool setFloat(T value) {
429
    if (!data_)
430
      return false;
431
    return setFloat(value, data_, resources_);
432
  }
433

434
  template <typename T>
435
  static enable_if_t<sizeof(T) == 4, bool> setFloat(T value, VariantData* data_,
90✔
436
                                                    ResourceManager*) {
437
    ARDUINOJSON_ASSERT(data_ != nullptr);
438
    ARDUINOJSON_ASSERT(data_->type == VariantType::Null);
439
    data_->type = VariantType::Float;
90✔
440
    data_->content.asFloat = value;
90✔
441
    return true;
90✔
442
  }
443

444
  template <typename T>
445
  static enable_if_t<sizeof(T) == 8, bool> setFloat(
446
      T value, VariantData* data_, ResourceManager* resources_);
447

448
  template <typename T>
449
  bool setInteger(T value) {
450
    if (!data_)
451
      return false;
452
    return setInteger(value, data_, resources_);
453
  }
454

455
  template <typename T>
456
  static enable_if_t<is_signed<T>::value, bool> setInteger(T value,
457
                                                           VariantData*,
458
                                                           ResourceManager*);
459

460
  template <typename T>
461
  static enable_if_t<is_unsigned<T>::value, bool> setInteger(T value,
462
                                                             VariantData*,
463
                                                             ResourceManager*);
464

465
  template <typename T>
466
  void setRawString(SerializedValue<T> value);
467

468
  template <typename TAdaptedString>
469
  bool setString(TAdaptedString value) {
470
    if (!data_)
471
      return false;
472
    return setString(value, data_, resources_);
473
  }
474

475
  template <typename TAdaptedString>
476
  static bool setString(TAdaptedString value, VariantData*, ResourceManager*);
477

478
  size_t size() {
46✔
479
    if (isObject())
46✔
480
      return asObject().size();
14✔
481

482
    if (isArray())
32✔
483
      return asArray().size();
16✔
484

485
    return 0;
16✔
486
  }
487

488
  ArrayImpl toArray() {
340✔
489
    if (!data_)
340✔
490
      return ArrayImpl();
1✔
491
    return toArray(data_, resources_);
339✔
492
  }
493

494
  static ArrayImpl toArray(VariantData* data_, ResourceManager* resources_) {
1,269✔
495
    ARDUINOJSON_ASSERT(data_ != nullptr);
496
    ARDUINOJSON_ASSERT(data_->type == VariantType::Null);
497
    ARDUINOJSON_ASSERT(resources_ != nullptr);
498
    data_->type = VariantType::Array;
1,269✔
499
    return ArrayImpl(new (&data_->content.asCollection) CollectionData(),
2,538✔
500
                     resources_);
1,269✔
501
  }
502

503
  ObjectImpl toObject() {
307✔
504
    if (!data_)
307✔
UNCOV
505
      return ObjectImpl();
×
506
    return toObject(data_, resources_);
307✔
507
  }
508

509
  static ObjectImpl toObject(VariantData* data_, ResourceManager* resources_) {
1,336✔
510
    ARDUINOJSON_ASSERT(data_ != nullptr);
511
    ARDUINOJSON_ASSERT(data_->type == VariantType::Null);
512
    ARDUINOJSON_ASSERT(resources_ != nullptr);
513
    data_->type = VariantType::Object;
1,336✔
514
    return ObjectImpl(new (&data_->content.asCollection) CollectionData(),
2,672✔
515
                      resources_);
1,336✔
516
  }
517

518
  VariantType type() const {
2,554✔
519
    return data_ ? data_->type : VariantType::Null;
2,554✔
520
  }
521

522
  // Release the resources used by this variant and set it to null.
523
  void clear() {
1,132✔
524
    if (data_)
1,132✔
525
      clear(data_, resources_);
1,130✔
526
  }
1,132✔
527

528
  static void clear(VariantData* data, ResourceManager* resources);
529

530
 private:
531
  VariantData* data_;
532
  ResourceManager* resources_;
533
};
534

535
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