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

bblanchon / ArduinoJson / 5371678521

pending completion
5371678521

push

github

bblanchon
Manage resources in `CollectionData`

107 of 107 new or added lines in 8 files covered. (100.0%)

3383 of 3405 relevant lines covered (99.35%)

6273.07 hits per line

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

99.47
/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
VariantData* collectionAddElement(CollectionData* array,
18
                                  ResourceManager* resources);
19
bool collectionCopy(CollectionData* dst, const CollectionData* src,
20
                    ResourceManager* resources);
21
void collectionRemoveElement(CollectionData* data, size_t index,
22
                             ResourceManager* resources);
23
template <typename T>
24
T parseNumber(const char* s);
25
void slotRelease(VariantSlot* slot, ResourceManager* resources);
26

27
class VariantData {
28
  VariantContent content_;  // must be first to allow cast from array to variant
29
  uint8_t flags_;
30

31
 public:
32
  VariantData() : flags_(VALUE_IS_NULL) {}
2,082✔
33

34
  template <typename TVisitor>
35
  typename TVisitor::result_type accept(TVisitor& visitor) const {
138,239✔
36
    switch (type()) {
138,239✔
37
      case VALUE_IS_FLOAT:
135✔
38
        return visitor.visitFloat(content_.asFloat);
135✔
39

40
      case VALUE_IS_ARRAY:
897✔
41
        return visitor.visitArray(content_.asCollection);
1,107✔
42

43
      case VALUE_IS_OBJECT:
2,588✔
44
        return visitor.visitObject(content_.asCollection);
2,588✔
45

46
      case VALUE_IS_LINKED_STRING:
898✔
47
        return visitor.visitString(content_.asLinkedString,
898✔
48
                                   strlen(content_.asLinkedString));
898✔
49

50
      case VALUE_IS_OWNED_STRING:
282✔
51
        return visitor.visitString(content_.asOwnedString->data,
282✔
52
                                   content_.asOwnedString->length);
282✔
53

54
      case VALUE_IS_RAW_STRING:
55✔
55
        return visitor.visitRawString(content_.asOwnedString->data,
55✔
56
                                      content_.asOwnedString->length);
55✔
57

58
      case VALUE_IS_SIGNED_INTEGER:
1,271✔
59
        return visitor.visitSignedInteger(content_.asSignedInteger);
1,271✔
60

61
      case VALUE_IS_UNSIGNED_INTEGER:
257✔
62
        return visitor.visitUnsignedInteger(content_.asUnsignedInteger);
257✔
63

64
      case VALUE_IS_BOOLEAN:
616✔
65
        return visitor.visitBoolean(content_.asBoolean != 0);
616✔
66

67
      default:
131,240✔
68
        return visitor.visitNull();
131,240✔
69
    }
70
  }
71

72
  VariantData* addElement(ResourceManager* resources) {
242✔
73
    auto array = isNull() ? &toArray() : asArray();
242✔
74
    return collectionAddElement(array, resources);
242✔
75
  }
76

77
  bool asBoolean() const {
554✔
78
    switch (type()) {
554✔
79
      case VALUE_IS_BOOLEAN:
307✔
80
        return content_.asBoolean;
307✔
81
      case VALUE_IS_SIGNED_INTEGER:
7✔
82
      case VALUE_IS_UNSIGNED_INTEGER:
83
        return content_.asUnsignedInteger != 0;
7✔
84
      case VALUE_IS_FLOAT:
5✔
85
        return content_.asFloat != 0;
5✔
86
      case VALUE_IS_NULL:
6✔
87
        return false;
6✔
88
      default:
229✔
89
        return true;
229✔
90
    }
91
  }
92

93
  CollectionData* asArray() {
706✔
94
    return isArray() ? &content_.asCollection : 0;
706✔
95
  }
96

97
  const CollectionData* asArray() const {
459✔
98
    return const_cast<VariantData*>(this)->asArray();
459✔
99
  }
100

101
  const CollectionData* asCollection() const {
67,927✔
102
    return isCollection() ? &content_.asCollection : 0;
67,927✔
103
  }
104

105
  template <typename T>
106
  T asFloat() const {
117✔
107
    static_assert(is_floating_point<T>::value, "T must be a floating point");
108
    switch (type()) {
117✔
109
      case VALUE_IS_BOOLEAN:
2✔
110
        return static_cast<T>(content_.asBoolean);
2✔
111
      case VALUE_IS_UNSIGNED_INTEGER:
3✔
112
        return static_cast<T>(content_.asUnsignedInteger);
3✔
113
      case VALUE_IS_SIGNED_INTEGER:
6✔
114
        return static_cast<T>(content_.asSignedInteger);
6✔
115
      case VALUE_IS_LINKED_STRING:
1✔
116
      case VALUE_IS_OWNED_STRING:
117
        return parseNumber<T>(content_.asOwnedString->data);
1✔
118
      case VALUE_IS_FLOAT:
103✔
119
        return static_cast<T>(content_.asFloat);
103✔
120
      default:
2✔
121
        return 0;
2✔
122
    }
123
  }
124

125
  template <typename T>
126
  T asIntegral() const {
210✔
127
    static_assert(is_integral<T>::value, "T must be an integral type");
128
    switch (type()) {
210✔
129
      case VALUE_IS_BOOLEAN:
2✔
130
        return content_.asBoolean;
2✔
131
      case VALUE_IS_UNSIGNED_INTEGER:
87✔
132
        return convertNumber<T>(content_.asUnsignedInteger);
87✔
133
      case VALUE_IS_SIGNED_INTEGER:
84✔
134
        return convertNumber<T>(content_.asSignedInteger);
84✔
135
      case VALUE_IS_LINKED_STRING:
6✔
136
        return parseNumber<T>(content_.asLinkedString);
6✔
137
      case VALUE_IS_OWNED_STRING:
2✔
138
        return parseNumber<T>(content_.asOwnedString->data);
2✔
139
      case VALUE_IS_FLOAT:
19✔
140
        return convertNumber<T>(content_.asFloat);
19✔
141
      default:
10✔
142
        return 0;
10✔
143
    }
144
  }
145

146
  CollectionData* asObject() {
2,744✔
147
    return isObject() ? &content_.asCollection : 0;
2,744✔
148
  }
149

150
  const CollectionData* asObject() const {
2,152✔
151
    return const_cast<VariantData*>(this)->asObject();
2,152✔
152
  }
153

154
  JsonString asRawString() const {
4✔
155
    switch (type()) {
4✔
156
      case VALUE_IS_RAW_STRING:
4✔
157
        return JsonString(content_.asOwnedString->data,
4✔
158
                          content_.asOwnedString->length, JsonString::Copied);
4✔
159
      default:
×
160
        return JsonString();
×
161
    }
162
  }
163

164
  JsonString asString() const {
444✔
165
    switch (type()) {
444✔
166
      case VALUE_IS_LINKED_STRING:
30✔
167
        return JsonString(content_.asLinkedString, JsonString::Linked);
30✔
168
      case VALUE_IS_OWNED_STRING:
76✔
169
        return JsonString(content_.asOwnedString->data,
76✔
170
                          content_.asOwnedString->length, JsonString::Copied);
76✔
171
      default:
338✔
172
        return JsonString();
338✔
173
    }
174
  }
175

176
  bool copyFrom(const VariantData* src, ResourceManager* resources) {
71✔
177
    release(resources);
71✔
178
    if (!src) {
71✔
179
      setNull();
4✔
180
      return true;
4✔
181
    }
182
    switch (src->type()) {
67✔
183
      case VALUE_IS_ARRAY:
12✔
184
        return collectionCopy(&toArray(), src->asArray(), resources);
12✔
185
      case VALUE_IS_OBJECT:
17✔
186
        return collectionCopy(&toObject(), src->asObject(), resources);
17✔
187
      case VALUE_IS_OWNED_STRING: {
11✔
188
        auto str = adaptString(src->asString());
11✔
189
        auto dup = resources->saveString(str);
11✔
190
        if (!dup)
11✔
191
          return false;
1✔
192
        setOwnedString(dup);
10✔
193
        return true;
10✔
194
      }
195
      case VALUE_IS_RAW_STRING: {
4✔
196
        auto str = adaptString(src->asRawString());
4✔
197
        auto dup = resources->saveString(str);
4✔
198
        if (!dup)
4✔
199
          return false;
1✔
200
        setRawString(dup);
3✔
201
        return true;
3✔
202
      }
203
      default:
23✔
204
        content_ = src->content_;
23✔
205
        flags_ = src->flags_;
23✔
206
        return true;
23✔
207
    }
208
  }
209

210
  VariantData* getElement(size_t index) const {
424✔
211
    auto array = asArray();
424✔
212
    if (!array)
424✔
213
      return nullptr;
15✔
214
    return array->getElement(index);
409✔
215
  }
216

217
  template <typename TAdaptedString>
218
  VariantData* getMember(TAdaptedString key) const {
2,135✔
219
    auto object = asObject();
2,135✔
220
    if (!object)
2,135✔
221
      return nullptr;
44✔
222
    return object->getMember(key);
2,091✔
223
  }
224

225
  VariantData* getOrAddElement(size_t index, ResourceManager* resources) {
136✔
226
    auto array = isNull() ? &toArray() : asArray();
136✔
227
    if (!array)
136✔
228
      return nullptr;
1✔
229
    return array->getOrAddElement(index, resources);
135✔
230
  }
231

232
  template <typename TAdaptedString>
233
  VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources) {
799✔
234
    if (key.isNull())
799✔
235
      return nullptr;
1✔
236
    auto obj = isNull() ? &toObject() : asObject();
798✔
237
    if (!obj)
798✔
238
      return nullptr;
1✔
239
    return obj->getOrAddMember(key, resources);
797✔
240
  }
241

242
  bool isArray() const {
802✔
243
    return (flags_ & VALUE_IS_ARRAY) != 0;
802✔
244
  }
245

246
  bool isBoolean() const {
35✔
247
    return type() == VALUE_IS_BOOLEAN;
35✔
248
  }
249

250
  bool isCollection() const {
67,966✔
251
    return (flags_ & COLLECTION_MASK) != 0;
67,966✔
252
  }
253

254
  bool isFloat() const {
402✔
255
    return (flags_ & NUMBER_BIT) != 0;
402✔
256
  }
257

258
  template <typename T>
259
  bool isInteger() const {
123✔
260
    switch (type()) {
123✔
261
      case VALUE_IS_UNSIGNED_INTEGER:
12✔
262
        return canConvertNumber<T>(content_.asUnsignedInteger);
12✔
263

264
      case VALUE_IS_SIGNED_INTEGER:
57✔
265
        return canConvertNumber<T>(content_.asSignedInteger);
57✔
266

267
      default:
54✔
268
        return false;
54✔
269
    }
270
  }
271

272
  bool isNull() const {
1,690✔
273
    return type() == VALUE_IS_NULL;
1,690✔
274
  }
275

276
  bool isObject() const {
3,094✔
277
    return (flags_ & VALUE_IS_OBJECT) != 0;
3,094✔
278
  }
279

280
  bool isString() const {
62✔
281
    return type() == VALUE_IS_LINKED_STRING || type() == VALUE_IS_OWNED_STRING;
62✔
282
  }
283

284
  size_t memoryUsage() const {
34✔
285
    switch (type()) {
34✔
286
      case VALUE_IS_OWNED_STRING:
10✔
287
      case VALUE_IS_RAW_STRING:
288
        return sizeofString(content_.asOwnedString->length);
10✔
289
      case VALUE_IS_OBJECT:
6✔
290
      case VALUE_IS_ARRAY:
291
        return content_.asCollection.memoryUsage();
6✔
292
      default:
18✔
293
        return 0;
18✔
294
    }
295
  }
296

297
  void movePointers(ptrdiff_t variantDistance) {
22✔
298
    if (flags_ & COLLECTION_MASK)
22✔
299
      content_.asCollection.movePointers(variantDistance);
10✔
300
  }
22✔
301

302
  size_t nesting() const {
21✔
303
    auto collection = asCollection();
21✔
304
    if (!collection)
21✔
305
      return 0;
5✔
306

307
    size_t maxChildNesting = 0;
16✔
308
    for (const VariantSlot* s = collection->head(); s; s = s->next()) {
22✔
309
      size_t childNesting = s->data()->nesting();
6✔
310
      if (childNesting > maxChildNesting)
6✔
311
        maxChildNesting = childNesting;
4✔
312
    }
313
    return maxChildNesting + 1;
16✔
314
  }
315

316
  void operator=(const VariantData& src) {
16✔
317
    content_ = src.content_;
16✔
318
    flags_ = uint8_t((flags_ & OWNED_KEY_BIT) | (src.flags_ & ~OWNED_KEY_BIT));
16✔
319
  }
16✔
320

321
  void removeElement(size_t index, ResourceManager* resources) {
7✔
322
    collectionRemoveElement(asArray(), index, resources);
7✔
323
  }
7✔
324

325
  template <typename TAdaptedString>
326
  void removeMember(TAdaptedString key, ResourceManager* resources) {
13✔
327
    collectionRemoveMember(asObject(), key, resources);
13✔
328
  }
13✔
329

330
  void reset() {
1,763✔
331
    flags_ = VALUE_IS_NULL;
1,763✔
332
  }
1,763✔
333

334
  void setBoolean(bool value) {
337✔
335
    setType(VALUE_IS_BOOLEAN);
337✔
336
    content_.asBoolean = value;
337✔
337
  }
337✔
338

339
  void setBoolean(bool value, ResourceManager* resources) {
249✔
340
    release(resources);
249✔
341
    setBoolean(value);
249✔
342
  }
249✔
343

344
  void setFloat(JsonFloat value) {
231✔
345
    setType(VALUE_IS_FLOAT);
231✔
346
    content_.asFloat = value;
231✔
347
  }
231✔
348

349
  void setFloat(JsonFloat value, ResourceManager* resources) {
61✔
350
    release(resources);
61✔
351
    setFloat(value);
61✔
352
  }
61✔
353

354
  template <typename T>
355
  typename enable_if<is_signed<T>::value>::type setInteger(T value) {
723✔
356
    setType(VALUE_IS_SIGNED_INTEGER);
723✔
357
    content_.asSignedInteger = value;
723✔
358
  }
723✔
359

360
  template <typename T>
361
  typename enable_if<is_unsigned<T>::value>::type setInteger(T value) {
2,313✔
362
    setType(VALUE_IS_UNSIGNED_INTEGER);
2,313✔
363
    content_.asUnsignedInteger = static_cast<JsonUInt>(value);
2,313✔
364
  }
2,313✔
365

366
  template <typename T>
367
  void setInteger(T value, ResourceManager* resources) {
571✔
368
    release(resources);
571✔
369
    setInteger(value);
571✔
370
  }
571✔
371

372
  void setNull() {
66,444✔
373
    setType(VALUE_IS_NULL);
66,444✔
374
  }
66,444✔
375

376
  void setNull(ResourceManager* resources) {
66,433✔
377
    release(resources);
66,433✔
378
    setNull();
66,433✔
379
  }
66,433✔
380

381
  void setRawString(StringNode* s) {
30✔
382
    ARDUINOJSON_ASSERT(s);
383
    setType(VALUE_IS_RAW_STRING);
30✔
384
    content_.asOwnedString = s;
30✔
385
  }
30✔
386

387
  template <typename T>
388
  void setRawString(SerializedValue<T> value, ResourceManager* resources) {
30✔
389
    release(resources);
30✔
390
    auto dup = resources->saveString(adaptString(value.data(), value.size()));
30✔
391
    if (dup)
30✔
392
      setRawString(dup);
27✔
393
    else
394
      setNull();
3✔
395
  }
30✔
396

397
  template <typename TAdaptedString>
398
  void setString(TAdaptedString value, ResourceManager* resources) {
65,954✔
399
    setNull(resources);
65,954✔
400

401
    if (value.isNull())
65,954✔
402
      return;
65,553✔
403

404
    if (value.isLinked()) {
401✔
405
      setLinkedString(value.data());
300✔
406
      return;
300✔
407
    }
408

409
    auto dup = resources->saveString(value);
101✔
410
    if (dup)
101✔
411
      setOwnedString(dup);
98✔
412
  }
413

414
  void setLinkedString(const char* s) {
300✔
415
    ARDUINOJSON_ASSERT(s);
416
    setType(VALUE_IS_LINKED_STRING);
300✔
417
    content_.asLinkedString = s;
300✔
418
  }
300✔
419

420
  void setOwnedString(StringNode* s) {
469✔
421
    ARDUINOJSON_ASSERT(s);
422
    setType(VALUE_IS_OWNED_STRING);
469✔
423
    content_.asOwnedString = s;
469✔
424
  }
469✔
425

426
  size_t size() const {
39✔
427
    return isCollection() ? content_.asCollection.size() : 0;
39✔
428
  }
429

430
  CollectionData& toArray() {
1,217✔
431
    setType(VALUE_IS_ARRAY);
1,217✔
432
    new (&content_.asCollection) CollectionData();
2,434✔
433
    return content_.asCollection;
1,217✔
434
  }
435

436
  CollectionData& toArray(ResourceManager* resources) {
220✔
437
    release(resources);
220✔
438
    return toArray();
220✔
439
  }
440

441
  CollectionData& toObject() {
1,269✔
442
    setType(VALUE_IS_OBJECT);
1,269✔
443
    new (&content_.asCollection) CollectionData();
2,538✔
444
    return content_.asCollection;
1,269✔
445
  }
446

447
  CollectionData& toObject(ResourceManager* resources) {
271✔
448
    release(resources);
271✔
449
    return toObject();
271✔
450
  }
451

452
  uint8_t type() const {
141,630✔
453
    return flags_ & VALUE_MASK;
141,630✔
454
  }
455

456
 private:
457
  void release(ResourceManager* resources) {
67,906✔
458
    if (flags_ & OWNED_VALUE_BIT)
67,906✔
459
      resources->dereferenceString(content_.asOwnedString->data);
11✔
460

461
    auto c = asCollection();
67,906✔
462
    if (c) {
67,906✔
463
      for (auto slot = c->head(); slot; slot = slot->next())
29✔
464
        slotRelease(slot, resources);
14✔
465
    }
466
  }
67,906✔
467

468
  void setType(uint8_t t) {
73,333✔
469
    flags_ &= OWNED_KEY_BIT;
73,333✔
470
    flags_ |= t;
73,333✔
471
  }
73,333✔
472
};
473

474
template <typename TVisitor>
475
typename TVisitor::result_type variantAccept(const VariantData* var,
5,986✔
476
                                             TVisitor& visitor) {
477
  if (var != 0)
5,986✔
478
    return var->accept(visitor);
5,835✔
479
  else
480
    return visitor.visitNull();
151✔
481
}
482

483
inline bool variantCopyFrom(VariantData* dst, const VariantData* src,
75✔
484
                            ResourceManager* resources) {
485
  if (!dst)
75✔
486
    return false;
4✔
487
  return dst->copyFrom(src, resources);
71✔
488
}
489

490
inline VariantData* variantAddElement(VariantData* var,
78✔
491
                                      ResourceManager* resources) {
492
  if (!var)
78✔
493
    return nullptr;
6✔
494
  return var->addElement(resources);
72✔
495
}
496

497
inline VariantData* variantGetElement(const VariantData* var, size_t index) {
429✔
498
  return var != 0 ? var->getElement(index) : 0;
429✔
499
}
500

501
template <typename TAdaptedString>
502
VariantData* variantGetMember(const VariantData* var, TAdaptedString key) {
2,140✔
503
  if (!var)
2,140✔
504
    return 0;
13✔
505
  return var->getMember(key);
2,127✔
506
}
507

508
inline VariantData* variantGetOrAddElement(VariantData* var, size_t index,
137✔
509
                                           ResourceManager* resources) {
510
  if (!var)
137✔
511
    return nullptr;
1✔
512
  return var->getOrAddElement(index, resources);
136✔
513
}
514

515
template <typename TAdaptedString>
516
VariantData* variantGetOrAddMember(VariantData* var, TAdaptedString key,
804✔
517
                                   ResourceManager* resources) {
518
  if (!var)
804✔
519
    return nullptr;
5✔
520
  return var->getOrAddMember(key, resources);
799✔
521
}
522

523
inline bool variantIsNull(const VariantData* var) {
1,237✔
524
  if (!var)
1,237✔
525
    return true;
726✔
526
  return var->isNull();
511✔
527
}
528

529
inline size_t variantNesting(const VariantData* var) {
14✔
530
  if (!var)
14✔
531
    return 0;
3✔
532
  return var->nesting();
11✔
533
}
534

535
inline void variantRemoveElement(VariantData* var, size_t index,
8✔
536
                                 ResourceManager* resources) {
537
  if (!var)
8✔
538
    return;
1✔
539
  var->removeElement(index, resources);
7✔
540
}
541

542
template <typename TAdaptedString>
543
void variantRemoveMember(VariantData* var, TAdaptedString key,
14✔
544
                         ResourceManager* resources) {
545
  if (!var)
14✔
546
    return;
1✔
547
  var->removeMember(key, resources);
13✔
548
}
549

550
inline void variantSetBoolean(VariantData* var, bool value,
253✔
551
                              ResourceManager* resources) {
552
  if (!var)
253✔
553
    return;
4✔
554
  var->setBoolean(value, resources);
249✔
555
}
556

557
inline void variantSetFloat(VariantData* var, JsonFloat value,
63✔
558
                            ResourceManager* resources) {
559
  if (!var)
63✔
560
    return;
2✔
561
  var->setFloat(value, resources);
61✔
562
}
563

564
template <typename T>
565
void variantSetInteger(VariantData* var, T value, ResourceManager* resources) {
590✔
566
  if (!var)
590✔
567
    return;
19✔
568
  var->setInteger(value, resources);
571✔
569
}
570

571
inline void variantSetNull(VariantData* var, ResourceManager* resources) {
419✔
572
  if (!var)
419✔
573
    return;
1✔
574
  var->setNull(resources);
418✔
575
}
576

577
template <typename T>
578
void variantSetRawString(VariantData* var, SerializedValue<T> value,
33✔
579
                         ResourceManager* resources) {
580
  if (!var)
33✔
581
    return;
3✔
582
  var->setRawString(value, resources);
30✔
583
}
584

585
template <typename TAdaptedString>
586
void variantSetString(VariantData* var, TAdaptedString value,
65,960✔
587
                      ResourceManager* resources) {
588
  if (!var)
65,960✔
589
    return;
6✔
590
  var->setString(value, resources);
65,954✔
591
}
592

593
inline size_t variantSize(const VariantData* var) {
30✔
594
  return var != 0 ? var->size() : 0;
30✔
595
}
596

597
inline CollectionData* variantToArray(VariantData* var,
222✔
598
                                      ResourceManager* resources) {
599
  if (!var)
222✔
600
    return 0;
2✔
601
  return &var->toArray(resources);
220✔
602
}
603

604
inline CollectionData* variantToObject(VariantData* var,
273✔
605
                                       ResourceManager* resources) {
606
  if (!var)
273✔
607
    return 0;
2✔
608
  return &var->toObject(resources);
271✔
609
}
610

611
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