Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
CDIUtils.hxx
Go to the documentation of this file.
1
35#ifndef _OPENLCB_CDIUTILS_HXX_
36#define _OPENLCB_CDIUTILS_HXX_
37
38#include "sxmlc.h"
39
40namespace openlcb
41{
42
44{
45public:
48 {
50 void operator()(XMLDoc *doc)
51 {
52 if (!doc)
53 return;
54 cleanup_doc(doc);
55 XMLDoc_free(doc);
56 delete doc;
57 }
58 };
59
61 typedef std::unique_ptr<XMLDoc, XMLDocDeleter> xmldoc_ptr_t;
62
65 {
67 void operator()(const SXML_CHAR *data)
68 {
69 free(const_cast<SXML_CHAR *>(data));
70 }
71 };
74 typedef std::unique_ptr<const SXML_CHAR, SXmlStringDeleter> xmlstring_t;
75
81 static XMLNode *find_child_or_null(const XMLNode *parent, const char *tag)
82 {
83 auto count = XMLNode_get_children_count(parent);
84 for (int i = 0; i < count; ++i)
85 {
86 auto *c = XMLNode_get_child(parent, i);
87 if (strcmp(tag, c->tag) == 0)
88 return c;
89 }
90 return nullptr;
91 }
92
100 static string find_node_name(const XMLNode *node, const char *def)
101 {
102 auto *n = find_child_or_null(node, "name");
103 if (n == nullptr || n->text == nullptr)
104 return def;
105 return n->text;
106 }
107
113 static string find_node_description(const XMLNode *node)
114 {
115 auto *n = find_child_or_null(node, "description");
116 if (n == nullptr || n->text == nullptr)
117 return "";
118 return n->text;
119 }
120
121 // this is a helper function that checks to see if the passed node has an
122 // element named by *tag in it. Sets the bool found to true or false
123 // accordingly and also returns a nullptr (if element is undefined)
124 // otherwise it will return the value of the element
125 static int find_numeric_element(
126 const XMLNode *parent, const char *tag, bool *found)
127 {
128 auto *n = find_child_or_null(parent, tag);
129 // if there is no element defined set bool to false, return any value
130 if (n == nullptr || n->text == nullptr){
131 *found = false;
132 return 0;// there is no defined element
133 }
134 // if the numeric element was found set the bool to true and return it
135 *found = true;
136 return atoi(n->text); // convert to int and return element value
137 }
138
145 static int find_node_min(const XMLNode *node, bool *found)
146 {
147 return find_numeric_element(node, "min", found);
148 }
149
156 static int find_node_max(const XMLNode *node, bool *found)
157 {
158 return find_numeric_element(node, "max", found);
159 }
160
167 static int find_node_default(const XMLNode *node, bool *found)
168 {
169 return find_numeric_element(node, "default", found);
170 }
171
176 static void prepare_doc(XMLDoc *doc)
177 {
178 auto *node = XMLDoc_root(doc);
179 while (node)
180 {
181 node->user = nullptr;
182 node = XMLNode_next(node);
183 }
184 }
185
188 static void cleanup_doc(XMLDoc *doc)
189 {
190 auto *node = XMLDoc_root(doc);
191 while (node)
192 {
193 free(node->user);
194 node->user = nullptr;
195 node = XMLNode_next(node);
196 }
197 }
198
206 template <class T, typename... Args>
207 static void new_userinfo(T **info, XMLNode *node, Args &&... args)
208 {
209 HASSERT(node->user == nullptr);
210 static_assert(std::is_trivially_destructible<T>::value == true,
211 "Userdata attached to nodes must be trivially destructible");
212 *info = static_cast<T *>(malloc(sizeof(T)));
213 new (*info) T(std::forward<Args>(args)...);
214 node->user = *info;
215 }
216
223 template <class T> static void get_userinfo(T **info, const XMLNode *node)
224 {
225 HASSERT(node);
226 *info = static_cast<T *>(node->user);
227 }
228
230 struct NodeInfo
231 {
239 int size = 0;
240 };
241
251 const XMLNode *node, const char *attr_name, int def = 0)
252 {
253 const SXML_CHAR *attr_value;
254 XMLNode_get_attribute_with_default(
255 const_cast<XMLNode *>(node), attr_name, &attr_value, nullptr);
256 xmlstring_t d(attr_value);
257 if ((!attr_value) || (attr_value[0] == 0))
258 {
259 return def;
260 }
261 return atoi(attr_value);
262 }
263
265 enum class DataType {
266 UNKNOWN = 0,
267 GROUP,
268 INT,
269 FLOAT,
270 STRING,
271 EVENTID
272 };
273
277 static DataType get_type_from_node(XMLNode *child)
278 {
279 if (strcmp(child->tag, "group") == 0)
280 {
281 return DataType::GROUP;
282 }
283 if (strcmp(child->tag, "int") == 0)
284 {
285 return DataType::INT;
286 }
287 if (strcmp(child->tag, "eventid") == 0)
288 {
289 return DataType::EVENTID;
290 }
291 if (strcmp(child->tag, "string") == 0)
292 {
293 return DataType::STRING;
294 }
295 if (strcmp(child->tag, "float") == 0)
296 {
297 return DataType::FLOAT;
298 }
299 return DataType::UNKNOWN;
300 };
301
305 static void layout_segment(XMLNode *segment)
306 {
307 HASSERT(strcmp(segment->tag, "segment") == 0);
308 unsigned current_offset = get_numeric_attribute(segment, "origin");
309 NodeInfo *info;
310 new_userinfo(&info, segment);
311 info->offset_from_parent = current_offset;
312 if (XMLNode_get_children_count(segment) == 0)
313 return;
314 XMLNode *current_parent = segment;
315 XMLNode *current_child = XMLNode_get_child(segment, 0);
316 NodeInfo *parent_info = info;
317 while (true)
318 {
319 if (strcmp(current_child->tag, "name") == 0 ||
320 strcmp(current_child->tag, "description") == 0 ||
321 strcmp(current_child->tag, "repname") == 0)
322 {
323 // Do nothing, not a data element
324 }
325 else
326 {
327 new_userinfo(&info, current_child);
328 parent_info->size +=
329 get_numeric_attribute(current_child, "offset", 0);
330 info->offset_from_parent = parent_info->size;
331 auto type = get_type_from_node(current_child);
332 switch (type)
333 {
334 case DataType::UNKNOWN:
335 // Probably should not get here.
336 break;
337 case DataType::EVENTID:
338 info->size = 8;
339 break;
340 case DataType::STRING:
341 case DataType::INT:
342 case DataType::FLOAT:
343 info->size =
344 get_numeric_attribute(current_child, "size", 1);
345 break;
346 case DataType::GROUP:
347 if (XMLNode_get_children_count(current_child) > 0)
348 {
349 current_parent = current_child;
350 current_child =
351 XMLNode_get_child(current_parent, 0);
352 get_userinfo(&parent_info, current_parent);
353 continue;
354 }
355 // an empty group has size == 0 and we don't need to do
356 // anything here.
357 break;
358 }
359 parent_info->size += info->size;
360 }
361
362 // Move to next child.
363 while ((current_child = XMLNode_next_sibling(current_child)) ==
364 nullptr)
365 {
366 // End of children; must go up.
367 if (current_parent == segment)
368 {
369 // nowhere to go up
370 break;
371 }
372 current_child = current_parent;
373 current_parent = current_child->father;
374 // handle groups with repetitions
375 get_userinfo(&info, current_child);
376 get_userinfo(&parent_info, current_parent);
377 int repcount = get_replication(current_child);
378 info->size *= repcount;
379 parent_info->size += info->size;
380 }
381 if (current_parent == segment && current_child == nullptr)
382 {
383 // end of iteration
384 break;
385 }
386 }
387 }
388
392 static int get_replication(const XMLNode *group)
393 {
394 HASSERT(strcmp(group->tag, "group") == 0);
395 return get_numeric_attribute(group, "replication", 1);
396 }
397
399 {
401 const XMLNode *node_;
407 unsigned address_;
408
412 : node_(nullptr)
413 , address_(0)
414 {
415 }
416
420 CDINodeRep(const XMLNode *node, std::nullptr_t)
421 : node_(node)
422 , address_(0)
423 {
424 }
425
428 CDINodeRep(const XMLNode *segment)
429 {
430 NodeInfo *info;
431 get_userinfo(&info, segment);
432 HASSERT(info);
433 node_ = segment;
435 }
436
442 CDINodeRep(const CDINodeRep *parent, const XMLNode *child)
443 {
444 HASSERT(child->father == parent->node_);
445 node_ = child;
446 if (strcmp(child->tag, "group") == 0)
447 {
448 HASSERT(get_replication(child) == 1);
449 }
450 address_ = parent->get_child_address(child);
451 }
452
460 CDINodeRep(const CDINodeRep *parent, const XMLNode *group, unsigned replica)
461 {
462 HASSERT(group->father == parent->node_);
463 node_ = group;
464 int replication = get_replication(group);
465 HASSERT(replication > 1);
466 NodeInfo *info;
467 get_userinfo(&info, group);
468 HASSERT(info);
469 unsigned base_address = parent->get_child_address(group);
470 unsigned stride = info->size / replication;
471 address_ = base_address + stride * replica;
472 }
473
476 template <typename... Args> void reset(Args &&... args)
477 {
478 new (this) CDINodeRep(std::forward<Args>(args)...);
479 }
480
484 unsigned get_child_address(const XMLNode *child) const
485 {
486 HASSERT(child->father == node_);
487 NodeInfo *info;
488 get_userinfo(&info, child);
489 HASSERT(info);
490 return address_ + info->offset_from_parent;
491 }
492
494 unsigned get_child_size(const XMLNode *child) const
495 {
496 HASSERT(child->father == node_);
497 NodeInfo *info;
498 get_userinfo(&info, child);
499 HASSERT(info);
500 return info->size;
501 }
502 };
503
504private:
507};
508
509} // namespace openlcb
510
511#endif // _OPENLCB_CDIUTILS_HXX_
DataType
Used to classify elements.
Definition CDIUtils.hxx:265
static int find_node_default(const XMLNode *node, bool *found)
Finds the default value of the xml integer, if there isn't one it returns a null pointer.
Definition CDIUtils.hxx:167
static void layout_segment(XMLNode *segment)
Allocates all userinfo structures within a segment and performs the offset layout algorithm.
Definition CDIUtils.hxx:305
static void get_userinfo(T **info, const XMLNode *node)
Retrieve the userinfo structure from an XML node.
Definition CDIUtils.hxx:223
static string find_node_name(const XMLNode *node, const char *def)
Finds the name of a CDI element.
Definition CDIUtils.hxx:100
CDIUtils()
Static class; never instantiated.
static int get_numeric_attribute(const XMLNode *node, const char *attr_name, int def=0)
Helper function to find and convert an attribute to a number.
Definition CDIUtils.hxx:250
static void cleanup_doc(XMLDoc *doc)
Deletes all userinfo structures allocated in a doc.
Definition CDIUtils.hxx:188
static void new_userinfo(T **info, XMLNode *node, Args &&... args)
Allocates a new object of type T for the node as userinfo; calls T's constructor with args....
Definition CDIUtils.hxx:207
static int get_replication(const XMLNode *group)
Definition CDIUtils.hxx:392
static int find_node_max(const XMLNode *node, bool *found)
Finds the max value of the xml integer, if there isn't one it returns a null pointer.
Definition CDIUtils.hxx:156
std::unique_ptr< const SXML_CHAR, SXmlStringDeleter > xmlstring_t
Smart pointer class for holding C strings from sxml.
Definition CDIUtils.hxx:74
static int find_node_min(const XMLNode *node, bool *found)
Finds the min value of the xml integer, if there isn't one it returns a null pointer.
Definition CDIUtils.hxx:145
static string find_node_description(const XMLNode *node)
Find the description of a CDI element.
Definition CDIUtils.hxx:113
static void prepare_doc(XMLDoc *doc)
Clears out all user info structure pointers.
Definition CDIUtils.hxx:176
static DataType get_type_from_node(XMLNode *child)
Classifies XML elements to node types.
Definition CDIUtils.hxx:277
std::unique_ptr< XMLDoc, XMLDocDeleter > xmldoc_ptr_t
Smart pointer class for holding XML documents.
Definition CDIUtils.hxx:61
static XMLNode * find_child_or_null(const XMLNode *parent, const char *tag)
Searches the list of children for the first child with a specific tag.
Definition CDIUtils.hxx:81
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
CDINodeRep(const CDINodeRep *parent, const XMLNode *group, unsigned replica)
Initializes a group rep for a given replica.
Definition CDIUtils.hxx:460
unsigned get_child_size(const XMLNode *child) const
Gets the size of a child (number of bytes occupied).
Definition CDIUtils.hxx:494
CDINodeRep(const XMLNode *segment)
Initializes a group rep from a segment root.
Definition CDIUtils.hxx:428
unsigned address_
address in the current space of the beginning of the current node.
Definition CDIUtils.hxx:407
CDINodeRep(const XMLNode *node, std::nullptr_t)
Initializes a group rep from an arbitrary XML element (e.g.
Definition CDIUtils.hxx:420
const XMLNode * node_
Element in the XML where we are. This is a segment or a group node.
Definition CDIUtils.hxx:401
CDINodeRep()
Default constructor.
Definition CDIUtils.hxx:411
unsigned get_child_address(const XMLNode *child) const
Gets the absolute address of a given child in the current segment.
Definition CDIUtils.hxx:484
void reset(Args &&... args)
Resets the current representation.
Definition CDIUtils.hxx:476
CDINodeRep(const CDINodeRep *parent, const XMLNode *child)
Initializes a group rep which has no replication or an entry rep.
Definition CDIUtils.hxx:442
Allocation data we hold about a Data Element in its userinfo structure.
Definition CDIUtils.hxx:231
int size
Total number of bytes that this element occupies.
Definition CDIUtils.hxx:239
int offset_from_parent
Offset of the address of this element from the address of the parent group element.
Definition CDIUtils.hxx:236
Helper class for deleting sxml strings.
Definition CDIUtils.hxx:65
void operator()(const SXML_CHAR *data)
Deletion operator.
Definition CDIUtils.hxx:67
Helper class for unique_ptr to delete an XML document correctly.
Definition CDIUtils.hxx:48
void operator()(XMLDoc *doc)
Deletion operator.
Definition CDIUtils.hxx:50