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

bblanchon / ArduinoJson / 5489391768

pending completion
5489391768

push

github

bblanchon
`VariantSlot` stores a `StringNode*` for owned keys

18 of 18 new or added lines in 5 files covered. (100.0%)

3406 of 3429 relevant lines covered (99.33%)

6529.06 hits per line

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

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

5
#pragma once
6

7
#include <ArduinoJson/Memory/StringNode.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/VariantContent.hpp>
13
#include <ArduinoJson/Variant/VariantSlot.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 flags_;
23

24
 public:
25
  VariantData() : flags_(VALUE_IS_NULL) {}
2,082✔
26

27
  template <typename TVisitor>
28
  typename TVisitor::result_type accept(TVisitor& visitor) const {
138,239✔
29
    switch (type()) {
138,239✔
30
      case VALUE_IS_FLOAT:
135✔
31
        return visitor.visitFloat(content_.asFloat);
135✔
32

33
      case VALUE_IS_ARRAY:
897✔
34
        return visitor.visitArray(content_.asArray);
1,107✔
35

36
      case VALUE_IS_OBJECT:
2,588✔
37
        return visitor.visitObject(content_.asObject);
2,588✔
38

39
      case VALUE_IS_LINKED_STRING:
898✔
40
        return visitor.visitString(content_.asLinkedString,
898✔
41
                                   strlen(content_.asLinkedString));
898✔
42

43
      case VALUE_IS_OWNED_STRING:
282✔
44
        return visitor.visitString(content_.asOwnedString->data,
282✔
45
                                   content_.asOwnedString->length);
282✔
46

47
      case VALUE_IS_RAW_STRING:
55✔
48
        return visitor.visitRawString(content_.asOwnedString->data,
55✔
49
                                      content_.asOwnedString->length);
55✔
50

51
      case VALUE_IS_SIGNED_INTEGER:
1,271✔
52
        return visitor.visitSignedInteger(content_.asSignedInteger);
1,271✔
53

54
      case VALUE_IS_UNSIGNED_INTEGER:
257✔
55
        return visitor.visitUnsignedInteger(content_.asUnsignedInteger);
257✔
56

57
      case VALUE_IS_BOOLEAN:
616✔
58
        return visitor.visitBoolean(content_.asBoolean != 0);
616✔
59

60
      default:
131,240✔
61
        return visitor.visitNull();
131,240✔
62
    }
63
  }
64

65
  template <typename TVisitor>
66
  static typename TVisitor::result_type accept(const VariantData* var,
5,986✔
67
                                               TVisitor& visitor) {
68
    if (var != 0)
5,986✔
69
      return var->accept(visitor);
5,835✔
70
    else
71
      return visitor.visitNull();
151✔
72
  }
73

74
  VariantData* addElement(ResourceManager* resources) {
242✔
75
    auto array = isNull() ? &toArray() : asArray();
242✔
76
    return detail::ArrayData::addElement(array, resources);
242✔
77
  }
78

79
  static VariantData* addElement(VariantData* var, ResourceManager* resources) {
78✔
80
    if (!var)
78✔
81
      return nullptr;
6✔
82
    return var->addElement(resources);
72✔
83
  }
84

85
  bool asBoolean() const {
554✔
86
    switch (type()) {
554✔
87
      case VALUE_IS_BOOLEAN:
307✔
88
        return content_.asBoolean;
307✔
89
      case VALUE_IS_SIGNED_INTEGER:
7✔
90
      case VALUE_IS_UNSIGNED_INTEGER:
91
        return content_.asUnsignedInteger != 0;
7✔
92
      case VALUE_IS_FLOAT:
5✔
93
        return content_.asFloat != 0;
5✔
94
      case VALUE_IS_NULL:
6✔
95
        return false;
6✔
96
      default:
229✔
97
        return true;
229✔
98
    }
99
  }
100

101
  ArrayData* asArray() {
694✔
102
    return isArray() ? &content_.asArray : 0;
694✔
103
  }
104

105
  const ArrayData* asArray() const {
447✔
106
    return const_cast<VariantData*>(this)->asArray();
447✔
107
  }
108

109
  CollectionData* asCollection() {
67,923✔
110
    return isCollection() ? &content_.asCollection : 0;
67,923✔
111
  }
112

113
  const CollectionData* asCollection() const {
21✔
114
    return const_cast<VariantData*>(this)->asCollection();
21✔
115
  }
116

117
  template <typename T>
118
  T asFloat() const {
117✔
119
    static_assert(is_floating_point<T>::value, "T must be a floating point");
120
    switch (type()) {
117✔
121
      case VALUE_IS_BOOLEAN:
2✔
122
        return static_cast<T>(content_.asBoolean);
2✔
123
      case VALUE_IS_UNSIGNED_INTEGER:
3✔
124
        return static_cast<T>(content_.asUnsignedInteger);
3✔
125
      case VALUE_IS_SIGNED_INTEGER:
6✔
126
        return static_cast<T>(content_.asSignedInteger);
6✔
127
      case VALUE_IS_LINKED_STRING:
1✔
128
      case VALUE_IS_OWNED_STRING:
129
        return parseNumber<T>(content_.asOwnedString->data);
1✔
130
      case VALUE_IS_FLOAT:
103✔
131
        return static_cast<T>(content_.asFloat);
103✔
132
      default:
2✔
133
        return 0;
2✔
134
    }
135
  }
136

137
  template <typename T>
138
  T asIntegral() const {
210✔
139
    static_assert(is_integral<T>::value, "T must be an integral type");
140
    switch (type()) {
210✔
141
      case VALUE_IS_BOOLEAN:
2✔
142
        return content_.asBoolean;
2✔
143
      case VALUE_IS_UNSIGNED_INTEGER:
87✔
144
        return convertNumber<T>(content_.asUnsignedInteger);
87✔
145
      case VALUE_IS_SIGNED_INTEGER:
84✔
146
        return convertNumber<T>(content_.asSignedInteger);
84✔
147
      case VALUE_IS_LINKED_STRING:
6✔
148
        return parseNumber<T>(content_.asLinkedString);
6✔
149
      case VALUE_IS_OWNED_STRING:
2✔
150
        return parseNumber<T>(content_.asOwnedString->data);
2✔
151
      case VALUE_IS_FLOAT:
19✔
152
        return convertNumber<T>(content_.asFloat);
19✔
153
      default:
10✔
154
        return 0;
10✔
155
    }
156
  }
157

158
  ObjectData* asObject() {
2,704✔
159
    return isObject() ? &content_.asObject : 0;
2,704✔
160
  }
161

162
  const ObjectData* asObject() const {
2,112✔
163
    return const_cast<VariantData*>(this)->asObject();
2,112✔
164
  }
165

166
  JsonString asRawString() const {
4✔
167
    switch (type()) {
4✔
168
      case VALUE_IS_RAW_STRING:
4✔
169
        return JsonString(content_.asOwnedString->data,
4✔
170
                          content_.asOwnedString->length, JsonString::Copied);
4✔
171
      default:
×
172
        return JsonString();
×
173
    }
174
  }
175

176
  JsonString asString() const {
444✔
177
    switch (type()) {
444✔
178
      case VALUE_IS_LINKED_STRING:
30✔
179
        return JsonString(content_.asLinkedString, JsonString::Linked);
30✔
180
      case VALUE_IS_OWNED_STRING:
76✔
181
        return JsonString(content_.asOwnedString->data,
76✔
182
                          content_.asOwnedString->length, JsonString::Copied);
76✔
183
      default:
338✔
184
        return JsonString();
338✔
185
    }
186
  }
187

188
  bool copyFrom(const VariantData& src, ResourceManager* resources) {
67✔
189
    release(resources);
67✔
190
    switch (src.type()) {
67✔
191
      case VALUE_IS_ARRAY:
12✔
192
        return toArray().copyFrom(src.content_.asArray, resources);
12✔
193
      case VALUE_IS_OBJECT:
17✔
194
        return toObject().copyFrom(src.content_.asObject, resources);
17✔
195
      case VALUE_IS_OWNED_STRING: {
11✔
196
        auto str = adaptString(src.asString());
11✔
197
        auto dup = resources->saveString(str);
11✔
198
        if (!dup)
11✔
199
          return false;
1✔
200
        setOwnedString(dup);
10✔
201
        return true;
10✔
202
      }
203
      case VALUE_IS_RAW_STRING: {
4✔
204
        auto str = adaptString(src.asRawString());
4✔
205
        auto dup = resources->saveString(str);
4✔
206
        if (!dup)
4✔
207
          return false;
1✔
208
        setRawString(dup);
3✔
209
        return true;
3✔
210
      }
211
      default:
23✔
212
        content_ = src.content_;
23✔
213
        flags_ = src.flags_;
23✔
214
        return true;
23✔
215
    }
216
  }
217

218
  static bool copy(VariantData* dst, const VariantData* src,
50✔
219
                   ResourceManager* resources) {
220
    if (!dst)
50✔
221
      return false;
1✔
222
    if (!src) {
49✔
223
      dst->setNull();
4✔
224
      return true;
4✔
225
    }
226
    return dst->copyFrom(*src, resources);
45✔
227
  }
228

229
  VariantData* getElement(size_t index) const {
424✔
230
    auto array = asArray();
424✔
231
    if (!array)
424✔
232
      return nullptr;
15✔
233
    return array->getElement(index);
409✔
234
  }
235

236
  static VariantData* getElement(const VariantData* var, size_t index) {
429✔
237
    return var != 0 ? var->getElement(index) : 0;
429✔
238
  }
239

240
  template <typename TAdaptedString>
241
  VariantData* getMember(TAdaptedString key) const {
2,112✔
242
    auto object = asObject();
2,112✔
243
    if (!object)
2,112✔
244
      return nullptr;
44✔
245
    return object->getMember(key);
2,068✔
246
  }
247

248
  template <typename TAdaptedString>
249
  static VariantData* getMember(const VariantData* var, TAdaptedString key) {
2,117✔
250
    if (!var)
2,117✔
251
      return 0;
13✔
252
    return var->getMember(key);
2,104✔
253
  }
254

255
  VariantData* getOrAddElement(size_t index, ResourceManager* resources) {
136✔
256
    auto array = isNull() ? &toArray() : asArray();
136✔
257
    if (!array)
136✔
258
      return nullptr;
1✔
259
    return array->getOrAddElement(index, resources);
135✔
260
  }
261

262
  template <typename TAdaptedString>
263
  VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources) {
799✔
264
    if (key.isNull())
799✔
265
      return nullptr;
1✔
266
    auto obj = isNull() ? &toObject() : asObject();
798✔
267
    if (!obj)
798✔
268
      return nullptr;
1✔
269
    return obj->getOrAddMember(key, resources);
797✔
270
  }
271

272
  bool isArray() const {
790✔
273
    return (flags_ & VALUE_IS_ARRAY) != 0;
790✔
274
  }
275

276
  bool isBoolean() const {
35✔
277
    return type() == VALUE_IS_BOOLEAN;
35✔
278
  }
279

280
  bool isCollection() const {
67,962✔
281
    return (flags_ & COLLECTION_MASK) != 0;
67,962✔
282
  }
283

284
  bool isFloat() const {
402✔
285
    return (flags_ & NUMBER_BIT) != 0;
402✔
286
  }
287

288
  template <typename T>
289
  bool isInteger() const {
123✔
290
    switch (type()) {
123✔
291
      case VALUE_IS_UNSIGNED_INTEGER:
12✔
292
        return canConvertNumber<T>(content_.asUnsignedInteger);
12✔
293

294
      case VALUE_IS_SIGNED_INTEGER:
57✔
295
        return canConvertNumber<T>(content_.asSignedInteger);
57✔
296

297
      default:
54✔
298
        return false;
54✔
299
    }
300
  }
301

302
  bool isNull() const {
1,690✔
303
    return type() == VALUE_IS_NULL;
1,690✔
304
  }
305

306
  static bool isNull(const VariantData* var) {
1,237✔
307
    if (!var)
1,237✔
308
      return true;
726✔
309
    return var->isNull();
511✔
310
  }
311

312
  bool isObject() const {
3,054✔
313
    return (flags_ & VALUE_IS_OBJECT) != 0;
3,054✔
314
  }
315

316
  bool isString() const {
62✔
317
    return type() == VALUE_IS_LINKED_STRING || type() == VALUE_IS_OWNED_STRING;
62✔
318
  }
319

320
  size_t memoryUsage() const {
34✔
321
    switch (type()) {
34✔
322
      case VALUE_IS_OWNED_STRING:
10✔
323
      case VALUE_IS_RAW_STRING:
324
        return sizeofString(content_.asOwnedString->length);
10✔
325
      case VALUE_IS_OBJECT:
6✔
326
      case VALUE_IS_ARRAY:
327
        return content_.asCollection.memoryUsage();
6✔
328
      default:
18✔
329
        return 0;
18✔
330
    }
331
  }
332

333
  void movePointers(ptrdiff_t variantDistance) {
22✔
334
    if (flags_ & COLLECTION_MASK)
22✔
335
      content_.asCollection.movePointers(variantDistance);
10✔
336
  }
22✔
337

338
  size_t nesting() const {
21✔
339
    auto collection = asCollection();
21✔
340
    if (collection)
21✔
341
      return collection->nesting();
16✔
342
    else
343
      return 0;
5✔
344
  }
345

346
  static size_t nesting(const VariantData* var) {
14✔
347
    if (!var)
14✔
348
      return 0;
3✔
349
    return var->nesting();
11✔
350
  }
351

352
  void operator=(const VariantData& src) {
16✔
353
    content_ = src.content_;
16✔
354
    flags_ = uint8_t((flags_ & OWNED_KEY_BIT) | (src.flags_ & ~OWNED_KEY_BIT));
16✔
355
  }
16✔
356

357
  void removeElement(size_t index, ResourceManager* resources) {
7✔
358
    ArrayData::removeElement(asArray(), index, resources);
7✔
359
  }
7✔
360

361
  static void removeElement(VariantData* var, size_t index,
8✔
362
                            ResourceManager* resources) {
363
    if (!var)
8✔
364
      return;
1✔
365
    var->removeElement(index, resources);
7✔
366
  }
367

368
  template <typename TAdaptedString>
369
  void removeMember(TAdaptedString key, ResourceManager* resources) {
13✔
370
    ObjectData::removeMember(asObject(), key, resources);
13✔
371
  }
13✔
372

373
  template <typename TAdaptedString>
374
  static void removeMember(VariantData* var, TAdaptedString key,
14✔
375
                           ResourceManager* resources) {
376
    if (!var)
14✔
377
      return;
1✔
378
    var->removeMember(key, resources);
13✔
379
  }
380

381
  void reset() {
1,763✔
382
    flags_ = VALUE_IS_NULL;
1,763✔
383
  }
1,763✔
384

385
  void setBoolean(bool value) {
337✔
386
    setType(VALUE_IS_BOOLEAN);
337✔
387
    content_.asBoolean = value;
337✔
388
  }
337✔
389

390
  void setBoolean(bool value, ResourceManager* resources) {
249✔
391
    release(resources);
249✔
392
    setBoolean(value);
249✔
393
  }
249✔
394

395
  void setFloat(JsonFloat value) {
231✔
396
    setType(VALUE_IS_FLOAT);
231✔
397
    content_.asFloat = value;
231✔
398
  }
231✔
399

400
  void setFloat(JsonFloat value, ResourceManager* resources) {
61✔
401
    release(resources);
61✔
402
    setFloat(value);
61✔
403
  }
61✔
404

405
  template <typename T>
406
  typename enable_if<is_signed<T>::value>::type setInteger(T value) {
723✔
407
    setType(VALUE_IS_SIGNED_INTEGER);
723✔
408
    content_.asSignedInteger = value;
723✔
409
  }
723✔
410

411
  template <typename T>
412
  typename enable_if<is_unsigned<T>::value>::type setInteger(T value) {
2,313✔
413
    setType(VALUE_IS_UNSIGNED_INTEGER);
2,313✔
414
    content_.asUnsignedInteger = static_cast<JsonUInt>(value);
2,313✔
415
  }
2,313✔
416

417
  template <typename T>
418
  void setInteger(T value, ResourceManager* resources) {
571✔
419
    release(resources);
571✔
420
    setInteger(value);
571✔
421
  }
571✔
422

423
  void setNull() {
66,444✔
424
    setType(VALUE_IS_NULL);
66,444✔
425
  }
66,444✔
426

427
  void setNull(ResourceManager* resources) {
66,433✔
428
    release(resources);
66,433✔
429
    setNull();
66,433✔
430
  }
66,433✔
431

432
  static void setNull(VariantData* var, ResourceManager* resources) {
409✔
433
    if (!var)
409✔
434
      return;
1✔
435
    var->setNull(resources);
408✔
436
  }
437

438
  void setRawString(StringNode* s) {
30✔
439
    ARDUINOJSON_ASSERT(s);
440
    setType(VALUE_IS_RAW_STRING);
30✔
441
    content_.asOwnedString = s;
30✔
442
  }
30✔
443

444
  template <typename T>
445
  void setRawString(SerializedValue<T> value, ResourceManager* resources) {
30✔
446
    release(resources);
30✔
447
    auto dup = resources->saveString(adaptString(value.data(), value.size()));
30✔
448
    if (dup)
30✔
449
      setRawString(dup);
27✔
450
    else
451
      setNull();
3✔
452
  }
30✔
453

454
  template <typename T>
455
  static void setRawString(VariantData* var, SerializedValue<T> value,
33✔
456
                           ResourceManager* resources) {
457
    if (!var)
33✔
458
      return;
3✔
459
    var->setRawString(value, resources);
30✔
460
  }
461

462
  template <typename TAdaptedString>
463
  void setString(TAdaptedString value, ResourceManager* resources) {
65,954✔
464
    setNull(resources);
65,954✔
465

466
    if (value.isNull())
65,954✔
467
      return;
65,553✔
468

469
    if (value.isLinked()) {
401✔
470
      setLinkedString(value.data());
300✔
471
      return;
300✔
472
    }
473

474
    auto dup = resources->saveString(value);
101✔
475
    if (dup)
101✔
476
      setOwnedString(dup);
98✔
477
  }
478

479
  template <typename TAdaptedString>
480
  static void setString(VariantData* var, TAdaptedString value,
65,960✔
481
                        ResourceManager* resources) {
482
    if (!var)
65,960✔
483
      return;
6✔
484
    var->setString(value, resources);
65,954✔
485
  }
486

487
  void setLinkedString(const char* s) {
300✔
488
    ARDUINOJSON_ASSERT(s);
489
    setType(VALUE_IS_LINKED_STRING);
300✔
490
    content_.asLinkedString = s;
300✔
491
  }
300✔
492

493
  void setOwnedString(StringNode* s) {
469✔
494
    ARDUINOJSON_ASSERT(s);
495
    setType(VALUE_IS_OWNED_STRING);
469✔
496
    content_.asOwnedString = s;
469✔
497
  }
469✔
498

499
  size_t size() const {
39✔
500
    return isCollection() ? content_.asCollection.size() : 0;
39✔
501
  }
502

503
  static size_t size(const VariantData* var) {
30✔
504
    return var != 0 ? var->size() : 0;
30✔
505
  }
506

507
  ArrayData& toArray() {
1,217✔
508
    setType(VALUE_IS_ARRAY);
1,217✔
509
    new (&content_.asArray) ArrayData();
2,434✔
510
    return content_.asArray;
1,217✔
511
  }
512

513
  ArrayData& toArray(ResourceManager* resources) {
220✔
514
    release(resources);
220✔
515
    return toArray();
220✔
516
  }
517

518
  static ArrayData* toArray(VariantData* var, ResourceManager* resources) {
222✔
519
    if (!var)
222✔
520
      return 0;
2✔
521
    return &var->toArray(resources);
220✔
522
  }
523

524
  ObjectData& toObject() {
1,269✔
525
    setType(VALUE_IS_OBJECT);
1,269✔
526
    new (&content_.asObject) ObjectData();
2,538✔
527
    return content_.asObject;
1,269✔
528
  }
529

530
  ObjectData& toObject(ResourceManager* resources) {
271✔
531
    release(resources);
271✔
532
    return toObject();
271✔
533
  }
534

535
  static ObjectData* toObject(VariantData* var, ResourceManager* resources) {
273✔
536
    if (!var)
273✔
537
      return 0;
2✔
538
    return &var->toObject(resources);
271✔
539
  }
540

541
  uint8_t type() const {
141,630✔
542
    return flags_ & VALUE_MASK;
141,630✔
543
  }
544

545
 private:
546
  void release(ResourceManager* resources) {
67,902✔
547
    if (flags_ & OWNED_VALUE_BIT)
67,902✔
548
      resources->dereferenceString(content_.asOwnedString);
11✔
549

550
    auto collection = asCollection();
67,902✔
551
    if (collection)
67,902✔
552
      collection->clear(resources);
15✔
553
  }
67,902✔
554

555
  void setType(uint8_t t) {
73,333✔
556
    flags_ &= OWNED_KEY_BIT;
73,333✔
557
    flags_ |= t;
73,333✔
558
  }
73,333✔
559
};
560

561
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