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

bblanchon / ArduinoJson / 15905238804

26 Jun 2025 02:53PM UTC coverage: 99.35% (+0.04%) from 99.31%
15905238804

push

github

bblanchon
Extract `VariantImpl`

393 of 394 new or added lines in 30 files covered. (99.75%)

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.62
/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) {
2,057✔
22
  if (n > tinyStringMaxLength)
2,057✔
23
    return false;
1,765✔
24
  bool containsNul = false;
292✔
25
  for (uint8_t i = 0; i < uint8_t(n); i++)
934✔
26
    containsNul |= !s[i];
642✔
27
  return !containsNul;
292✔
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,804✔
37
    return p;
74,804✔
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) {
280✔
50
    ARDUINOJSON_ASSERT(type == VariantType::Null);
51
    ARDUINOJSON_ASSERT(s.size() <= tinyStringMaxLength);
52

53
    type = VariantType::TinyString;
280✔
54

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

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

65
  void setOwnedString(StringNode* s) {
1,765✔
66
    ARDUINOJSON_ASSERT(type == VariantType::Null);
67
    ARDUINOJSON_ASSERT(s);
68
    type = VariantType::OwnedString;
1,765✔
69
    content.asOwnedString = s;
1,765✔
70
  }
1,765✔
71

72
  void setRawString(StringNode* s) {
87✔
73
    ARDUINOJSON_ASSERT(type == VariantType::Null);
74
    ARDUINOJSON_ASSERT(s);
75
    type = VariantType::RawString;
87✔
76
    content.asOwnedString = 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::LinkedString ||
136✔
85
           type == VariantType::OwnedString || type == VariantType::TinyString;
80✔
86
  }
87
};
88

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

93
  VariantImpl(VariantData* data, ResourceManager* resources)
386,458✔
94
      : data_(data), resources_(resources) {}
386,458✔
95

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

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

104
  template <typename TVisitor>
105
  typename TVisitor::result_type accept(TVisitor& visit) {
139,520✔
106
    if (!data_)
139,520✔
107
      return visit.visit(nullptr);
170✔
108

109
#if ARDUINOJSON_USE_8_BYTE_POOL
110
    auto eightByteValue = getEightByte();
139,350✔
111
#endif
112
    switch (data_->type) {
139,350✔
113
      case VariantType::Float:
121✔
114
        return visit.visit(data_->content.asFloat);
121✔
115

116
#if ARDUINOJSON_USE_DOUBLE
117
      case VariantType::Double:
16✔
118
        return visit.visit(eightByteValue->asDouble);
16✔
119
#endif
120

121
      case VariantType::Array:
983✔
122
        return visit.visit(asArray());
983✔
123

124
      case VariantType::Object:
2,681✔
125
        return visit.visit(asObject());
2,681✔
126

127
      case VariantType::TinyString:
142✔
128
        return visit.visit(JsonString(data_->content.asTinyString));
142✔
129

130
      case VariantType::LinkedString:
1,155✔
131
        return visit.visit(JsonString(asLinkedString(), true));
1,155✔
132

133
      case VariantType::OwnedString:
833✔
134
        return visit.visit(JsonString(data_->content.asOwnedString->data,
1,666✔
135
                                      data_->content.asOwnedString->length));
1,666✔
136

137
      case VariantType::RawString:
129✔
138
        return visit.visit(RawString(data_->content.asOwnedString->data,
258✔
139
                                     data_->content.asOwnedString->length));
258✔
140

141
      case VariantType::Int32:
1,185✔
142
        return visit.visit(static_cast<JsonInteger>(data_->content.asInt32));
1,185✔
143

144
      case VariantType::Uint32:
256✔
145
        return visit.visit(static_cast<JsonUInt>(data_->content.asUint32));
256✔
146

147
#if ARDUINOJSON_USE_LONG_LONG
148
      case VariantType::Int64:
7✔
149
        return visit.visit(eightByteValue->asInt64);
7✔
150

151
      case VariantType::Uint64:
8✔
152
        return visit.visit(eightByteValue->asUint64);
8✔
153
#endif
154

155
      case VariantType::Boolean:
607✔
156
        return visit.visit(data_->content.asBoolean != 0);
607✔
157

158
      default:
131,227✔
159
        return visit.visit(nullptr);
131,227✔
160
    }
161
  }
162

163
  VariantData* addElement() {
165✔
164
    auto array = isNull() ? toArray() : asArray();
165✔
165
    return array.addElement();
330✔
166
  }
167

168
  template <typename T>
169
  bool addValue(const T& value) {
139✔
170
    auto array = isNull() ? toArray() : asArray();
139✔
171
    return array.addValue(value);
278✔
172
  }
173

174
  bool asBoolean() const {
1,254✔
175
    if (!data_)
1,254✔
176
      return false;
699✔
177

178
#if ARDUINOJSON_USE_8_BYTE_POOL
179
    auto eightByteValue = getEightByte();
555✔
180
#endif
181
    switch (data_->type) {
555✔
182
      case VariantType::Boolean:
305✔
183
        return data_->content.asBoolean;
305✔
184
      case VariantType::Uint32:
6✔
185
      case VariantType::Int32:
186
        return data_->content.asUint32 != 0;
6✔
187
      case VariantType::Float:
2✔
188
        return data_->content.asFloat != 0;
2✔
189
#if ARDUINOJSON_USE_DOUBLE
190
      case VariantType::Double:
4✔
191
        return eightByteValue->asDouble != 0;
4✔
192
#endif
193
      case VariantType::Null:
6✔
194
        return false;
6✔
195
#if ARDUINOJSON_USE_LONG_LONG
196
      case VariantType::Uint64:
2✔
197
      case VariantType::Int64:
198
        return eightByteValue->asUint64 != 0;
2✔
199
#endif
200
      default:
230✔
201
        return true;
230✔
202
    }
203
  }
204

205
  ArrayImpl asArray() {
1,831✔
206
    return ArrayImpl(isArray() ? &data_->content.asCollection : nullptr,
1,831✔
207
                     resources_);
1,831✔
208
  }
209

210
  CollectionImpl asCollection() {
69,462✔
211
    return CollectionImpl(
69,462✔
212
        isCollection() ? &data_->content.asCollection : nullptr, resources_);
138,924✔
213
  }
214

215
  template <typename T>
216
  T asFloat() const {
55✔
217
    if (!data_)
55✔
218
      return 0.0;
1✔
219

220
    static_assert(is_floating_point<T>::value, "T must be a floating point");
221
#if ARDUINOJSON_USE_8_BYTE_POOL
222
    auto eightByteValue = getEightByte();
54✔
223
#endif
224
    const char* str = nullptr;
54✔
225
    switch (data_->type) {
54✔
226
      case VariantType::Boolean:
2✔
227
        return static_cast<T>(data_->content.asBoolean);
2✔
228
      case VariantType::Uint32:
2✔
229
        return static_cast<T>(data_->content.asUint32);
2✔
230
      case VariantType::Int32:
4✔
231
        return static_cast<T>(data_->content.asInt32);
4✔
232
#if ARDUINOJSON_USE_LONG_LONG
233
      case VariantType::Uint64:
1✔
234
        return static_cast<T>(eightByteValue->asUint64);
1✔
235
      case VariantType::Int64:
1✔
236
        return static_cast<T>(eightByteValue->asInt64);
1✔
237
#endif
238
      case VariantType::TinyString:
1✔
239
        str = data_->content.asTinyString;
1✔
240
        break;
1✔
241
      case VariantType::LinkedString:
1✔
242
        str = asLinkedString();
1✔
243
        break;
1✔
244
      case VariantType::OwnedString:
1✔
245
        str = data_->content.asOwnedString->data;
1✔
246
        break;
1✔
247
      case VariantType::Float:
18✔
248
        return static_cast<T>(data_->content.asFloat);
18✔
249
#if ARDUINOJSON_USE_DOUBLE
250
      case VariantType::Double:
21✔
251
        return static_cast<T>(eightByteValue->asDouble);
21✔
252
#endif
253
      default:
2✔
254
        return 0.0;
2✔
255
    }
256

257
    ARDUINOJSON_ASSERT(str != nullptr);
258
    return parseNumber<T>(str);
3✔
259
  }
260

261
  template <typename T>
262
  T asIntegral() const {
203✔
263
    if (!data_)
203✔
264
      return 0;
5✔
265

266
    static_assert(is_integral<T>::value, "T must be an integral type");
267
#if ARDUINOJSON_USE_8_BYTE_POOL
268
    auto eightByteValue = getEightByte();
198✔
269
#endif
270
    const char* str = nullptr;
198✔
271
    switch (data_->type) {
198✔
272
      case VariantType::Boolean:
2✔
273
        return data_->content.asBoolean;
2✔
274
      case VariantType::Uint32:
66✔
275
        return convertNumber<T>(data_->content.asUint32);
66✔
276
      case VariantType::Int32:
83✔
277
        return convertNumber<T>(data_->content.asInt32);
83✔
278
#if ARDUINOJSON_USE_LONG_LONG
279
      case VariantType::Uint64:
10✔
280
        return convertNumber<T>(eightByteValue->asUint64);
10✔
281
      case VariantType::Int64:
11✔
282
        return convertNumber<T>(eightByteValue->asInt64);
11✔
283
#endif
284
      case VariantType::TinyString:
2✔
285
        str = data_->content.asTinyString;
2✔
286
        break;
2✔
287
      case VariantType::LinkedString:
6✔
288
        str = asLinkedString();
6✔
289
        break;
6✔
290
      case VariantType::OwnedString:
1✔
291
        str = data_->content.asOwnedString->data;
1✔
292
        break;
1✔
293
      case VariantType::Float:
5✔
294
        return convertNumber<T>(data_->content.asFloat);
5✔
295
#if ARDUINOJSON_USE_DOUBLE
296
      case VariantType::Double:
10✔
297
        return convertNumber<T>(eightByteValue->asDouble);
10✔
298
#endif
299
      default:
2✔
300
        return 0;
2✔
301
    }
302

303
    ARDUINOJSON_ASSERT(str != nullptr);
304
    return parseNumber<T>(str);
9✔
305
  }
306

307
  ObjectImpl asObject() {
5,697✔
308
    return ObjectImpl(isObject() ? &data_->content.asCollection : nullptr,
5,697✔
309
                      resources_);
5,697✔
310
  }
311

312
  JsonString asRawString() const {
50✔
313
    switch (type()) {
50✔
314
      case VariantType::RawString:
26✔
315
        return JsonString(data_->content.asOwnedString->data,
26✔
316
                          data_->content.asOwnedString->length);
26✔
317
      default:
24✔
318
        return JsonString();
24✔
319
    }
320
  }
321

322
  const char* asLinkedString() const;
323

324
  JsonString asString() const {
12,783✔
325
    switch (type()) {
12,783✔
326
      case VariantType::TinyString:
798✔
327
        return JsonString(data_->content.asTinyString);
798✔
328
      case VariantType::LinkedString:
4,663✔
329
        return JsonString(asLinkedString(), true);
4,663✔
330
      case VariantType::OwnedString:
6,877✔
331
        return JsonString(data_->content.asOwnedString->data,
6,877✔
332
                          data_->content.asOwnedString->length);
6,877✔
333
      default:
445✔
334
        return JsonString();
445✔
335
    }
336
  }
337

338
#if ARDUINOJSON_USE_8_BYTE_POOL
339
  const EightByteValue* getEightByte() const;
340
#endif
341

342
  VariantData* getElement(size_t index) {
471✔
343
    return asArray().getElement(index);
471✔
344
  }
345

346
  template <typename TAdaptedString>
347
  VariantData* getMember(TAdaptedString key) {
2,210✔
348
    return asObject().getMember(key);
2,210✔
349
  }
350

351
  VariantData* getOrAddElement(size_t index) {
153✔
352
    auto array = isNull() ? toArray() : asArray();
153✔
353
    return array.getOrAddElement(index);
306✔
354
  }
355

356
  template <typename TAdaptedString>
357
  VariantData* getOrAddMember(TAdaptedString key) {
962✔
358
    if (key.isNull())
962✔
359
      return nullptr;
1✔
360
    auto obj = isNull() ? toObject() : asObject();
961✔
361
    return obj.getOrAddMember(key);
961✔
362
  }
363

364
  bool isArray() const {
1,863✔
365
    return type() == VariantType::Array;
1,863✔
366
  }
367

368
  bool isBoolean() const {
369
    return type() == VariantType::Boolean;
370
  }
371

372
  bool isCollection() const {
69,462✔
373
    return type() & VariantTypeBits::CollectionMask;
69,462✔
374
  }
375

376
  bool isFloat() const {
377
    return data_ && data_->isFloat();
378
  }
379

380
  template <typename T>
381
  bool isInteger() const {
167✔
382
    if (!data_)
167✔
383
      return false;
16✔
384

385
#if ARDUINOJSON_USE_LONG_LONG
386
    auto eightByteValue = getEightByte();
148✔
387
#endif
388
    switch (data_->type) {
151✔
389
      case VariantType::Uint32:
11✔
390
        return canConvertNumber<T>(data_->content.asUint32);
11✔
391

392
      case VariantType::Int32:
65✔
393
        return canConvertNumber<T>(data_->content.asInt32);
65✔
394

395
#if ARDUINOJSON_USE_LONG_LONG
396
      case VariantType::Uint64:
3✔
397
        return canConvertNumber<T>(eightByteValue->asUint64);
3✔
398

399
      case VariantType::Int64:
4✔
400
        return canConvertNumber<T>(eightByteValue->asInt64);
4✔
401
#endif
402

403
      default:
68✔
404
        return false;
68✔
405
    }
406
  }
407

408
  bool isNull() const {
2,708✔
409
    return type() == VariantType::Null;
2,708✔
410
  }
411

412
  bool isObject() const {
5,743✔
413
    return type() == VariantType::Object;
5,743✔
414
  }
415

416
  bool isString() const {
417
    return data_ && data_->isString();
418
  }
419

420
  size_t nesting() {
30✔
421
    return asCollection().nesting();
30✔
422
  }
423

424
  void removeElement(size_t index) {
10✔
425
    asArray().removeElement(index);
10✔
426
  }
10✔
427

428
  template <typename TAdaptedString>
429
  void removeMember(TAdaptedString key) {
17✔
430
    asObject().removeMember(key);
17✔
431
  }
17✔
432

433
  bool setBoolean(bool value) {
250✔
434
    if (!data_)
250✔
435
      return false;
2✔
436
    data_->setBoolean(value);
248✔
437
    return true;
248✔
438
  }
439

440
  template <typename T>
441
  enable_if_t<sizeof(T) == 4, bool> setFloat(T value) {
90✔
442
    ARDUINOJSON_ASSERT(type() == VariantType::Null);  // must call clear() first
443
    if (!data_)
90✔
NEW
444
      return false;
×
445
    data_->type = VariantType::Float;
90✔
446
    data_->content.asFloat = value;
90✔
447
    return true;
90✔
448
  }
449

450
  template <typename T>
451
  enable_if_t<sizeof(T) == 8, bool> setFloat(T value);
452

453
  template <typename T>
454
  enable_if_t<is_signed<T>::value, bool> setInteger(T value);
455

456
  template <typename T>
457
  enable_if_t<is_unsigned<T>::value, bool> setInteger(T value);
458

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

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

465
  bool setLinkedString(const char* s);
466

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

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

474
    return 0;
16✔
475
  }
476

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

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

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

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

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

507
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