Arcane  4.2.2.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 std::cout << "KEY='" << key << "'\n";
422 if (c.second.children.empty()) {
423 writeKeyValue(w, key, c.second.has_data ? c.second.data : std::string());
424 }
425 else {
426 if (!key.empty()) {
427 w.writeKey(key);
428 writeJsonNode(w, c.second);
429 }
430 }
431 }
432 w.endObject();
433 }
434}
435
436} // namespace
437
438/*---------------------------------------------------------------------------*/
439/*---------------------------------------------------------------------------*/
440
441namespace Arcane::Alina
442{
443
444/*---------------------------------------------------------------------------*/
445/*---------------------------------------------------------------------------*/
446
447PropertyTree::
448PropertyTree()
449: m_property_tree(new PropertyTreeImpl())
450, m_is_own(true)
451{
452}
453
454/*---------------------------------------------------------------------------*/
455/*---------------------------------------------------------------------------*/
456
457PropertyTree::
458PropertyTree(const PropertyTree& rhs)
459{
460 if (rhs.m_is_own) {
461 m_property_tree = new PropertyTreeImpl(toImpl(rhs));
462 m_is_own = true;
463 }
464 else {
465 m_property_tree = rhs.m_property_tree;
466 m_is_own = false;
467 }
468}
469
470/*---------------------------------------------------------------------------*/
471/*---------------------------------------------------------------------------*/
472
473PropertyTree::
474~PropertyTree()
475{
476 if (m_is_own)
477 delete &toImpl(this);
478}
479
480/*---------------------------------------------------------------------------*/
481/*---------------------------------------------------------------------------*/
482
483PropertyTree PropertyTree::
484get_child_empty(const std::string& path) const
485{
486 const PropertyTreeImpl* child = toImpl(this).findNode(path);
487 PropertyTree p;
488 p.m_property_tree = const_cast<PropertyTreeImpl*>(child ? child : &detail::empty_ptree());
489 p.m_is_own = false;
490 return p;
491}
492
493/*---------------------------------------------------------------------------*/
494/*---------------------------------------------------------------------------*/
495
496bool PropertyTree::
497erase(const char* name)
498{
499 return toImpl(this).eraseChild(name);
500}
501
502/*---------------------------------------------------------------------------*/
503/*---------------------------------------------------------------------------*/
504
505size_t PropertyTree::
506count(const char* name) const
507{
508 return toImpl(this).count(name);
509}
510
511/*---------------------------------------------------------------------------*/
512/*---------------------------------------------------------------------------*/
513
514void PropertyTree::
515read_json(const std::string& filename)
516{
517 std::ifstream is(filename, std::ios::binary);
518 if (!is)
519 ARCCORE_FATAL("Can not read JSON file '{0}'", filename);
520 std::string content((std::istreambuf_iterator<char>(is)), std::istreambuf_iterator<char>());
521
522 JSONDocument doc;
523 Span<const Byte> bytes(reinterpret_cast<const Byte*>(content.data()), content.size());
524 doc.parse(bytes, StringView(filename));
525
526 toImpl(this) = nodeFromJson(doc.root());
527}
528
529/*---------------------------------------------------------------------------*/
530/*---------------------------------------------------------------------------*/
531
532Int32 PropertyTree::get(const char* param_type, Int32 default_value) const
533{
534 const std::string* s = toImpl(this).getValue(param_type);
535 if (!s)
536 return default_value;
537 return parseInt32(*s, param_type);
538}
539Int64 PropertyTree::get(const char* param_type, Int64 default_value) const
540{
541 const std::string* s = toImpl(this).getValue(param_type);
542 if (!s)
543 return default_value;
544 return parseInteger(*s, param_type);
545}
546double PropertyTree::get(const char* param_type, double default_value) const
547{
548 const std::string* s = toImpl(this).getValue(param_type);
549 if (!s)
550 return default_value;
551 return parseReal(*s, param_type);
552}
553double* PropertyTree::get(const char* param_type, double* default_value) const
554{
555 const std::string* s = toImpl(this).getValue(param_type);
556 if (!s)
557 return default_value;
558 return static_cast<double*>(parsePointer(*s, param_type));
559}
560void* PropertyTree::get(const char* param_type, void* default_value) const
561{
562 const std::string* s = toImpl(this).getValue(param_type);
563 if (!s)
564 return default_value;
565 return parsePointer(*s, param_type);
566}
567std::string PropertyTree::get(const char* param_type, const std::string& default_value) const
568{
569 const std::string* s = toImpl(this).getValue(param_type);
570 if (!s)
571 return default_value;
572 return *s;
573}
574
575void PropertyTree::put(const std::string& path, Int32 value)
576{
577 toImpl(this).put(path, std::to_string(value));
578}
579void PropertyTree::put(const std::string& path, Int64 value)
580{
581 toImpl(this).put(path, std::to_string(value));
582}
583void PropertyTree::put(const std::string& path, double value)
584{
585 toImpl(this).put(path, realToString(value));
586}
587void PropertyTree::put(const std::string& path, const std::string& value)
588{
589 toImpl(this).put(path, value);
590}
591void PropertyTree::put(const std::string& path, double* value)
592{
593 toImpl(this).put(path, pointerToString(value));
594}
595void PropertyTree::put(const std::string& path, void* value)
596{
597 toImpl(this).put(path, pointerToString(value));
598}
599
600/*---------------------------------------------------------------------------*/
601/*---------------------------------------------------------------------------*/
602
603void PropertyTree::
604_addChild(const std::string& path, const char* name,
605 const PropertyTree& obj)
606{
607 toImpl(this).addChild(std::string(path) + name, toImpl(obj));
608}
609
610/*---------------------------------------------------------------------------*/
611/*---------------------------------------------------------------------------*/
612
613void PropertyTree::
614check_params(const std::set<std::string>& names) const
615{
616 const auto& p = toImpl(this);
617 bool has_error = false;
618 for (const auto& n : names) {
619 if (!p.count(n)) {
620 ARCCORE_ALINA_PARAM_MISSING(n);
621 }
622 }
623 for (const auto& v : p.children) {
624 if (!names.count(v.first)) {
625 std::cerr << "WARNING: unknown parameter " << v.first << "\n";
626 has_error = true;
627 }
628 }
629 if (has_error)
630 ARCCORE_FATAL("Invalid parameters");
631}
632
633/*---------------------------------------------------------------------------*/
634/*---------------------------------------------------------------------------*/
635
636void PropertyTree::
637check_params(const std::set<std::string>& names,
638 const std::set<std::string>& opt_names) const
639{
640 const auto& p = toImpl(this);
641 bool has_error = false;
642
643 for (const auto& n : names) {
644 if (!p.count(n)) {
645 ARCCORE_ALINA_PARAM_MISSING(n);
646 }
647 }
648 for (const auto& n : opt_names) {
649 if (!p.count(n)) {
650 ARCCORE_ALINA_PARAM_MISSING(n);
651 }
652 }
653 for (const auto& v : p.children) {
654 if (!names.count(v.first) && !opt_names.count(v.first)) {
655 std::cerr << "WARNING: unknown parameter " << v.first << "\n";
656 has_error = true;
657 }
658 }
659 if (has_error)
660 ARCCORE_FATAL("Invalid parameters");
661}
662
663/*---------------------------------------------------------------------------*/
664/*---------------------------------------------------------------------------*/
665
666void PropertyTree::
667putKeyValue(const std::string& param)
668{
669 size_t eq_pos = param.find('=');
670 if (eq_pos == std::string::npos)
671 ARCCORE_FATAL("param in put() should have \"key=value\" format (param='{0}')", param);
672 toImpl(this).put(param.substr(0, eq_pos), param.substr(eq_pos + 1));
673}
674
675/*---------------------------------------------------------------------------*/
676/*---------------------------------------------------------------------------*/
677
678detail::empty_params::
679empty_params(const PropertyTree& ap)
680{
681 const auto& p = toImpl(ap);
682 for (const auto& v : p.children) {
683 std::cerr << "Alina: unknown parameter " << v.first << "\n";
684 }
685}
686
687/*---------------------------------------------------------------------------*/
688/*---------------------------------------------------------------------------*/
689
690std::ostream& operator<<(std::ostream& o, const PropertyTree& obj)
691{
692 JSONWriter writer(JSONWriter::FormatFlags::None);
693 writeJsonNode(writer, toImpl(obj));
694 o << writer.getBuffer();
695 return o;
696}
697
698/*---------------------------------------------------------------------------*/
699/*---------------------------------------------------------------------------*/
700
701} // namespace Arcane::Alina
702
703/*---------------------------------------------------------------------------*/
704/*---------------------------------------------------------------------------*/
#define ARCCORE_FATAL(...)
Macro throwing a FatalErrorException.
Class to store parameters as a hierarchical key/value tree.
Definition AlinaUtils.h:112
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
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.