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

bblanchon / ArduinoJson / 13547532995

26 Feb 2025 03:29PM UTC coverage: 99.279% (-0.04%) from 99.323%
13547532995

push

github

bblanchon
Store static strings in a dedicated pool

52 of 53 new or added lines in 10 files covered. (98.11%)

1 existing line in 1 file now uncovered.

3995 of 4024 relevant lines covered (99.28%)

10875.16 hits per line

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

98.26
/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
class VariantData {
21
  VariantContent content_;  // must be first to allow cast from array to variant
22
  VariantType type_;
23
  SlotId next_;
24

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

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

33
  VariantData() : type_(VariantType::Null), next_(NULL_SLOT) {}
76,848✔
34

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

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

43
  template <typename TVisitor>
44
  typename TVisitor::result_type accept(
139,334✔
45
      TVisitor& visit, const ResourceManager* resources) const {
46
#if ARDUINOJSON_USE_EXTENSIONS
47
    auto extension = getExtension(resources);
139,334✔
48
#else
49
    (void)resources;  // silence warning
50
#endif
51
    switch (type_) {
139,334✔
52
      case VariantType::Float:
121✔
53
        return visit.visit(content_.asFloat);
121✔
54

55
#if ARDUINOJSON_USE_DOUBLE
56
      case VariantType::Double:
16✔
57
        return visit.visit(extension->asDouble);
16✔
58
#endif
59

60
      case VariantType::Array:
983✔
61
        return visit.visit(content_.asArray);
983✔
62

63
      case VariantType::Object:
2,677✔
64
        return visit.visit(content_.asObject);
2,677✔
65

66
      case VariantType::LinkedString:
1,153✔
67
        return visit.visit(JsonString(asLinkedString(resources), true));
1,153✔
68

69
      case VariantType::OwnedString:
967✔
70
        return visit.visit(JsonString(content_.asOwnedString->data,
967✔
71
                                      content_.asOwnedString->length));
967✔
72

73
      case VariantType::RawString:
129✔
74
        return visit.visit(RawString(content_.asOwnedString->data,
129✔
75
                                     content_.asOwnedString->length));
129✔
76

77
      case VariantType::Int32:
1,183✔
78
        return visit.visit(static_cast<JsonInteger>(content_.asInt32));
1,183✔
79

80
      case VariantType::Uint32:
256✔
81
        return visit.visit(static_cast<JsonUInt>(content_.asUint32));
256✔
82

83
#if ARDUINOJSON_USE_LONG_LONG
84
      case VariantType::Int64:
7✔
85
        return visit.visit(extension->asInt64);
7✔
86

87
      case VariantType::Uint64:
8✔
88
        return visit.visit(extension->asUint64);
8✔
89
#endif
90

91
      case VariantType::Boolean:
607✔
92
        return visit.visit(content_.asBoolean != 0);
607✔
93

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

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

109
  VariantData* addElement(ResourceManager* resources) {
159✔
110
    auto array = isNull() ? &toArray() : asArray();
159✔
111
    return detail::ArrayData::addElement(array, resources);
159✔
112
  }
113

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

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

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

134
  bool asBoolean(const ResourceManager* resources) const {
554✔
135
#if ARDUINOJSON_USE_EXTENSIONS
136
    auto extension = getExtension(resources);
554✔
137
#else
138
    (void)resources;  // silence warning
139
#endif
140
    switch (type_) {
554✔
141
      case VariantType::Boolean:
305✔
142
        return content_.asBoolean;
305✔
143
      case VariantType::Uint32:
6✔
144
      case VariantType::Int32:
145
        return content_.asUint32 != 0;
6✔
146
      case VariantType::Float:
2✔
147
        return content_.asFloat != 0;
2✔
148
#if ARDUINOJSON_USE_DOUBLE
149
      case VariantType::Double:
4✔
150
        return extension->asDouble != 0;
4✔
151
#endif
152
      case VariantType::Null:
6✔
153
        return false;
6✔
154
#if ARDUINOJSON_USE_LONG_LONG
155
      case VariantType::Uint64:
2✔
156
      case VariantType::Int64:
157
        return extension->asUint64 != 0;
2✔
158
#endif
159
      default:
229✔
160
        return true;
229✔
161
    }
162
  }
163

164
  ArrayData* asArray() {
776✔
165
    return isArray() ? &content_.asArray : 0;
776✔
166
  }
167

168
  const ArrayData* asArray() const {
496✔
169
    return const_cast<VariantData*>(this)->asArray();
496✔
170
  }
171

172
  CollectionData* asCollection() {
69,466✔
173
    return isCollection() ? &content_.asCollection : 0;
69,466✔
174
  }
175

176
  const CollectionData* asCollection() const {
44✔
177
    return const_cast<VariantData*>(this)->asCollection();
44✔
178
  }
179

180
  template <typename T>
181
  T asFloat(const ResourceManager* resources) const {
52✔
182
    static_assert(is_floating_point<T>::value, "T must be a floating point");
183
#if ARDUINOJSON_USE_EXTENSIONS
184
    auto extension = getExtension(resources);
52✔
185
#else
186
    (void)resources;  // silence warning
187
#endif
188
    switch (type_) {
52✔
189
      case VariantType::Boolean:
2✔
190
        return static_cast<T>(content_.asBoolean);
2✔
191
      case VariantType::Uint32:
2✔
192
        return static_cast<T>(content_.asUint32);
2✔
193
      case VariantType::Int32:
4✔
194
        return static_cast<T>(content_.asInt32);
4✔
195
#if ARDUINOJSON_USE_LONG_LONG
196
      case VariantType::Uint64:
1✔
197
        return static_cast<T>(extension->asUint64);
1✔
198
      case VariantType::Int64:
1✔
199
        return static_cast<T>(extension->asInt64);
1✔
200
#endif
UNCOV
201
      case VariantType::LinkedString:
×
NEW
202
        return parseNumber<T>(asLinkedString(resources));
×
203
      case VariantType::OwnedString:
1✔
204
        return parseNumber<T>(content_.asOwnedString->data);
1✔
205
      case VariantType::Float:
18✔
206
        return static_cast<T>(content_.asFloat);
18✔
207
#if ARDUINOJSON_USE_DOUBLE
208
      case VariantType::Double:
21✔
209
        return static_cast<T>(extension->asDouble);
21✔
210
#endif
211
      default:
2✔
212
        return 0;
2✔
213
    }
214
  }
215

216
  template <typename T>
217
  T asIntegral(const ResourceManager* resources) const {
197✔
218
    static_assert(is_integral<T>::value, "T must be an integral type");
219
#if ARDUINOJSON_USE_EXTENSIONS
220
    auto extension = getExtension(resources);
197✔
221
#else
222
    (void)resources;  // silence warning
223
#endif
224
    switch (type_) {
197✔
225
      case VariantType::Boolean:
2✔
226
        return content_.asBoolean;
2✔
227
      case VariantType::Uint32:
66✔
228
        return convertNumber<T>(content_.asUint32);
66✔
229
      case VariantType::Int32:
83✔
230
        return convertNumber<T>(content_.asInt32);
83✔
231
#if ARDUINOJSON_USE_LONG_LONG
232
      case VariantType::Uint64:
10✔
233
        return convertNumber<T>(extension->asUint64);
10✔
234
      case VariantType::Int64:
11✔
235
        return convertNumber<T>(extension->asInt64);
11✔
236
#endif
237
      case VariantType::LinkedString:
6✔
238
        return parseNumber<T>(asLinkedString(resources));
6✔
239
      case VariantType::OwnedString:
2✔
240
        return parseNumber<T>(content_.asOwnedString->data);
2✔
241
      case VariantType::Float:
5✔
242
        return convertNumber<T>(content_.asFloat);
5✔
243
#if ARDUINOJSON_USE_DOUBLE
244
      case VariantType::Double:
10✔
245
        return convertNumber<T>(extension->asDouble);
10✔
246
#endif
247
      default:
2✔
248
        return 0;
2✔
249
    }
250
  }
251

252
  ObjectData* asObject() {
2,936✔
253
    return isObject() ? &content_.asObject : 0;
2,936✔
254
  }
255

256
  const ObjectData* asObject() const {
2,230✔
257
    return const_cast<VariantData*>(this)->asObject();
2,230✔
258
  }
259

260
  JsonString asRawString() const {
46✔
261
    switch (type_) {
46✔
262
      case VariantType::RawString:
26✔
263
        return JsonString(content_.asOwnedString->data,
26✔
264
                          content_.asOwnedString->length);
26✔
265
      default:
20✔
266
        return JsonString();
20✔
267
    }
268
  }
269

270
  const char* asLinkedString(const ResourceManager* resources) const;
271

272
  JsonString asString(const ResourceManager* resources) const {
12,746✔
273
    switch (type_) {
12,746✔
274
      case VariantType::LinkedString:
4,663✔
275
        return JsonString(asLinkedString(resources), true);
4,663✔
276
      case VariantType::OwnedString:
7,653✔
277
        return JsonString(content_.asOwnedString->data,
7,653✔
278
                          content_.asOwnedString->length);
7,653✔
279
      default:
430✔
280
        return JsonString();
430✔
281
    }
282
  }
283

284
#if ARDUINOJSON_USE_EXTENSIONS
285
  const VariantExtension* getExtension(const ResourceManager* resources) const;
286
#endif
287

288
  VariantData* getElement(size_t index,
463✔
289
                          const ResourceManager* resources) const {
290
    return ArrayData::getElement(asArray(), index, resources);
463✔
291
  }
292

293
  static VariantData* getElement(const VariantData* var, size_t index,
466✔
294
                                 const ResourceManager* resources) {
295
    return var != 0 ? var->getElement(index, resources) : 0;
466✔
296
  }
297

298
  template <typename TAdaptedString>
299
  VariantData* getMember(TAdaptedString key,
2,196✔
300
                         const ResourceManager* resources) const {
301
    return ObjectData::getMember(asObject(), key, resources);
2,196✔
302
  }
303

304
  template <typename TAdaptedString>
305
  static VariantData* getMember(const VariantData* var, TAdaptedString key,
2,185✔
306
                                const ResourceManager* resources) {
307
    if (!var)
2,185✔
308
      return 0;
13✔
309
    return var->getMember(key, resources);
2,172✔
310
  }
311

312
  VariantData* getOrAddElement(size_t index, ResourceManager* resources) {
152✔
313
    auto array = isNull() ? &toArray() : asArray();
152✔
314
    if (!array)
152✔
315
      return nullptr;
1✔
316
    return array->getOrAddElement(index, resources);
151✔
317
  }
318

319
  template <typename TAdaptedString>
320
  VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources) {
956✔
321
    if (key.isNull())
956✔
322
      return nullptr;
1✔
323
    auto obj = isNull() ? &toObject() : asObject();
955✔
324
    if (!obj)
955✔
325
      return nullptr;
×
326
    return obj->getOrAddMember(key, resources);
955✔
327
  }
328

329
  bool isArray() const {
893✔
330
    return type_ == VariantType::Array;
893✔
331
  }
332

333
  bool isBoolean() const {
35✔
334
    return type_ == VariantType::Boolean;
35✔
335
  }
336

337
  bool isCollection() const {
69,466✔
338
    return type_ & VariantTypeBits::CollectionMask;
69,466✔
339
  }
340

341
  bool isFloat() const {
405✔
342
    return type_ & VariantTypeBits::NumberBit;
405✔
343
  }
344

345
  template <typename T>
346
  bool isInteger(const ResourceManager* resources) const {
151✔
347
#if ARDUINOJSON_USE_LONG_LONG
348
    auto extension = getExtension(resources);
148✔
349
#else
350
    (void)resources;  // silence warning
351
#endif
352
    switch (type_) {
151✔
353
      case VariantType::Uint32:
11✔
354
        return canConvertNumber<T>(content_.asUint32);
11✔
355

356
      case VariantType::Int32:
65✔
357
        return canConvertNumber<T>(content_.asInt32);
65✔
358

359
#if ARDUINOJSON_USE_LONG_LONG
360
      case VariantType::Uint64:
3✔
361
        return canConvertNumber<T>(extension->asUint64);
3✔
362

363
      case VariantType::Int64:
4✔
364
        return canConvertNumber<T>(extension->asInt64);
4✔
365
#endif
366

367
      default:
68✔
368
        return false;
68✔
369
    }
370
  }
371

372
  bool isNull() const {
1,955✔
373
    return type_ == VariantType::Null;
1,955✔
374
  }
375

376
  static bool isNull(const VariantData* var) {
1,290✔
377
    if (!var)
1,290✔
378
      return true;
737✔
379
    return var->isNull();
553✔
380
  }
381

382
  bool isObject() const {
3,323✔
383
    return type_ == VariantType::Object;
3,323✔
384
  }
385

386
  bool isString() const {
80✔
387
    return type_ == VariantType::LinkedString ||
136✔
388
           type_ == VariantType::OwnedString;
136✔
389
  }
390

391
  size_t nesting(const ResourceManager* resources) const {
44✔
392
    auto collection = asCollection();
44✔
393
    if (collection)
44✔
394
      return collection->nesting(resources);
30✔
395
    else
396
      return 0;
14✔
397
  }
398

399
  static size_t nesting(const VariantData* var,
28✔
400
                        const ResourceManager* resources) {
401
    if (!var)
28✔
402
      return 0;
6✔
403
    return var->nesting(resources);
22✔
404
  }
405

406
  void removeElement(size_t index, ResourceManager* resources) {
9✔
407
    ArrayData::removeElement(asArray(), index, resources);
9✔
408
  }
9✔
409

410
  static void removeElement(VariantData* var, size_t index,
10✔
411
                            ResourceManager* resources) {
412
    if (!var)
10✔
413
      return;
1✔
414
    var->removeElement(index, resources);
9✔
415
  }
416

417
  template <typename TAdaptedString>
418
  void removeMember(TAdaptedString key, ResourceManager* resources) {
16✔
419
    ObjectData::removeMember(asObject(), key, resources);
16✔
420
  }
16✔
421

422
  template <typename TAdaptedString>
423
  static void removeMember(VariantData* var, TAdaptedString key,
17✔
424
                           ResourceManager* resources) {
425
    if (!var)
17✔
426
      return;
1✔
427
    var->removeMember(key, resources);
16✔
428
  }
429

430
  void reset() {  // TODO: remove
1,945✔
431
    type_ = VariantType::Null;
1,945✔
432
  }
1,945✔
433

434
  void setBoolean(bool value) {
330✔
435
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
436
    type_ = VariantType::Boolean;
330✔
437
    content_.asBoolean = value;
330✔
438
  }
330✔
439

440
  template <typename T>
441
  enable_if_t<sizeof(T) == 4, bool> setFloat(T value, ResourceManager*) {
89✔
442
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
443
    type_ = VariantType::Float;
89✔
444
    content_.asFloat = value;
89✔
445
    return true;
89✔
446
  }
447

448
  template <typename T>
449
  enable_if_t<sizeof(T) == 8, bool> setFloat(T value, ResourceManager*);
450

451
  template <typename T>
452
  enable_if_t<is_signed<T>::value, bool> setInteger(T value,
453
                                                    ResourceManager* resources);
454

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

459
  void setRawString(StringNode* s) {
87✔
460
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
461
    ARDUINOJSON_ASSERT(s);
462
    type_ = VariantType::RawString;
87✔
463
    content_.asOwnedString = s;
87✔
464
  }
87✔
465

466
  template <typename T>
467
  void setRawString(SerializedValue<T> value, ResourceManager* resources);
468

469
  template <typename T>
470
  static void setRawString(VariantData* var, SerializedValue<T> value,
36✔
471
                           ResourceManager* resources) {
472
    if (!var)
36✔
473
      return;
2✔
474
    var->clear(resources);
34✔
475
    var->setRawString(value, resources);
34✔
476
  }
477

478
  template <typename TAdaptedString>
479
  bool setString(TAdaptedString value, ResourceManager* resources);
480

481
  bool setString(StringNode* s, ResourceManager*) {
1,331✔
482
    setOwnedString(s);
1,331✔
483
    return true;
1,331✔
484
  }
485

486
  template <typename TAdaptedString>
487
  static void setString(VariantData* var, TAdaptedString value,
66,129✔
488
                        ResourceManager* resources) {
489
    if (!var)
66,129✔
490
      return;
5✔
491
    var->clear(resources);
66,124✔
492
    var->setString(value, resources);
66,124✔
493
  }
494

495
  bool setLinkedString(const char* s, ResourceManager* resources);
496

497
  void setOwnedString(StringNode* s) {
2,024✔
498
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
499
    ARDUINOJSON_ASSERT(s);
500
    type_ = VariantType::OwnedString;
2,024✔
501
    content_.asOwnedString = s;
2,024✔
502
  }
2,024✔
503

504
  size_t size(const ResourceManager* resources) const {
42✔
505
    if (isObject())
42✔
506
      return content_.asObject.size(resources);
14✔
507

508
    if (isArray())
28✔
509
      return content_.asArray.size(resources);
15✔
510

511
    return 0;
13✔
512
  }
513

514
  static size_t size(const VariantData* var, const ResourceManager* resources) {
30✔
515
    return var != 0 ? var->size(resources) : 0;
30✔
516
  }
517

518
  ArrayData& toArray() {
1,269✔
519
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
520
    type_ = VariantType::Array;
1,269✔
521
    new (&content_.asArray) ArrayData();
2,538✔
522
    return content_.asArray;
1,269✔
523
  }
524

525
  static ArrayData* toArray(VariantData* var, ResourceManager* resources) {
256✔
526
    if (!var)
256✔
527
      return 0;
×
528
    var->clear(resources);
256✔
529
    return &var->toArray();
256✔
530
  }
531

532
  ObjectData& toObject() {
1,344✔
533
    ARDUINOJSON_ASSERT(type_ == VariantType::Null);  // must call clear() first
534
    type_ = VariantType::Object;
1,344✔
535
    new (&content_.asObject) ObjectData();
2,688✔
536
    return content_.asObject;
1,344✔
537
  }
538

539
  static ObjectData* toObject(VariantData* var, ResourceManager* resources) {
316✔
540
    if (!var)
316✔
541
      return 0;
×
542
    var->clear(resources);
316✔
543
    return &var->toObject();
316✔
544
  }
545

546
  VariantType type() const {
547
    return type_;
548
  }
549

550
  // Release the resources used by this variant and set it to null.
551
  void clear(ResourceManager* resources);
552

553
  static void clear(VariantData* var, ResourceManager* resources) {
541✔
554
    if (!var)
541✔
555
      return;
1✔
556
    var->clear(resources);
540✔
557
  }
558
};
559

560
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