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

PredatorCZ / PreCore / 461

pending completion
461

push

github-actions-ci

PredatorCZ
update readme

3204 of 6096 relevant lines covered (52.56%)

354.05 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

3
    Copyright 2019-2023 Lukas Cone
4

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

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

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

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

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

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

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

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

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

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

82
    return true;
83
  }
84

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

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

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

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

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

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

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

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

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

134
  return varName;
148✔
135
}
136

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

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

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

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

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

179
  return thisNode;
8✔
180
}
181

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

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

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

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

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

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

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

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

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

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

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

300
  return thisNode;
8✔
301
}
302

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

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

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

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

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

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

343
    return retVal;
120✔
344
  };
345

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

8✔
469
  return thisNode;
470
}
471

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

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

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

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

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

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

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

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

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