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

PredatorCZ / PreCore / 460

pending completion
460

push

github-actions-ci

PredatorCZ
try fix coverage

3204 of 6095 relevant lines covered (52.57%)

354.19 hits per line

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

93.31
/src/reflector_xml.cpp
1
/*  a XML I/O source for Reflector class
2
    more info in README for PreCore Project
3

4
    Copyright 2019-2021 Lukas Cone
5

6
    Licensed under the Apache License, Version 2.0 (the "License");
7
    you may not use this file except in compliance with the License.
8
    You may obtain a copy of the License at
9

10
        http://www.apache.org/licenses/LICENSE-2.0
11

12
    Unless required by applicable law or agreed to in writing, software
13
    distributed under the License is distributed on an "AS IS" BASIS,
14
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
    See the License for the specific language governing permissions and
16
    limitations under the License.
17
*/
18

19
#include "spike/reflect/reflector_xml.hpp"
20
#include "spike/master_printer.hpp"
21
#include "spike/util/pugiex.hpp"
22

23
struct ReflectedInstanceFriend : ReflectedInstance {
24
  void *Instance() { return instance; }
25
  const void *Instance() const { return constInstance; }
26
  const reflectorStatic *Refl() const { return rfStatic; }
16✔
27
};
28

29
class ReflectorFriend : public Reflector {
30
public:
31
  using Reflector::GetReflectedInstance;
32
  using Reflector::GetReflectedType;
33
};
34

35
auto GetMakeAttribute(pugi::xml_node node, const char *name) {
163✔
36
  if (auto child = node.attribute(name); child) {
163✔
37
    return child;
×
38
  } else {
39
    return node.append_attribute(name);
163✔
40
  }
41
}
42

43
auto GetMakeChild(pugi::xml_node node, const char *name) {
112✔
44
  if (auto child = node.child(name); child) {
112✔
45
    return child;
×
46
  } else {
47
    return node.append_child(name);
112✔
48
  }
49
}
50

51
static bool SaveV2(const ReflType &cType, const Reflector &ri,
74✔
52
                   pugi::xml_node thisNode, size_t t,
53
                   const std::string &varName,
54
                   ReflectorXMLUtil::flag_type flags) {
55
  switch (cType.type) {
74✔
56
  case REFType::String:
57
  case REFType::CString: {
58
    if (!flags[ReflectorXMLUtil::Flags_StringAsAttribute]) {
1✔
59
      return false;
60
    }
61
  }
62
    [[fallthrough]];
63
  case REFType::Bool:
64
  case REFType::Enum:
65
  case REFType::FloatingPoint:
66
  case REFType::Integer:
67
  case REFType::UnsignedInteger:
68
  case REFType::BitFieldMember: {
69
    std::string str = ri.GetReflectedValue(t);
40✔
70
    auto cNode = GetMakeAttribute(thisNode, varName.data());
40✔
71
    cNode.set_value(str.data());
40✔
72
    return true;
73
  }
74
  case REFType::Vector: {
75
    static const char axes[4][2]{"x", "y", "z", "w"};
76
    pugi::xml_node cNode = GetMakeChild(thisNode, varName.c_str());
4✔
77

78
    for (size_t a = 0; a < cType.asVector.numItems; a++) {
17✔
79
      std::string str = ri.GetReflectedValue(t, a);
13✔
80
      GetMakeAttribute(cNode, axes[a]).set_value(str.data());
13✔
81
    }
82

83
    return true;
84
  }
85

86
  case REFType::EnumFlags: {
2✔
87
    if (!ReflectedEnum::Registry().count(JenHash(cType.asClass.typeHash))) {
2✔
88
      return false;
89
    }
90

91
    auto cEnum = ReflectedEnum::Registry().at(JenHash(cType.asClass.typeHash));
2✔
92
    pugi::xml_node cNode = GetMakeChild(thisNode, varName.c_str());
2✔
93

94
    for (size_t e = 0; e < cEnum->numMembers; e++) {
8✔
95
      auto name = cEnum->names[e];
6✔
96
      const uint64 value = cEnum->values[e];
6✔
97
      auto valueName = ri.GetReflectedValue(t, value);
6✔
98
      GetMakeAttribute(cNode, name).set_value(valueName.data());
6✔
99
    }
100

101
    return true;
102
  }
103
  default:
104
    return false;
105
  }
106
}
107

108
static pugi::xml_node MakeNode(const Reflector &, const reflectorStatic *stat,
2✔
109
                               pugi::xml_node node) {
110
  std::string className;
111

112
  if (stat->className)
2✔
113
    className = stat->className;
114
  else {
115
    className.resize(15);
116
    const auto cHash = stat->classHash.raw();
117
    snprintf(&className[0], 15, "h:%X", cHash);
118
  }
119

120
  return GetMakeChild(node, className.c_str());
4✔
121
}
122

123
static std::string GetName(const Reflector &, const reflectorStatic *stat,
148✔
124
                           const ReflType &cType, size_t t) {
125
  std::string varName;
126

127
  if (stat->typeNames && stat->typeNames[t]) {
148✔
128
    varName = stat->typeNames[t];
129
  } else {
130
    varName.resize(15);
131
    const auto cHash = cType.valueNameHash.raw();
132
    snprintf(&varName[0], 15, "h:%X", cHash);
133
  }
134

135
  return varName;
148✔
136
}
137

138
pugi::xml_node ReflectorXMLUtil::Save(const Reflector &ri, pugi::xml_node node,
8✔
139
                                      bool asNewNode) {
140
  auto &&rif = static_cast<const ReflectorFriend &>(ri);
141
  auto stat =
142
      static_cast<ReflectedInstanceFriend &&>(rif.GetReflectedInstance())
8✔
143
          .Refl();
144
  pugi::xml_node thisNode = asNewNode ? MakeNode(ri, stat, node) : node;
8✔
145

146
  for (size_t t = 0; t < stat->nTypes; t++) {
82✔
147
    auto &&cType = stat->types[t];
74✔
148
    std::string varName = GetName(ri, stat, cType, t);
74✔
149
    pugi::xml_node cNode = GetMakeChild(thisNode, varName.c_str());
74✔
150

151
    if (ri.IsReflectedSubClass(t)) {
74✔
152
      if (ri.IsArray(t)) {
5✔
153
        const int numItems = cType.asArray.numItems;
2✔
154

155
        for (int s = 0; s < numItems; s++) {
6✔
156
          auto subRef = ri.GetReflectedSubClass(t, s);
4✔
157
          if (!subRef) {
4✔
158
            throw std::runtime_error("Class not registered!");
×
159
          }
160
          ReflectorPureWrap subCl(subRef);
161
          ReflectorXMLUtil::Save(
4✔
162
              subCl, GetMakeChild(cNode, ("i:" + std::to_string(s)).c_str()),
8✔
163
              false);
164
        }
165

166
      } else {
167
        auto subRef = ri.GetReflectedSubClass(t);
3✔
168
        if (!subRef) {
3✔
169
          throw std::runtime_error("Class not registered!");
×
170
        }
171
        ReflectorPureWrap subCl(subRef);
172
        ReflectorXMLUtil::Save(subCl, cNode, false);
3✔
173
      }
174
    } else {
175
      std::string str = ri.GetReflectedValue(t);
69✔
176
      cNode.append_buffer(str.c_str(), str.size());
69✔
177
    }
178
  }
179

180
  return thisNode;
8✔
181
}
182

183
pugi::xml_node ReflectorXMLUtil::SaveV2(const Reflector &ri,
1✔
184
                                        pugi::xml_node node, bool asNewNode) {
185
  flag_type opts;
186
  opts.Set(Flags_ClassNode, asNewNode);
187
  return SaveV2a(ri, node, opts);
1✔
188
}
189

190
pugi::xml_node ReflectorXMLUtil::SaveV2a(const Reflector &ri,
8✔
191
                                         pugi::xml_node node, flag_type opts) {
192
  auto &&rif = static_cast<const ReflectorFriend &>(ri);
193
  auto stat =
194
      static_cast<ReflectedInstanceFriend &&>(rif.GetReflectedInstance())
8✔
195
          .Refl();
196
  pugi::xml_node thisNode =
197
      opts[Flags_ClassNode] ? MakeNode(ri, stat, node) : node;
8✔
198

199
  for (size_t t = 0; t < stat->nTypes; t++) {
82✔
200
    auto &&cType = stat->types[t];
74✔
201
    std::string varName = GetName(ri, stat, cType, t);
74✔
202

203
    if (::SaveV2(cType, ri, thisNode, t, varName, opts)) {
74✔
204
      continue;
205
    }
206

207
    if (ri.IsReflectedSubClass(t)) {
28✔
208
      if (ri.IsArray(t)) {
5✔
209
        const int numItems = cType.asArray.numItems;
2✔
210

211
        for (int s = 0; s < numItems; s++) {
6✔
212
          auto subRef = ri.GetReflectedSubClass(t, s);
4✔
213
          if (!subRef) {
4✔
214
            throw std::runtime_error("Class not registered!");
×
215
          }
216
          ReflectorPureWrap subCl(subRef);
217
          auto nodeName = varName + '-' + std::to_string(s);
8✔
218
          pugi::xml_node cNode = GetMakeChild(thisNode, nodeName.c_str());
4✔
219
          auto subOpts = opts;
220
          subOpts -= Flags_ClassNode;
221
          SaveV2a(subCl, cNode, subOpts);
4✔
222
        }
223

224
      } else {
225
        pugi::xml_node cNode = GetMakeChild(thisNode, varName.c_str());
3✔
226
        auto subRef = ri.GetReflectedSubClass(t);
3✔
227
        if (!subRef) {
3✔
228
          throw std::runtime_error("Class not registered!");
×
229
        }
230
        ReflectorPureWrap subCl(subRef);
231
        auto subOpts = opts;
232
        subOpts -= Flags_ClassNode;
233
        SaveV2a(subCl, cNode, subOpts);
3✔
234
      }
235
    } else if (ri.IsArray(t)) {
23✔
236
      const auto &arr = cType.asArray;
237
      const int numItems = arr.numItems;
22✔
238
      switch (arr.type) {
22✔
239
      case REFType::Bool:
240
      case REFType::Enum:
241
      case REFType::FloatingPoint:
242
      case REFType::Integer:
243
      case REFType::UnsignedInteger:
244
      case REFType::BitFieldMember: {
245
        for (int s = 0; s < numItems; s++) {
64✔
246
          std::string str = ri.GetReflectedValue(t, s);
48✔
247
          auto nodeName = varName + '-' + std::to_string(s);
96✔
248
          auto cNode = GetMakeAttribute(thisNode, nodeName.data());
48✔
249
          cNode.set_value(str.data());
48✔
250
        }
251
        break;
252
      }
253
      case REFType::Vector: {
254
        for (int s = 0; s < numItems; s++) {
16✔
255
          static const char axes[4][2]{"x", "y", "z", "w"};
256
          auto nodeName = varName + '-' + std::to_string(s);
24✔
257
          pugi::xml_node cNode = GetMakeChild(thisNode, nodeName.c_str());
12✔
258

259
          for (size_t a = 0; a < arr.asVector.numItems; a++) {
50✔
260
            std::string str = ri.GetReflectedValue(t, s, a);
38✔
261
            GetMakeAttribute(cNode, axes[a]).set_value(str.data());
38✔
262
          }
263
        }
264
        break;
265
      }
266
      case REFType::EnumFlags: {
2✔
267
        if (!ReflectedEnum::Registry().count(JenHash(arr.asClass.typeHash))) {
2✔
268
          break;
269
        }
270

271
        auto &&cEnum =
272
            ReflectedEnum::Registry().at(JenHash(arr.asClass.typeHash));
2✔
273

274
        for (int s = 0; s < numItems; s++) {
8✔
275
          auto nodeName = varName + '-' + std::to_string(s);
12✔
276
          pugi::xml_node cNode = GetMakeChild(thisNode, nodeName.c_str());
6✔
277

278
          for (size_t e = 0; e < cEnum->numMembers; e++) {
24✔
279
            auto name = cEnum->names[e];
18✔
280
            const uint64 value = cEnum->values[e];
18✔
281
            auto valueName = ri.GetReflectedValue(t, s, value);
18✔
282
            GetMakeAttribute(cNode, name).set_value(valueName.data());
18✔
283
          }
284
        }
285
        break;
286
      }
287
      default: {
288
        pugi::xml_node cNode = GetMakeChild(thisNode, varName.c_str());
×
289
        std::string str = ri.GetReflectedValue(t);
×
290
        cNode.append_buffer(str.c_str(), str.size());
×
291
        break;
292
      }
293
      }
294
    } else {
295
      pugi::xml_node cNode = GetMakeChild(thisNode, varName.c_str());
1✔
296
      std::string str = ri.GetReflectedValue(t);
1✔
297
      cNode.append_buffer(str.c_str(), str.size());
1✔
298
    }
299
  }
300

301
  return thisNode;
8✔
302
}
303

304
pugi::xml_node ReflectorXMLUtil::LoadV2(Reflector &ri, pugi::xml_node node,
8✔
305
                                        bool lookupClassNode) {
306
  auto &&rif = static_cast<ReflectorFriend &>(ri);
307
  const reflectorStatic *stat =
308
      static_cast<ReflectedInstanceFriend &&>(rif.GetReflectedInstance())
8✔
309
          .Refl();
8✔
310
  pugi::xml_node thisNode;
8✔
311
  static constexpr size_t nan_ = -1;
312
  struct retval {
313
    JenHash hash;
314
    size_t index = nan_;
315
  };
316

317
  auto MakeHash = [](std::string_view name) -> JenHash {
121✔
318
    if (name[0] == 'h' && name[1] == ':') {
121✔
319
      name.remove_prefix(2);
320
      return JenHash(strtoul(name.data(), nullptr, 16));
×
321
    } else {
322
      return name;
121✔
323
    }
324
  };
325

326
  auto MakeNode = [MakeHash](auto a) {
120✔
327
    std::string_view name(a.name());
120✔
328
    retval retVal;
329
    const size_t found = name.find_last_of('-');
330

331
    if (found != name.npos) {
120✔
332
      char *endChar = nullptr;
70✔
333
      const char *startChar = name.data() + found + 1;
70✔
334
      auto index = strtoll(startChar, &endChar, 10);
70✔
335

336
      if (startChar != endChar) {
70✔
337
        name = name.substr(0, found);
70✔
338
        retVal.index = index;
70✔
339
      }
340
    }
341

342
    retVal.hash = MakeHash(name);
120✔
343

344
    return retVal;
120✔
345
  };
346

32✔
347
  if (lookupClassNode) {
32✔
348
    thisNode = node.find_child([&](pugi::xml_node nde) {
349
      return !nde.empty() && MakeHash(nde.name()) == stat->classHash;
350
    });
351
  } else {
32✔
352
    thisNode = node;
22✔
353
  }
22✔
354

22✔
355
  for (auto a : thisNode.attributes()) {
356
    auto node = MakeNode(a);
22✔
357
    if (node.index == nan_) {
22✔
358
      ri.SetReflectedValue(node.hash, a.value());
22✔
359
    } else {
360
      ri.SetReflectedValue(node.hash, a.value(), node.index);
361
    }
362
  }
32✔
363

364
  for (auto a : thisNode.children()) {
32✔
365
    auto node = MakeNode(a);
366

88✔
367
    if (ri.IsReflectedSubClass(node.hash)) {
88✔
368
      if (node.index == nan_) {
369
        node.index = 0;
370
      }
371

88✔
372
      auto rfInst = ri.GetReflectedSubClass(node.hash, node.index);
48✔
373
      if (!rfInst) {
48✔
374
        throw std::runtime_error("Class not registered!");
48✔
375
      }
376
      ReflectorPureWrap subRefl(rfInst);
48✔
377
      ReflectorXMLUtil::LoadV2(subRefl, a);
48✔
378
      continue;
48✔
379
    }
380

381
    if (a.attributes_begin() == a.attributes_end()) {
382
      ri.SetReflectedValue(node.hash, a.text().as_string());
88✔
383
      continue;
384
    }
88✔
385

386
    auto refType = rif.GetReflectedType(node.hash);
387

8✔
388
    if (!refType) {
1✔
389
      continue;
1✔
390
    }
391

392
    auto DoVector = [&] {
7✔
393
      for (auto t : a.attributes()) {
394
        size_t element = -1;
395

96✔
396
        switch (*t.name()) {
88✔
397
        case 'x':
88✔
398
          element = 0;
40✔
399
          break;
400
        case 'y':
48✔
401
          element = 1;
402
          break;
403
        case 'z':
404
          element = 2;
40✔
405
          break;
32✔
406
        case 'w':
407
          element = 3;
32✔
408
          break;
7✔
409
        }
3✔
410

411
        if (node.index == nan_) {
412
          ri.SetReflectedValue(refType->index, t.value(), element);
7✔
413
        } else {
7✔
414
          ri.SetReflectedValue(refType->index, t.value(), node.index, element);
×
415
        }
416
      }
417
    };
7✔
418

419
    auto DoFlags = [&] {
420
      std::string cpString;
421

25✔
422
      for (auto t : a.attributes()) {
1✔
423
        if (t.as_bool()) {
1✔
424
          cpString.append(t.name());
425
          cpString.push_back('|');
426
        }
24✔
427
      }
428

24✔
429
      if (cpString.empty()) {
×
430
        cpString = "NULL";
431
      } else {
432
        cpString.pop_back();
16✔
433
      }
67✔
434

435
      if (node.index == nan_) {
436
        ri.SetReflectedValue(refType->index, cpString.data());
51✔
437
      } else {
438
        ri.SetReflectedValue(refType->index, cpString.data(), node.index);
439
      }
440
    };
441

442
    switch (refType->type) {
443
    case REFType::EnumFlags: {
444
      DoFlags();
445
      break;
446
    }
447
    case REFType::Vector:
448
      DoVector();
449
      break;
450

451
    case REFType::Array: {
51✔
452
      switch (refType->asArray.type) {
13✔
453
      case REFType::Vector:
454
        DoVector();
38✔
455
        break;
456
      case REFType::EnumFlags:
457
        DoFlags();
40✔
458
        break;
459

8✔
460
      default:
461
        break;
462
      }
32✔
463
    }
24✔
464

12✔
465
    default:
12✔
466
      break;
467
    }
468
  }
469

8✔
470
  return thisNode;
471
}
472

473
pugi::xml_node ReflectorXMLUtil::Load(Reflector &ri, pugi::xml_node node,
474
                                      bool lookupClassNode) {
475
  auto &&rif = static_cast<ReflectorFriend &>(ri);
8✔
476
  const reflectorStatic *stat =
8✔
477
      static_cast<ReflectedInstanceFriend &&>(rif.GetReflectedInstance())
478
          .Refl();
6✔
479
  pugi::xml_node thisNode;
480

8✔
481
  auto MakeHash = [](std::string_view name) -> JenHash {
482
    if (name[0] == 'h' && name[1] == ':') {
24✔
483
      name.remove_prefix(2);
2✔
484
      return JenHash(strtoul(name.data(), nullptr, 16));
2✔
485
    } else {
486
      return name;
487
    }
4✔
488
  };
4✔
489

490
  if (lookupClassNode) {
491
    thisNode = node.find_child([&](pugi::xml_node nde) {
18✔
492
      return !nde.empty() && MakeHash(nde.name()) == stat->classHash;
18✔
493
    });
12✔
494
  } else {
12✔
495
    thisNode = node;
496
  }
6✔
497

6✔
498
  if (thisNode.empty()) {
499
    return thisNode;
500
  }
501

502
  for (auto &a : thisNode.children()) {
503
    auto hash = MakeHash(a.name());
504

505
    if (ri.IsReflectedSubClass(hash)) {
506
      if (ri.IsArray(hash)) {
507
        for (auto sc : a.children()) {
508
          auto index = atoll(sc.name() + 2);
509
          auto rfInst = ri.GetReflectedSubClass(hash, index);
510
          if (!rfInst) {
8✔
511
            throw std::runtime_error("Class not registered!");
512
          }
513
          ReflectorPureWrap subRefl(rfInst);
8✔
514
          ReflectorXMLUtil::Load(subRefl, sc);
515
        }
516

517
      } else {
8✔
518
        auto rfInst = ri.GetReflectedSubClass(hash);
8✔
519
        if (!rfInst) {
8✔
520
          throw std::runtime_error("Class not registered!");
521
        }
75✔
522
        ReflectorPureWrap subRefl(rfInst);
75✔
523
        ReflectorXMLUtil::Load(subRefl, a);
524
      }
×
525
      continue;
526
    }
75✔
527

528
    ri.SetReflectedValue(hash, a.text().as_string());
529
  }
530

8✔
531
  return thisNode;
1✔
532
}
1✔
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