Arcane  4.2.3.0
Developer documentation
Loading...
Searching...
No Matches
AlinaUtils.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* AlinaUtils.cc (C) 2000-2026 */
9/* */
10/* Utility classes. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arccore/alina/AlinaUtils.h"
15
16#include "arccore/base/Convert.h"
17
18#include "arccore/common/JSONReader.h"
19#include "arccore/common/JSONWriter.h"
20
21#include "arccore/alina/CSRMatrixView.h"
22
23#include <charconv>
24#include <fstream>
25#include <list>
26
27/*---------------------------------------------------------------------------*/
28/*---------------------------------------------------------------------------*/
29
30namespace Arcane::Alina::detail
31{
32
33/*---------------------------------------------------------------------------*/
34/*---------------------------------------------------------------------------*/
46{
47 public:
48
49 std::string data;
50 bool has_data = false;
51 std::list<std::pair<std::string, PropertyTreeImpl>> children;
52
53 public:
54
55 bool empty() const { return !has_data && children.empty(); }
56
57 public:
58
60 PropertyTreeImpl* findChild(const std::string& name)
61 {
62 for (auto& c : children)
63 if (c.first == name)
64 return &c.second;
65 return nullptr;
66 }
67 const PropertyTreeImpl* findChild(const std::string& name) const
68 {
69 for (const auto& c : children)
70 if (c.first == name)
71 return &c.second;
72 return nullptr;
73 }
74
76 size_t count(const std::string& name) const
77 {
78 size_t n = 0;
79 for (const auto& c : children)
80 if (c.first == name)
81 ++n;
82 return n;
83 }
84
86 bool eraseChild(const std::string& name)
87 {
88 auto it = children.begin();
89 while (it != children.end()) {
90 if (it->first == name) {
91 children.erase(it);
92 return true;
93 }
94 ++it;
95 }
96 return false;
97 }
98
99 public:
100
103 PropertyTreeImpl* findNode(const std::string& path)
104 {
105 if (path.empty())
106 return this;
107 PropertyTreeImpl* n = this;
108 for (const auto& part : _splitPath(path)) {
109 n = n->findChild(part);
110 if (!n)
111 return nullptr;
112 }
113 return n;
114 }
115 const PropertyTreeImpl* findNode(const std::string& path) const
116 {
117 if (path.empty())
118 return this;
119 const PropertyTreeImpl* n = this;
120 for (const auto& part : _splitPath(path)) {
121 n = n->findChild(part);
122 if (!n)
123 return nullptr;
124 }
125 return n;
126 }
127
129 const std::string* getValue(const std::string& path) const
130 {
131 const PropertyTreeImpl* n = findNode(path);
132 if (n && n->has_data)
133 return &n->data;
134 return nullptr;
135 }
136
137 public:
138
141 void put(const std::string& path, const std::string& value)
142 {
143 PropertyTreeImpl* n = _createNode(path);
144 n->data = value;
145 n->has_data = true;
146 }
147
150 void addChild(const std::string& path, const PropertyTreeImpl& subtree)
151 {
152 std::vector<std::string> parts = _splitPath(path);
153 if (parts.empty())
154 return;
155 std::string parent_path;
156 for (size_t i = 0; i + 1 < parts.size(); ++i) {
157 if (!parent_path.empty())
158 parent_path += '.';
159 parent_path += parts[i];
160 }
161 PropertyTreeImpl* parent = _createNode(parent_path);
162 parent->children.emplace_back(std::move(parts.back()), subtree);
163 }
164
165 private:
166
167 static std::vector<std::string> _splitPath(const std::string& path)
168 {
169 std::vector<std::string> parts;
170 std::string current;
171 for (char c : path) {
172 if (c == '.') {
173 parts.push_back(current);
174 current.clear();
175 }
176 else {
177 current += c;
178 }
179 }
180 parts.push_back(current);
181 return parts;
182 }
183
185 PropertyTreeImpl* _createNode(const std::string& path)
186 {
187 PropertyTreeImpl* n = this;
188 for (const auto& part : _splitPath(path)) {
189 PropertyTreeImpl* next = n->findChild(part);
190 if (!next)
191 next = &n->children.emplace_back(part, PropertyTreeImpl()).second;
192 n = next;
193 }
194 return n;
195 }
196};
197
198/*---------------------------------------------------------------------------*/
199/*---------------------------------------------------------------------------*/
200
201const PropertyTreeImpl& empty_ptree()
202{
203 static const PropertyTreeImpl p;
204 return p;
205}
206
207// To access PropertyTree::m_property_tree as 'detail::PropertyTreeImpl'
209{
210 public:
211
212 static const PropertyTreeImpl& toImpl(const PropertyTree& p)
213 {
214 return *(static_cast<const PropertyTreeImpl*>(p.m_property_tree));
215 }
216 static PropertyTreeImpl& toImpl(PropertyTree& p)
217 {
218 return *(static_cast<PropertyTreeImpl*>(p.m_property_tree));
219 }
220};
221
222/*---------------------------------------------------------------------------*/
223/*---------------------------------------------------------------------------*/
224
225} // namespace Arcane::Alina::detail
226
227/*---------------------------------------------------------------------------*/
228/*---------------------------------------------------------------------------*/
229
230namespace
231{
232using namespace Arcane;
233using namespace Arcane::Alina;
235const PropertyTreeImpl& toImpl(const PropertyTree& p)
236{
237 return detail::PropertyWrapper::toImpl(p);
238}
239const PropertyTreeImpl& toImpl(const PropertyTree* p)
240{
241 return detail::PropertyWrapper::toImpl(*p);
242}
243PropertyTreeImpl& toImpl(PropertyTree* p)
244{
245 return detail::PropertyWrapper::toImpl(*p);
246}
247
248/*---------------------------------------------------------------------------*/
249/*---------------------------------------------------------------------------*/
250
251std::string realToString(double v)
252{
253 char buf[64];
254 auto res = std::to_chars(buf, buf + sizeof(buf), v);
255 return std::string(buf, res.ptr);
256}
257
258bool tryParseInt64(const std::string& s, Int64& v)
259{
261 if (!x)
262 return false;
263 v = *x;
264 return true;
265}
266
267bool tryParseDouble(const std::string& s, double& v)
268{
270 if (!x)
271 return false;
272 v = Convert::toDouble(*x);
273 return true;
274}
275
276// 'true' and 'false' are accepted to ease the reading of boolean
277// parameters stored in a JSON file.
278Int64 parseInteger(const std::string& s, const char* name)
279{
280 if (s == "true")
281 return 1;
282 if (s == "false")
283 return 0;
284 Int64 v = 0;
285 if (!tryParseInt64(s, v))
286 ARCCORE_FATAL("Can not convert value '{0}' to integer for parameter '{1}'", s, name);
287 return v;
288}
289
290Int32 parseInt32(const std::string& s, const char* name)
291{
292 Int64 v = parseInteger(s, name);
293 if (v < static_cast<Int64>(std::numeric_limits<Int32>::min()) ||
294 v > static_cast<Int64>(std::numeric_limits<Int32>::max()))
295 ARCCORE_FATAL("Value '{0}' is out of range for parameter '{1}'", s, name);
296 return static_cast<Int32>(v);
297}
298
299double parseReal(const std::string& s, const char* name)
300{
301 if (s == "true")
302 return 1.0;
303 if (s == "false")
304 return 0.0;
305 double v = 0.0;
306 if (!tryParseDouble(s, v))
307 ARCCORE_FATAL("Can not convert value '{0}' to real for parameter '{1}'", s, name);
308 return v;
309}
310
311void* parsePointer(const std::string& s, const char* name)
312{
313 if (s.empty())
314 return nullptr;
315 // The value is a hexadecimal number with an optional '0x' prefix.
317 if (!x)
318 ARCCORE_FATAL("Can not convert value '{0}' to pointer for parameter '{1}'", s, name);
319 return reinterpret_cast<void*>(static_cast<std::uintptr_t>(*x));
320}
321
322std::string pointerToString(const void* p)
323{
324 std::ostringstream ostr;
325 ostr << "0x" << std::hex << reinterpret_cast<std::uintptr_t>(p);
326 return ostr.str();
327}
328
329/*---------------------------------------------------------------------------*/
330/*---------------------------------------------------------------------------*/
331
332PropertyTreeImpl nodeFromJson(const JSONValue& v)
333{
334 PropertyTreeImpl n;
335 if (v.isObject()) {
336 for (const auto& kv : v.keyValueChildren()) {
337 std::string name(kv.name().toStdStringView());
338 n.children.emplace_back(std::move(name), nodeFromJson(kv.value()));
339 }
340 }
341 else if (v.isArray()) {
342 for (const auto& e : v.valueAsArray())
343 n.children.emplace_back("", nodeFromJson(e));
344 }
345 else if (v.isString()) {
346 n.data = std::string(v.valueAsStringView().toStdStringView());
347 n.has_data = true;
348 }
349 else if (v.isBool()) {
350 n.data = v.valueAsBool() ? "true" : "false";
351 n.has_data = true;
352 }
353 else if (v.isNumber()) {
354 if (v.isInt64()) {
355 n.data = std::to_string(v.valueAsInt64());
356 }
357 else if (v.isUint64()) {
358 UInt64 u = v.valueAsUInt64();
359 if (u <= static_cast<UInt64>(std::numeric_limits<Int64>::max()))
360 n.data = std::to_string(static_cast<Int64>(u));
361 else
362 n.data = realToString(static_cast<double>(u));
363 }
364 else {
365 n.data = realToString(v.valueAsReal());
366 }
367 n.has_data = true;
368 }
369 // A 'null' value produces an empty node.
370 return n;
371}
372
373/*---------------------------------------------------------------------------*/
374/*---------------------------------------------------------------------------*/
375
376void writeKeyValue(JSONWriter& w, const StringView& key, const std::string& s)
377{
378 if (s == "true") {
379 w.write(key, true);
380 return;
381 }
382 if (s == "false") {
383 w.write(key, false);
384 return;
385 }
386 Int64 i = 0;
387 if (tryParseInt64(s, i)) {
388 w.write(key, i);
389 return;
390 }
391 double d = 0.0;
392 if (tryParseDouble(s, d)) {
393 w.write(key, d);
394 return;
395 }
396 w.write(key, StringView(s));
397}
398
399void writeJsonNode(JSONWriter& w, const PropertyTreeImpl& n)
400{
401 if (n.children.empty()) {
402 w.writeValue(n.has_data ? StringView(n.data) : StringView());
403 return;
404 }
405 bool is_array = true;
406 for (const auto& c : n.children)
407 if (!c.first.empty()) {
408 is_array = false;
409 break;
410 }
411 if (is_array) {
412 w.beginArray();
413 for (const auto& c : n.children)
414 writeJsonNode(w, c.second);
415 w.endArray();
416 }
417 else {
418 w.beginObject();
419 for (const auto& c : n.children) {
420 String key(c.first);
421 if (c.second.children.empty()) {
422 writeKeyValue(w, key, c.second.has_data ? c.second.data : std::string());
423 }
424 else {
425 if (!key.empty()) {
426 w.writeKey(key);
427 writeJsonNode(w, c.second);
428 }
429 }
430 }
431 w.endObject();
432 }
433}
434
435} // namespace
436
437/*---------------------------------------------------------------------------*/
438/*---------------------------------------------------------------------------*/
439
440namespace Arcane::Alina
441{
442
443/*---------------------------------------------------------------------------*/
444/*---------------------------------------------------------------------------*/
445
446PropertyTree::
447PropertyTree(PropertyTreeImpl* p, bool is_own)
448: m_property_tree(p)
449, m_is_own(is_own)
450{
451}
452
453PropertyTree::
454PropertyTree()
455: m_property_tree(new PropertyTreeImpl())
456, m_is_own(true)
457{
458}
459
460/*---------------------------------------------------------------------------*/
461/*---------------------------------------------------------------------------*/
462
463PropertyTree::
464PropertyTree(const PropertyTree& rhs)
465{
466 if (rhs.m_is_own) {
467 m_property_tree = new PropertyTreeImpl(toImpl(rhs));
468 m_is_own = true;
469 }
470 else {
471 m_property_tree = rhs.m_property_tree;
472 m_is_own = false;
473 }
474}
475
476/*---------------------------------------------------------------------------*/
477/*---------------------------------------------------------------------------*/
478
479PropertyTree& PropertyTree::
480operator=(const PropertyTree& rhs)
481{
482 // Always take an independent copy. In particular, rhs can be a non-owning
483 // child view into this tree, so copy it before releasing the current tree.
484 auto* new_property_tree = new PropertyTreeImpl(toImpl(rhs));
485 if (m_is_own)
486 delete &toImpl(this);
487 m_property_tree = new_property_tree;
488 m_is_own = true;
489 return *this;
490}
491
492/*---------------------------------------------------------------------------*/
493/*---------------------------------------------------------------------------*/
494
495PropertyTree::
496~PropertyTree()
497{
498 if (m_is_own)
499 delete &toImpl(this);
500}
501
502/*---------------------------------------------------------------------------*/
503/*---------------------------------------------------------------------------*/
504
505PropertyTree PropertyTree::
506get_child_empty(const std::string& path) const
507{
508 const PropertyTreeImpl* child = toImpl(this).findNode(path);
509 if (!child)
510 child = &detail::empty_ptree();
511 PropertyTree p(const_cast<PropertyTreeImpl*>(child),false);
512 return p;
513}
514
515/*---------------------------------------------------------------------------*/
516/*---------------------------------------------------------------------------*/
517
518bool PropertyTree::
519erase(const char* name)
520{
521 return toImpl(this).eraseChild(name);
522}
523
524/*---------------------------------------------------------------------------*/
525/*---------------------------------------------------------------------------*/
526
527size_t PropertyTree::
528count(const char* name) const
529{
530 return toImpl(this).count(name);
531}
532
533/*---------------------------------------------------------------------------*/
534/*---------------------------------------------------------------------------*/
535
536void PropertyTree::
537read_json(const std::string& filename)
538{
539 std::ifstream is(filename, std::ios::binary);
540 if (!is)
541 ARCCORE_FATAL("Can not read JSON file '{0}'", filename);
542 std::string content((std::istreambuf_iterator<char>(is)), std::istreambuf_iterator<char>());
543
544 JSONDocument doc;
545 Span<const Byte> bytes(reinterpret_cast<const Byte*>(content.data()), content.size());
546 doc.parse(bytes, StringView(filename));
547
548 toImpl(this) = nodeFromJson(doc.root());
549}
550
551/*---------------------------------------------------------------------------*/
552/*---------------------------------------------------------------------------*/
553
554Int32 PropertyTree::get(const char* param_type, Int32 default_value) const
555{
556 const std::string* s = toImpl(this).getValue(param_type);
557 if (!s)
558 return default_value;
559 return parseInt32(*s, param_type);
560}
561Int64 PropertyTree::get(const char* param_type, Int64 default_value) const
562{
563 const std::string* s = toImpl(this).getValue(param_type);
564 if (!s)
565 return default_value;
566 return parseInteger(*s, param_type);
567}
568double PropertyTree::get(const char* param_type, double default_value) const
569{
570 const std::string* s = toImpl(this).getValue(param_type);
571 if (!s)
572 return default_value;
573 return parseReal(*s, param_type);
574}
575double* PropertyTree::get(const char* param_type, double* default_value) const
576{
577 const std::string* s = toImpl(this).getValue(param_type);
578 if (!s)
579 return default_value;
580 return static_cast<double*>(parsePointer(*s, param_type));
581}
582void* PropertyTree::get(const char* param_type, void* default_value) const
583{
584 const std::string* s = toImpl(this).getValue(param_type);
585 if (!s)
586 return default_value;
587 return parsePointer(*s, param_type);
588}
589std::string PropertyTree::get(const char* param_type, const std::string& default_value) const
590{
591 const std::string* s = toImpl(this).getValue(param_type);
592 if (!s)
593 return default_value;
594 return *s;
595}
596
597void PropertyTree::put(const std::string& path, Int32 value)
598{
599 toImpl(this).put(path, std::to_string(value));
600}
601void PropertyTree::put(const std::string& path, Int64 value)
602{
603 toImpl(this).put(path, std::to_string(value));
604}
605void PropertyTree::put(const std::string& path, double value)
606{
607 toImpl(this).put(path, realToString(value));
608}
609void PropertyTree::put(const std::string& path, const std::string& value)
610{
611 toImpl(this).put(path, value);
612}
613void PropertyTree::put(const std::string& path, double* value)
614{
615 toImpl(this).put(path, pointerToString(value));
616}
617void PropertyTree::put(const std::string& path, void* value)
618{
619 toImpl(this).put(path, pointerToString(value));
620}
621
622/*---------------------------------------------------------------------------*/
623/*---------------------------------------------------------------------------*/
624
625void PropertyTree::
626_addChild(const std::string& path, const char* name,
627 const PropertyTree& obj)
628{
629 toImpl(this).addChild(std::string(path) + name, toImpl(obj));
630}
631
632/*---------------------------------------------------------------------------*/
633/*---------------------------------------------------------------------------*/
634
635void PropertyTree::
636check_params(const std::set<std::string>& names) const
637{
638 const auto& p = toImpl(this);
639 bool has_error = false;
640 for (const auto& n : names) {
641 if (!p.count(n)) {
642 ARCCORE_ALINA_PARAM_MISSING(n);
643 }
644 }
645 for (const auto& v : p.children) {
646 if (!names.count(v.first)) {
647 std::cerr << "WARNING: unknown parameter " << v.first << "\n";
648 has_error = true;
649 }
650 }
651 if (has_error)
652 ARCCORE_FATAL("Invalid parameters");
653}
654
655/*---------------------------------------------------------------------------*/
656/*---------------------------------------------------------------------------*/
657
658void PropertyTree::
659check_params(const std::set<std::string>& names,
660 const std::set<std::string>& opt_names) const
661{
662 const auto& p = toImpl(this);
663 bool has_error = false;
664
665 for (const auto& n : names) {
666 if (!p.count(n)) {
667 ARCCORE_ALINA_PARAM_MISSING(n);
668 }
669 }
670 for (const auto& n : opt_names) {
671 if (!p.count(n)) {
672 ARCCORE_ALINA_PARAM_MISSING(n);
673 }
674 }
675 for (const auto& v : p.children) {
676 if (!names.count(v.first) && !opt_names.count(v.first)) {
677 std::cerr << "WARNING: unknown parameter " << v.first << "\n";
678 has_error = true;
679 }
680 }
681 if (has_error)
682 ARCCORE_FATAL("Invalid parameters");
683}
684
685/*---------------------------------------------------------------------------*/
686/*---------------------------------------------------------------------------*/
687
688void PropertyTree::
689putKeyValue(const std::string& param)
690{
691 size_t eq_pos = param.find('=');
692 if (eq_pos == std::string::npos)
693 ARCCORE_FATAL("param in put() should have \"key=value\" format (param='{0}')", param);
694 toImpl(this).put(param.substr(0, eq_pos), param.substr(eq_pos + 1));
695}
696
697/*---------------------------------------------------------------------------*/
698/*---------------------------------------------------------------------------*/
699
700detail::empty_params::
701empty_params(const PropertyTree& ap)
702{
703 const auto& p = toImpl(ap);
704 for (const auto& v : p.children) {
705 std::cerr << "Alina: unknown parameter " << v.first << "\n";
706 }
707}
708
709/*---------------------------------------------------------------------------*/
710/*---------------------------------------------------------------------------*/
711
712std::ostream& operator<<(std::ostream& o, const PropertyTree& obj)
713{
714 JSONWriter writer(JSONWriter::FormatFlags::None);
715 writeJsonNode(writer, toImpl(obj));
716 o << writer.getBuffer();
717 return o;
718}
719
720/*---------------------------------------------------------------------------*/
721/*---------------------------------------------------------------------------*/
722
723} // namespace Arcane::Alina
724
725/*---------------------------------------------------------------------------*/
726/*---------------------------------------------------------------------------*/
#define ARCCORE_FATAL(...)
Macro throwing a FatalErrorException.
Class to store parameters as a hierarchical key/value tree.
Definition AlinaUtils.h:113
Minimal hierarchical property tree with string leaf values.
Definition AlinaUtils.cc:46
PropertyTreeImpl * findChild(const std::string &name)
First direct child with name name or nullptr if not found.
Definition AlinaUtils.cc:60
size_t count(const std::string &name) const
Number of direct children with name name.
Definition AlinaUtils.cc:76
void put(const std::string &path, const std::string &value)
const std::string * getValue(const std::string &path) const
Leaf value at the '.'-separated path path or nullptr if not found.
PropertyTreeImpl * findNode(const std::string &path)
void addChild(const std::string &path, const PropertyTreeImpl &subtree)
PropertyTreeImpl * _createNode(const std::string &path)
Node at the '.'-separated path path, creating intermediate nodes.
bool eraseChild(const std::string &name)
Remove the first direct child with name name.
Definition AlinaUtils.cc:86
static std::optional< T > tryParse(StringView s)
Converts s to type T.
void parse(Span< const Byte > bytes, Int16 flags=ParseNoFlags)
Reads the file in UTF-8 format.
JSONValue root() const
Root element.
bool isUint64() const
True if the value is an integer in the 'UInt64' range.
UInt64 valueAsUInt64() const
Value in UInt64 format. Returns 0 if 'null()' is true.
Real valueAsReal() const
Value in Real format. Returns 0.0 if 'null()' is true.
bool valueAsBool() const
Value in boolean format. Returns false if 'null()' is true.
Int64 valueAsInt64() const
Value in Int64 format. Returns 0 if 'null()' is true.
bool isInt64() const
True if the value is an integer in the 'Int64' range.
StringView valueAsStringView() const
Value in StringView format. The string is empty if 'null()' is true.
bool isBool() const
True if the value is a boolean.
bool isString() const
True if the value is a string.
bool isNumber() const
True if the value is a number.
View of a UTF-8 character string.
Definition StringView.h:44
std::string_view toStdStringView() const ARCCORE_NOEXCEPT
Returns an STL view of the current view.
Definition StringView.h:109
constexpr bool empty() const ARCCORE_NOEXCEPT
True if the string is null or empty.
Definition StringView.h:102
double toDouble(Real r)
Converts a Real to double.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
std::uint64_t UInt64
Unsigned integer type of 64 bits.
std::int64_t Int64
Signed integer type of 64 bits.
unsigned char Byte
Type of a byte.
Definition BaseTypes.h:42
std::int32_t Int32
Signed integer type of 32 bits.