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

bblanchon / ArduinoJson / 10581347216

27 Aug 2024 02:08PM UTC coverage: 99.522% (+0.3%) from 99.194%
10581347216

push

github

bblanchon
Test `JsonVariant::as<T>()` with extension slots

3954 of 3973 relevant lines covered (99.52%)

10535.96 hits per line

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

98.95
/src/ArduinoJson/Variant/VariantData.hpp
1
// ArduinoJson - https://arduinojson.org
2
// Copyright © 2014-2024, 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
class VariantData {
21
  VariantContent content_;  // must be first to allow cast from array to variant
22
  uint8_t type_;
23
  SlotId next_;
24

25
 public:
26
  // Placement new
27
  static void* operator new(size_t, void* p) noexcept {
74,670✔
28
    return p;
74,670✔
29
  }
30

31
  static void operator delete(void*, void*) noexcept {}
32

33
  VariantData() : type_(VALUE_IS_NULL), next_(NULL_SLOT) {}
76,653✔
34

35
  SlotId next() const {
291,764✔
36
    return next_;
291,764✔
37
  }
38

39
  void setNext(SlotId slot) {
71,826✔
40
    next_ = slot;
71,826✔
41
  }
71,826✔
42

43
  template <typename TVisitor>
44
  typename TVisitor::result_type accept(
139,240✔
45
      TVisitor& visit, const ResourceManager* resources) const {
46
    (void)resources;  // silence warning
47
    switch (type_) {
139,240✔
48
      case VALUE_IS_FLOAT:
60✔
49
        return visit.visit(static_cast<JsonFloat>(content_.asFloat));
60✔
50

51
#if ARDUINOJSON_USE_DOUBLE
52
      case VALUE_IS_DOUBLE:
76✔
53
        return visit.visit(getExtension(resources)->asDouble);
76✔
54
#endif
55

56
      case VALUE_IS_ARRAY:
967✔
57
        return visit.visit(content_.asArray);
967✔
58

59
      case VALUE_IS_OBJECT:
2,661✔
60
        return visit.visit(content_.asObject);
2,661✔
61

62
      case VALUE_IS_LINKED_STRING:
1,160✔
63
        return visit.visit(JsonString(content_.asLinkedString));
1,160✔
64

65
      case VALUE_IS_OWNED_STRING:
917✔
66
        return visit.visit(JsonString(content_.asOwnedString->data,
917✔
67
                                      content_.asOwnedString->length,
917✔
68
                                      JsonString::Copied));
917✔
69

70
      case VALUE_IS_RAW_STRING:
125✔
71
        return visit.visit(RawString(content_.asOwnedString->data,
125✔
72
                                     content_.asOwnedString->length));
125✔
73

74
      case VALUE_IS_INT32:
1,173✔
75
        return visit.visit(static_cast<JsonInteger>(content_.asInt32));
1,173✔
76

77
      case VALUE_IS_UINT32:
254✔
78
        return visit.visit(static_cast<JsonUInt>(content_.asUint32));
254✔
79

80
#if ARDUINOJSON_USE_LONG_LONG
81
      case VALUE_IS_INT64:
7✔
82
        return visit.visit(getExtension(resources)->asInt64);
7✔
83

84
      case VALUE_IS_UINT64:
8✔
85
        return visit.visit(getExtension(resources)->asUint64);
8✔
86
#endif
87

88
      case VALUE_IS_BOOLEAN:
606✔
89
        return visit.visit(content_.asBoolean != 0);
606✔
90

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

96
  template <typename TVisitor>
97
  static typename TVisitor::result_type accept(const VariantData* var,
699✔
98
                                               const ResourceManager* resources,
99
                                               TVisitor& visit) {
100
    if (var != 0)
699✔
101
      return var->accept(visit, resources);
696✔
102
    else
103
      return visit.visit(nullptr);
3✔
104
  }
105

106
  VariantData* addElement(ResourceManager* resources) {
157✔
107
    auto array = isNull() ? &toArray() : asArray();
157✔
108
    return detail::ArrayData::addElement(array, resources);
157✔
109
  }
110

111
  static VariantData* addElement(VariantData* var, ResourceManager* resources) {
41✔
112
    if (!var)
41✔
113
      return nullptr;
6✔
114
    return var->addElement(resources);
35✔
115
  }
116

117
  template <typename T>
118
  bool addValue(T&& value, ResourceManager* resources) {
124✔
119
    auto array = isNull() ? &toArray() : asArray();
124✔
120
    return detail::ArrayData::addValue(array, detail::forward<T>(value),
124✔
121
                                       resources);
124✔
122
  }
123

124
  template <typename T>
125
  static bool addValue(VariantData* var, T&& value,
43✔
126
                       ResourceManager* resources) {
127
    if (!var)
43✔
128
      return false;
6✔
129
    return var->addValue(value, resources);
37✔
130
  }
131

132
  bool asBoolean(const ResourceManager* resources) const {
553✔
133
    (void)resources;  // silence warning
134
    switch (type_) {
553✔
135
      case VALUE_IS_BOOLEAN:
304✔
136
        return content_.asBoolean;
304✔
137
      case VALUE_IS_UINT32:
6✔
138
      case VALUE_IS_INT32:
139
        return content_.asUint32 != 0;
6✔
140
      case VALUE_IS_FLOAT:
2✔
141
        return content_.asFloat != 0;
2✔
142
#if ARDUINOJSON_USE_DOUBLE
143
      case VALUE_IS_DOUBLE:
4✔
144
        return getExtension(resources)->asDouble != 0;
4✔
145
#endif
146
      case VALUE_IS_NULL:
6✔
147
        return false;
6✔
148
#if ARDUINOJSON_USE_LONG_LONG
149
      case VALUE_IS_UINT64:
2✔
150
      case VALUE_IS_INT64:
151
        return getExtension(resources)->asUint64 != 0;
2✔
152
#endif
153
      default:
229✔
154
        return true;
229✔
155
    }
156
  }
157

158
  ArrayData* asArray() {
760✔
159
    return isArray() ? &content_.asArray : 0;
760✔
160
  }
161

162
  const ArrayData* asArray() const {
481✔
163
    return const_cast<VariantData*>(this)->asArray();
481✔
164
  }
165

166
  CollectionData* asCollection() {
69,357✔
167
    return isCollection() ? &content_.asCollection : 0;
69,357✔
168
  }
169

170
  const CollectionData* asCollection() const {
44✔
171
    return const_cast<VariantData*>(this)->asCollection();
44✔
172
  }
173

174
  template <typename T>
175
  T asFloat(const ResourceManager* resources) const {
47✔
176
    static_assert(is_floating_point<T>::value, "T must be a floating point");
177
    (void)resources;  // silence warning
178
    switch (type_) {
47✔
179
      case VALUE_IS_BOOLEAN:
2✔
180
        return static_cast<T>(content_.asBoolean);
2✔
181
      case VALUE_IS_UINT32:
2✔
182
        return static_cast<T>(content_.asUint32);
2✔
183
      case VALUE_IS_INT32:
4✔
184
        return static_cast<T>(content_.asInt32);
4✔
185
#if ARDUINOJSON_USE_LONG_LONG
186
      case VALUE_IS_UINT64:
1✔
187
        return static_cast<T>(getExtension(resources)->asUint64);
1✔
188
      case VALUE_IS_INT64:
1✔
189
        return static_cast<T>(getExtension(resources)->asInt64);
1✔
190
#endif
191
      case VALUE_IS_LINKED_STRING:
1✔
192
      case VALUE_IS_OWNED_STRING:
193
        return parseNumber<T>(content_.asOwnedString->data);
1✔
194
      case VALUE_IS_FLOAT:
15✔
195
        return static_cast<T>(content_.asFloat);
15✔
196
#if ARDUINOJSON_USE_DOUBLE
197
      case VALUE_IS_DOUBLE:
19✔
198
        return static_cast<T>(getExtension(resources)->asDouble);
19✔
199
#endif
200
      default:
2✔
201
        return 0;
2✔
202
    }
203
  }
204

205
  template <typename T>
206
  T asIntegral(const ResourceManager* resources) const {
197✔
207
    static_assert(is_integral<T>::value, "T must be an integral type");
208
    (void)resources;  // silence warning
209
    switch (type_) {
197✔
210
      case VALUE_IS_BOOLEAN:
2✔
211
        return content_.asBoolean;
2✔
212
      case VALUE_IS_UINT32:
66✔
213
        return convertNumber<T>(content_.asUint32);
66✔
214
      case VALUE_IS_INT32:
83✔
215
        return convertNumber<T>(content_.asInt32);
83✔
216
#if ARDUINOJSON_USE_LONG_LONG
217
      case VALUE_IS_UINT64:
10✔
218
        return convertNumber<T>(getExtension(resources)->asUint64);
10✔
219
      case VALUE_IS_INT64:
11✔
220
        return convertNumber<T>(getExtension(resources)->asInt64);
11✔
221
#endif
222
      case VALUE_IS_LINKED_STRING:
6✔
223
        return parseNumber<T>(content_.asLinkedString);
6✔
224
      case VALUE_IS_OWNED_STRING:
2✔
225
        return parseNumber<T>(content_.asOwnedString->data);
2✔
226
      case VALUE_IS_FLOAT:
5✔
227
        return convertNumber<T>(content_.asFloat);
5✔
228
#if ARDUINOJSON_USE_DOUBLE
229
      case VALUE_IS_DOUBLE:
10✔
230
        return convertNumber<T>(getExtension(resources)->asDouble);
10✔
231
#endif
232
      default:
2✔
233
        return 0;
2✔
234
    }
235
  }
236

237
  ObjectData* asObject() {
2,845✔
238
    return isObject() ? &content_.asObject : 0;
2,845✔
239
  }
240

241
  const ObjectData* asObject() const {
2,175✔
242
    return const_cast<VariantData*>(this)->asObject();
2,175✔
243
  }
244

245
  JsonString asRawString() const {
46✔
246
    switch (type_) {
46✔
247
      case VALUE_IS_RAW_STRING:
26✔
248
        return JsonString(content_.asOwnedString->data,
26✔
249
                          content_.asOwnedString->length, JsonString::Copied);
26✔
250
      default:
20✔
251
        return JsonString();
20✔
252
    }
253
  }
254

255
  JsonString asString() const {
12,371✔
256
    switch (type_) {
12,371✔
257
      case VALUE_IS_LINKED_STRING:
4,512✔
258
        return JsonString(content_.asLinkedString, JsonString::Linked);
4,512✔
259
      case VALUE_IS_OWNED_STRING:
7,453✔
260
        return JsonString(content_.asOwnedString->data,
7,453✔
261
                          content_.asOwnedString->length, JsonString::Copied);
7,453✔
262
      default:
406✔
263
        return JsonString();
406✔
264
    }
265
  }
266

267
#if ARDUINOJSON_USE_EXTENSIONS
268
  const VariantExtension* getExtension(const ResourceManager* resources) const;
269
#endif
270

271
  VariantData* getElement(size_t index,
448✔
272
                          const ResourceManager* resources) const {
273
    return ArrayData::getElement(asArray(), index, resources);
448✔
274
  }
275

276
  static VariantData* getElement(const VariantData* var, size_t index,
451✔
277
                                 const ResourceManager* resources) {
278
    return var != 0 ? var->getElement(index, resources) : 0;
451✔
279
  }
280

281
  template <typename TAdaptedString>
282
  VariantData* getMember(TAdaptedString key,
2,141✔
283
                         const ResourceManager* resources) const {
284
    return ObjectData::getMember(asObject(), key, resources);
2,141✔
285
  }
286

287
  template <typename TAdaptedString>
288
  static VariantData* getMember(const VariantData* var, TAdaptedString key,
2,141✔
289
                                const ResourceManager* resources) {
290
    if (!var)
2,141✔
291
      return 0;
13✔
292
    return var->getMember(key, resources);
2,128✔
293
  }
294

295
  VariantData* getOrAddElement(size_t index, ResourceManager* resources) {
150✔
296
    auto array = isNull() ? &toArray() : asArray();
150✔
297
    if (!array)
150✔
298
      return nullptr;
1✔
299
    return array->getOrAddElement(index, resources);
149✔
300
  }
301

302
  template <typename TAdaptedString>
303
  VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources) {
899✔
304
    if (key.isNull())
899✔
305
      return nullptr;
1✔
306
    auto obj = isNull() ? &toObject() : asObject();
898✔
307
    if (!obj)
898✔
308
      return nullptr;
×
309
    return obj->getOrAddMember(key, resources);
898✔
310
  }
311

312
  bool isArray() const {
877✔
313
    return type_ == VALUE_IS_ARRAY;
877✔
314
  }
315

316
  bool isBoolean() const {
35✔
317
    return type_ == VALUE_IS_BOOLEAN;
35✔
318
  }
319

320
  bool isCollection() const {
69,357✔
321
    return (type_ & COLLECTION_MASK) != 0;
69,357✔
322
  }
323

324
  bool isFloat() const {
405✔
325
    return (type_ & NUMBER_BIT) != 0;
405✔
326
  }
327

328
  template <typename T>
329
  bool isInteger(const ResourceManager* resources) const {
141✔
330
    (void)resources;  // silence warning
331
    switch (type_) {
141✔
332
      case VALUE_IS_UINT32:
11✔
333
        return canConvertNumber<T>(content_.asUint32);
11✔
334

335
      case VALUE_IS_INT32:
65✔
336
        return canConvertNumber<T>(content_.asInt32);
65✔
337

338
#if ARDUINOJSON_USE_LONG_LONG
339
      case VALUE_IS_UINT64:
3✔
340
        return canConvertNumber<T>(getExtension(resources)->asUint64);
3✔
341

342
      case VALUE_IS_INT64:
4✔
343
        return canConvertNumber<T>(getExtension(resources)->asInt64);
4✔
344
#endif
345

346
      default:
58✔
347
        return false;
58✔
348
    }
349
  }
350

351
  bool isNull() const {
1,881✔
352
    return type_ == VALUE_IS_NULL;
1,881✔
353
  }
354

355
  static bool isNull(const VariantData* var) {
1,280✔
356
    if (!var)
1,280✔
357
      return true;
731✔
358
    return var->isNull();
549✔
359
  }
360

361
  bool isObject() const {
3,229✔
362
    return type_ == VALUE_IS_OBJECT;
3,229✔
363
  }
364

365
  bool isString() const {
71✔
366
    return type_ == VALUE_IS_LINKED_STRING || type_ == VALUE_IS_OWNED_STRING;
71✔
367
  }
368

369
  size_t nesting(const ResourceManager* resources) const {
44✔
370
    auto collection = asCollection();
44✔
371
    if (collection)
44✔
372
      return collection->nesting(resources);
30✔
373
    else
374
      return 0;
14✔
375
  }
376

377
  static size_t nesting(const VariantData* var,
28✔
378
                        const ResourceManager* resources) {
379
    if (!var)
28✔
380
      return 0;
6✔
381
    return var->nesting(resources);
22✔
382
  }
383

384
  void removeElement(size_t index, ResourceManager* resources) {
9✔
385
    ArrayData::removeElement(asArray(), index, resources);
9✔
386
  }
9✔
387

388
  static void removeElement(VariantData* var, size_t index,
10✔
389
                            ResourceManager* resources) {
390
    if (!var)
10✔
391
      return;
1✔
392
    var->removeElement(index, resources);
9✔
393
  }
394

395
  template <typename TAdaptedString>
396
  void removeMember(TAdaptedString key, ResourceManager* resources) {
14✔
397
    ObjectData::removeMember(asObject(), key, resources);
14✔
398
  }
14✔
399

400
  template <typename TAdaptedString>
401
  static void removeMember(VariantData* var, TAdaptedString key,
15✔
402
                           ResourceManager* resources) {
403
    if (!var)
15✔
404
      return;
1✔
405
    var->removeMember(key, resources);
14✔
406
  }
407

408
  void reset() {  // TODO: remove
1,898✔
409
    type_ = VALUE_IS_NULL;
1,898✔
410
  }
1,898✔
411

412
  void setBoolean(bool value) {
329✔
413
    ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL);  // must call clear() first
414
    type_ = VALUE_IS_BOOLEAN;
329✔
415
    content_.asBoolean = value;
329✔
416
  }
329✔
417

418
  template <typename T>
419
  enable_if_t<sizeof(T) == 4, bool> setFloat(T value, ResourceManager*) {
14✔
420
    ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL);  // must call clear() first
421
    type_ = VALUE_IS_FLOAT;
14✔
422
    content_.asFloat = value;
14✔
423
    return true;
14✔
424
  }
425

426
  template <typename T>
427
  enable_if_t<sizeof(T) == 8, bool> setFloat(T value, ResourceManager*);
428

429
  template <typename T>
430
  enable_if_t<is_signed<T>::value, bool> setInteger(T value,
431
                                                    ResourceManager* resources);
432

433
  template <typename T>
434
  enable_if_t<is_unsigned<T>::value, bool> setInteger(
435
      T value, ResourceManager* resources);
436

437
  void setRawString(StringNode* s) {
87✔
438
    ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL);  // must call clear() first
439
    ARDUINOJSON_ASSERT(s);
440
    type_ = VALUE_IS_RAW_STRING;
87✔
441
    content_.asOwnedString = s;
87✔
442
  }
87✔
443

444
  template <typename T>
445
  void setRawString(SerializedValue<T> value, ResourceManager* resources);
446

447
  template <typename T>
448
  static void setRawString(VariantData* var, SerializedValue<T> value,
36✔
449
                           ResourceManager* resources) {
450
    if (!var)
36✔
451
      return;
2✔
452
    var->clear(resources);
34✔
453
    var->setRawString(value, resources);
34✔
454
  }
455

456
  template <typename TAdaptedString>
457
  bool setString(TAdaptedString value, ResourceManager* resources);
458

459
  bool setString(StringNode* s, ResourceManager*) {
1,332✔
460
    setOwnedString(s);
1,332✔
461
    return true;
1,332✔
462
  }
463

464
  template <typename TAdaptedString>
465
  static void setString(VariantData* var, TAdaptedString value,
66,080✔
466
                        ResourceManager* resources) {
467
    if (!var)
66,080✔
468
      return;
5✔
469
    var->clear(resources);
66,075✔
470
    var->setString(value, resources);
66,075✔
471
  }
472

473
  void setLinkedString(const char* s) {
1,152✔
474
    ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL);  // must call clear() first
475
    ARDUINOJSON_ASSERT(s);
476
    type_ = VALUE_IS_LINKED_STRING;
1,152✔
477
    content_.asLinkedString = s;
1,152✔
478
  }
1,152✔
479

480
  void setOwnedString(StringNode* s) {
1,929✔
481
    ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL);  // must call clear() first
482
    ARDUINOJSON_ASSERT(s);
483
    type_ = VALUE_IS_OWNED_STRING;
1,929✔
484
    content_.asOwnedString = s;
1,929✔
485
  }
1,929✔
486

487
  size_t size(const ResourceManager* resources) const {
42✔
488
    if (isObject())
42✔
489
      return content_.asObject.size(resources);
14✔
490

491
    if (isArray())
28✔
492
      return content_.asArray.size(resources);
15✔
493

494
    return 0;
13✔
495
  }
496

497
  static size_t size(const VariantData* var, const ResourceManager* resources) {
30✔
498
    return var != 0 ? var->size(resources) : 0;
30✔
499
  }
500

501
  ArrayData& toArray() {
1,260✔
502
    ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL);  // must call clear() first
503
    type_ = VALUE_IS_ARRAY;
1,260✔
504
    new (&content_.asArray) ArrayData();
2,520✔
505
    return content_.asArray;
1,260✔
506
  }
507

508
  static ArrayData* toArray(VariantData* var, ResourceManager* resources) {
261✔
509
    if (!var)
261✔
510
      return 0;
×
511
    var->clear(resources);
261✔
512
    return &var->toArray();
261✔
513
  }
514

515
  ObjectData& toObject() {
1,324✔
516
    ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL);  // must call clear() first
517
    type_ = VALUE_IS_OBJECT;
1,324✔
518
    new (&content_.asObject) ObjectData();
2,648✔
519
    return content_.asObject;
1,324✔
520
  }
521

522
  static ObjectData* toObject(VariantData* var, ResourceManager* resources) {
315✔
523
    if (!var)
315✔
524
      return 0;
×
525
    var->clear(resources);
315✔
526
    return &var->toObject();
315✔
527
  }
528

529
  uint8_t type() const {
530
    return type_;
531
  }
532

533
  // Release the resources used by this variant and set it to null.
534
  void clear(ResourceManager* resources);
535

536
  static void clear(VariantData* var, ResourceManager* resources) {
501✔
537
    if (!var)
501✔
538
      return;
1✔
539
    var->clear(resources);
500✔
540
  }
541
};
542

543
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