Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Foundation/DataExchange/VariantValue.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
6#include "Stroika/Foundation/Characters/FloatConversion.h"
8#include "Stroika/Foundation/Characters/String2Int.h"
10#include "Stroika/Foundation/Containers/Concrete/Mapping_HashTable.h"
12#include "Stroika/Foundation/DataExchange/BadFormatException.h"
14#include "Stroika/Foundation/Math/Common.h"
16
17#include "VariantValue.h"
18
19using namespace Stroika::Foundation;
20using namespace Characters::Literals;
22
23using Memory::MakeSharedPtr;
24
25// Comment this in to turn on aggressive noisy DbgTrace in this module
26// #define USE_NOISY_TRACE_IN_THIS_MODULE_ 1
27
28// see Satisfies Concepts:
29static_assert (regular<VariantValue>);
30
31namespace {
32 // Mimic private class values - but cannot reference directly cuz private and this isn't part of the class
33 using IntegerType_ = long long int;
34 using UnsignedIntegerType_ = unsigned long long int;
35 using FloatType_ = long double;
36
37 // simple mappings to make use of TIRep_<> private template
38 template <typename T>
39 struct TN_ {};
40 template <>
41 struct TN_<bool> {
42 static constexpr VariantValue::Type kTYPEENUM{VariantValue::eBoolean};
43 };
44 template <>
45 struct TN_<Memory::BLOB> {
46 static constexpr VariantValue::Type kTYPEENUM{VariantValue::eBLOB};
47 };
48 template <>
49 struct TN_<IntegerType_> {
50 static constexpr VariantValue::Type kTYPEENUM{VariantValue::eInteger};
51 };
52 template <>
53 struct TN_<UnsignedIntegerType_> {
54 static constexpr VariantValue::Type kTYPEENUM{VariantValue::eUnsignedInteger};
55 };
56 template <>
57 struct TN_<FloatType_> {
58 static constexpr VariantValue::Type kTYPEENUM{VariantValue::eFloat};
59 };
60 template <>
61 struct TN_<Date> {
62 static constexpr VariantValue::Type kTYPEENUM{VariantValue::eDate};
63 };
64 template <>
65 struct TN_<DateTime> {
66 static constexpr VariantValue::Type kTYPEENUM{VariantValue::eDateTime};
67 };
68 template <>
69 struct TN_<String> {
70 static constexpr VariantValue::Type kTYPEENUM{VariantValue::eString};
71 };
72 template <>
73 struct TN_<Sequence<VariantValue>> {
74 static constexpr VariantValue::Type kTYPEENUM{VariantValue::eArray};
75 };
76 template <>
77 struct TN_<Mapping<String, VariantValue>> {
78 static constexpr VariantValue::Type kTYPEENUM{VariantValue::eMap};
79 };
80}
81
82template <typename T>
83struct VariantValue::TIRep_ final : VariantValue::IRep_, public Memory::UseBlockAllocationIfAppropriate<TIRep_<T>> {
84 template <typename FWD>
85 inline TIRep_ (FWD&& v)
86 : fVal{forward<FWD> (v)}
87 {
88 }
89 virtual Type GetType () const override
90 {
91 return TN_<T>::kTYPEENUM;
92 }
93 T fVal;
94};
95
96/*
97 ********************************************************************************
98 ******************************** VariantValue **********************************
99 ********************************************************************************
100 */
101const shared_ptr<VariantValue::IRep_> VariantValue::kFalseRep_ = MakeSharedPtr<TIRep_<bool>> (false);
102const shared_ptr<VariantValue::IRep_> VariantValue::kTrueRep_ = MakeSharedPtr<TIRep_<bool>> (true);
103
105 : fVal_{MakeSharedPtr<TIRep_<Memory::BLOB>> (val)}
106{
107}
108
109VariantValue::VariantValue (signed char val)
110 : fVal_{MakeSharedPtr<TIRep_<IntegerType_>> (val)}
111{
112}
113
114VariantValue::VariantValue (short int val)
115 : fVal_{MakeSharedPtr<TIRep_<IntegerType_>> (val)}
116{
117}
118
120 : fVal_{MakeSharedPtr<TIRep_<IntegerType_>> (val)}
121{
122}
123
124VariantValue::VariantValue (long int val)
125 : fVal_{MakeSharedPtr<TIRep_<IntegerType_>> (val)}
126{
127}
128
129VariantValue::VariantValue (long long int val)
130 : fVal_{MakeSharedPtr<TIRep_<IntegerType_>> (val)}
131{
132}
133
134VariantValue::VariantValue (unsigned char val)
135 : fVal_{MakeSharedPtr<TIRep_<UnsignedIntegerType_>> (val)}
136{
137}
138
139VariantValue::VariantValue (unsigned short int val)
140 : fVal_{MakeSharedPtr<TIRep_<UnsignedIntegerType_>> (val)}
141{
142}
143
144VariantValue::VariantValue (unsigned int val)
145 : fVal_{MakeSharedPtr<TIRep_<UnsignedIntegerType_>> (val)}
146{
147}
148
149VariantValue::VariantValue (unsigned long int val)
150 : fVal_{MakeSharedPtr<TIRep_<UnsignedIntegerType_>> (val)}
151{
152}
153
154VariantValue::VariantValue (unsigned long long int val)
155 : fVal_{MakeSharedPtr<TIRep_<UnsignedIntegerType_>> (val)}
156{
157}
158
160 : fVal_{MakeSharedPtr<TIRep_<FloatType_>> (val)}
161{
162}
163
165 : fVal_{MakeSharedPtr<TIRep_<FloatType_>> (val)}
166{
167}
168
169VariantValue::VariantValue (long double val)
170 : fVal_{MakeSharedPtr<TIRep_<FloatType_>> (val)}
171{
172}
173
175 : fVal_{MakeSharedPtr<TIRep_<Date>> (val)}
176{
177}
178
179VariantValue::VariantValue (const DateTime& val)
180 : fVal_{MakeSharedPtr<TIRep_<DateTime>> (val)}
181{
182}
184 : fVal_{MakeSharedPtr<TIRep_<String>> (val)}
185{
186}
187VariantValue::VariantValue (const map<wstring, VariantValue>& val)
189{
190}
191
193 : fVal_{MakeSharedPtr<TIRep_<Mapping<String, VariantValue>>> (val)}
194{
195}
196
198 : fVal_{MakeSharedPtr<TIRep_<Mapping<String, VariantValue>>> (move (val))}
199{
200}
201
203 : fVal_{MakeSharedPtr<TIRep_<Sequence<VariantValue>>> (move (val))}
204{
205}
206
208 : fVal_{MakeSharedPtr<TIRep_<Sequence<VariantValue>>> (val)}
209{
210}
211
213 : fVal_{MakeSharedPtr<TIRep_<Sequence<VariantValue>>> (Sequence<VariantValue> (val))}
214{
215}
216
217#if qStroika_HasComponent_boost
218namespace {
219 inline auto mk_ (const boost::json::value& val) -> VariantValue
220 {
221 using namespace boost;
222 switch (val.kind ()) {
223 case json::kind::null:
224 return VariantValue{};
225 break;
226 case json::kind::bool_:
227 return val.as_bool ();
228 break;
229 case json::kind::double_:
230 return val.as_double ();
231 break;
232 case json::kind::int64:
233 return val.as_int64 ();
234 break;
235 case json::kind::uint64:
236 return val.as_uint64 ();
237 break;
238 case json::kind::string: {
239 const json::string& bs = val.as_string (); // boost::json::string documents it represents a string as a series of UTF-8 characters
240 return String::FromUTF8 (span{bs});
241 } break;
242 case json::kind::array: {
243 const auto& a = val.as_array ();
244 std::vector<VariantValue> r; // performance tweak, add in STL, avoiding virtual calls for each add, and then move to Stroika Sequence
245 r.reserve (a.size ());
246 for (const boost::json::value& i : a) {
247 r.emplace_back (mk_ (i));
248 }
250 } break;
251 case json::kind::object: {
252 const auto& o = val.as_object ();
253 Containers::Concrete::Mapping_HashTable<String, VariantValue>::DEFAULT_HASHTABLE<> r; // performance tweak, add in STL, avoiding virtual calls for each add, and then move to Stroika mapping
254 for (const auto& i : o) {
255 r.insert ({String::FromUTF8 (span{i.key ()}), mk_ (i.value ())});
256 }
258 } break;
259 default:
261 return VariantValue{};
262 }
263 }
264}
265VariantValue::VariantValue (const boost::json::value& val)
266 : VariantValue{mk_ (val)}
267{
268}
269#endif
270
272{
273 if (fVal_ == nullptr) {
274 return true;
275 }
276 switch (fVal_->GetType ()) {
277 case Type::eBoolean:
278 case Type::eInteger:
279 case Type::eUnsignedInteger: {
280 return false; // cannot be empty
281 }
282 case Type::eBLOB: {
283 auto v = Debug::UncheckedDynamicCast<const TIRep_<Memory::BLOB>*> (fVal_.get ());
284 return v->fVal.empty ();
285 }
286 case Type::eFloat: {
287 auto v = Debug::UncheckedDynamicCast<const TIRep_<FloatType_>*> (fVal_.get ());
288 AssertNotNull (v);
289 return isnan (v->fVal);
290 }
291 case Type::eDate: {
292 return false; // cannot be empty (a change since Stroika v2.1d11 - used to be v->fVal.empty ())
293 }
294 case Type::eDateTime: {
295 return false; // cannot be empty (a change since Stroika v2.1d11 - used to be v->fVal.empty ())
296 }
297 case Type::eString: {
298 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
299 AssertNotNull (v);
300 return v->fVal.empty ();
301 }
302 case Type::eMap: {
303 auto v = Debug::UncheckedDynamicCast<const TIRep_<Mapping<String, VariantValue>>*> (fVal_.get ());
304 AssertNotNull (v);
305 return v->fVal.empty ();
306 }
307 case Type::eArray: {
308 auto v = Debug::UncheckedDynamicCast<const TIRep_<Sequence<VariantValue>>*> (fVal_.get ());
309 AssertNotNull (v);
310 return v->fVal.empty ();
311 }
312 default: {
313 return As<String> ().empty ();
314 }
315 }
316}
317
319{
320 return As<String> ();
321}
322
323bool VariantValue::AsBool_ () const
324{
325 if (fVal_ == nullptr) {
326 return false;
327 }
328 switch (fVal_->GetType ()) {
329 case Type::eBoolean: {
330 auto v = Debug::UncheckedDynamicCast<const TIRep_<bool>*> (fVal_.get ());
331 AssertNotNull (v);
332 return v->fVal;
333 }
334 case Type::eString: {
335 //return tmp != "false"; // no need to worry about case etc - cuz XML-Schema xs:boolean is case-sensitive
336 return As<String> () == "true"sv; // no need to worry about case etc - cuz XML-Schema xs:boolean is case-sensitive
337 }
338 case Type::eInteger: {
339 return As<IntegerType_> () != 0;
340 }
341 case Type::eUnsignedInteger: {
342 return As<UnsignedIntegerType_> () != 0;
343 }
344 default: {
345#if USE_NOISY_TRACE_IN_THIS_MODULE_
346 DbgTrace ("failed coerce-to-bool: type={}, value={}"_f, fVal_->GetType (), *this);
347#endif
348 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue to bool"sv});
349 }
350 }
351}
352
354{
355 if (GetType () == to) [[likely]] {
356 return true; // performance tweak
357 }
358 try {
360 (void)ConvertTo (to);
361 return true;
362 }
363 catch (...) {
364 return false;
365 }
366}
367
369{
370 if (GetType () == to) [[likely]] {
371 return *this; // performance tweak
372 }
373 static const DataExchange::BadFormatException kCannotCoerce2Null_{"Cannot coerce VariantValue to null"sv};
374 switch (to) {
375 case Type::eNull:
376 // Only null (caught above) can translate to null...
378 case Type::eBLOB:
379 return As<Memory::BLOB> ();
380 case Type::eBoolean:
381 return As<bool> ();
382 case Type::eInteger:
383 return As<int> ();
384 case Type::eUnsignedInteger:
385 return As<unsigned int> ();
386 case Type::eFloat:
387 return As<FloatType_> ();
388 case Type::eDate:
389 return As<Time::Date> ();
390 case Type::eDateTime:
391 return As<Time::DateTime> ();
392 case Type::eString:
393 return As<String> ();
394 case Type::eArray:
396 case Type::eMap:
398 }
399 static const DataExchange::BadFormatException kCannotCoerce2ThatType_{"Cannot coerce VariantValue to that type"sv};
401}
402
404{
406 switch (GetType ()) {
407 case Type::eNull:
408 return *this;
409 case Type::eBLOB:
410 return ConvertTo (Type::eString);
411 case Type::eBoolean:
412 return *this;
413 case Type::eInteger:
414 return ConvertTo (Type::eFloat);
415 case Type::eUnsignedInteger:
416 return ConvertTo (Type::eFloat);
417 case Type::eFloat: {
418 // tricky case - nans and infs must be converted to strings, because they cannot be emitted in JSON as 'numbers'
419 // and so when are read back, they are read back as strings.
420 FloatType_ f = As<FloatType_> ();
421 if (std::isnan (f) or std::isinf (f)) {
423 }
424 return *this;
425 }
426 case Type::eDate:
427 return ConvertTo (Type::eString);
428 case Type::eDateTime:
429 return ConvertTo (Type::eString);
430 case Type::eString:
431 return *this;
432 case Type::eArray:
433 // must recursively normalize all sub-elements
434 return VariantValue{
435 As<Sequence<VariantValue>> ().Map<Sequence<VariantValue>> ([] (const VariantValue& v) { return v.Normalize (); })};
436 case Type::eMap:
437 // must recursively normalize all sub-elements, but also produce a sorted-map
439 [] (const KVPT& kvp) { return KVPT{kvp.fKey, kvp.fValue.Normalize ()}; })};
440 default:
442 return nullptr;
443 }
444}
445
446Memory::BLOB VariantValue::AsBLOB_ () const
447{
448 if (fVal_ == nullptr) {
449 return Memory::BLOB{};
450 }
451 switch (fVal_->GetType ()) {
452 case Type::eBLOB: {
453 auto v = Debug::UncheckedDynamicCast<const TIRep_<Memory::BLOB>*> (fVal_.get ());
454 AssertNotNull (v);
455 return v->fVal;
456 }
457 default: {
458 return Cryptography::Encoding::Algorithm::Base64::Decode (As<String> ());
459 }
460 }
461}
462
463VariantValue::IntegerType_ VariantValue::AsInteger_ () const
464{
465 if (fVal_ == nullptr) {
466 return 0;
467 }
468 switch (fVal_->GetType ()) {
469 case Type::eBoolean: {
470 auto v = Debug::UncheckedDynamicCast<const TIRep_<bool>*> (fVal_.get ());
471 AssertNotNull (v);
472 return v->fVal;
473 }
474 case Type::eFloat: {
475 auto v = Debug::UncheckedDynamicCast<const TIRep_<FloatType_>*> (fVal_.get ());
476 AssertNotNull (v);
477 return Math::Round<VariantValue::IntegerType_> (v->fVal);
478 }
479 case Type::eInteger: {
480 auto v = Debug::UncheckedDynamicCast<const TIRep_<IntegerType_>*> (fVal_.get ());
481 AssertNotNull (v);
482 return v->fVal;
483 }
484 case Type::eUnsignedInteger: {
485 auto v = Debug::UncheckedDynamicCast<const TIRep_<UnsignedIntegerType_>*> (fVal_.get ());
486 AssertNotNull (v);
487 return v->fVal;
488 }
489 case Type::eString: {
490 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
491 AssertNotNull (v);
492 return Characters::String2Int<IntegerType_> (v->fVal);
493 }
494 default: {
495#if USE_NOISY_TRACE_IN_THIS_MODULE_
496 DbgTrace ("failed coerce-to-int: type={}, value={}"_f, fVal_->GetType (), *this);
497#endif
498 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue to integer"sv});
499 }
500 }
501}
502
503VariantValue::UnsignedIntegerType_ VariantValue::AsUnsignedInteger_ () const
504{
505 if (fVal_ == nullptr) {
506 return 0;
507 }
508 switch (fVal_->GetType ()) {
509 case Type::eFloat: {
510 auto v = Debug::UncheckedDynamicCast<const TIRep_<FloatType_>*> (fVal_.get ());
511 AssertNotNull (v);
512 return Math::Round<VariantValue::UnsignedIntegerType_> (v->fVal);
513 }
514 case Type::eInteger: {
515 auto v = Debug::UncheckedDynamicCast<const TIRep_<IntegerType_>*> (fVal_.get ());
516 AssertNotNull (v);
517 return v->fVal;
518 }
519 case Type::eUnsignedInteger: {
520 auto v = Debug::UncheckedDynamicCast<const TIRep_<UnsignedIntegerType_>*> (fVal_.get ());
521 AssertNotNull (v);
522 return v->fVal;
523 }
524 case Type::eString: {
525 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
526 AssertNotNull (v);
527 // not sure this is right for high order bit set for unsigned long long?
528 // --LGP 2013-08-25
529 return Characters::String2Int<UnsignedIntegerType_> (v->fVal);
530 }
531 default: {
532#if USE_NOISY_TRACE_IN_THIS_MODULE_
533 DbgTrace ("failed coerce-to-uint: type={}, value={}"_f, fVal_->GetType (), *this);
534#endif
535 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue to unsigned integer"sv});
536 }
537 }
538}
539
540VariantValue::FloatType_ VariantValue::AsFloatType_ () const
541{
542 if (fVal_ == nullptr) {
543 return Math::nan<FloatType_> ();
544 }
545 switch (fVal_->GetType ()) {
546 case Type::eInteger: {
547 auto v = Debug::UncheckedDynamicCast<const TIRep_<IntegerType_>*> (fVal_.get ());
548 AssertNotNull (v);
549 return static_cast<FloatType_> (v->fVal);
550 }
551 case Type::eUnsignedInteger: {
552 auto v = Debug::UncheckedDynamicCast<const TIRep_<UnsignedIntegerType_>*> (fVal_.get ());
553 AssertNotNull (v);
554 return static_cast<FloatType_> (v->fVal);
555 }
556 case Type::eFloat: {
557 auto v = Debug::UncheckedDynamicCast<const TIRep_<FloatType_>*> (fVal_.get ());
558 AssertNotNull (v);
559 return v->fVal;
560 }
561 case Type::eString: {
562 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
563 AssertNotNull (v);
564 // NB. this may return NAN if string not a well-formed number (including empty string case)
565 return Characters::FloatConversion::ToFloat<FloatType_> (v->fVal);
566 }
567 default: {
568#if USE_NOISY_TRACE_IN_THIS_MODULE_
569 DbgTrace ("failed coerce-to-float: type={}, value={}"_f, fVal_->GetType (), *this);
570#endif
571 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue to float"sv});
572 }
573 }
574}
575
576Date VariantValue::AsDate_ () const
577{
578 if (fVal_ == nullptr) {
579 Execution::Throw (Date::FormatException::kThe); // until Stroika v2.1d11 this returned Date{}, but no nonger support empty Date objects
580 }
581 switch (fVal_->GetType ()) {
582 case Type::eDate: {
583 auto v = Debug::UncheckedDynamicCast<const TIRep_<Date>*> (fVal_.get ());
584 AssertNotNull (v);
585 return v->fVal;
586 }
587 case Type::eDateTime: {
588 auto v = Debug::UncheckedDynamicCast<const TIRep_<DateTime>*> (fVal_.get ());
589 AssertNotNull (v);
590 return v->fVal.GetDate ();
591 }
592 case Type::eString: {
593 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
594 AssertNotNull (v);
595 return Date::Parse (v->fVal, Date::kISO8601Format);
596 }
597 default: {
598#if USE_NOISY_TRACE_IN_THIS_MODULE_
599 DbgTrace ("failed coerce-to-date: type={}, value={}"_f, fVal_->GetType (), *this);
600#endif
601 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue to date"sv});
602 }
603 }
604}
605
606DateTime VariantValue::AsDateTime_ () const
607{
608 if (fVal_ == nullptr) {
609 Execution::Throw (DateTime::FormatException::kThe); // until Stroika v2.1d11 this returned DateTime{}, but no longer support empty DateTime objects
610 }
611 switch (fVal_->GetType ()) {
612 case Type::eDate: {
613 auto v = Debug::UncheckedDynamicCast<const TIRep_<Date>*> (fVal_.get ());
614 AssertNotNull (v);
615 return DateTime{v->fVal};
616 }
617 case Type::eDateTime: {
618 auto v = Debug::UncheckedDynamicCast<const TIRep_<DateTime>*> (fVal_.get ());
619 AssertNotNull (v);
620 return v->fVal;
621 }
622 case Type::eString: {
623 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
624 AssertNotNull (v);
625 return DateTime::Parse (v->fVal, DateTime::kISO8601Format);
626 }
627 default: {
628#if USE_NOISY_TRACE_IN_THIS_MODULE_
629 DbgTrace ("failed coerce-to-datetime: type={}, value={}"_f, fVal_->GetType (), *this);
630#endif
631 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue to date-time"sv});
632 }
633 }
634}
635
636#if qStroika_HasComponent_boost
637boost::json::value VariantValue::AsBoostJSONValue_ () const
638{
639 using namespace boost;
640 if (fVal_ == nullptr) {
641 return json::value{nullptr};
642 }
643 switch (fVal_->GetType ()) {
644 case Type::eNull: {
645 return json::value{nullptr};
646 }
647 case Type::eBoolean: {
648 return As<bool> ();
649 }
650 case Type::eString: {
651 // I think boost uses / expects UTF8?
652 return As<String> ().AsUTF8<string> ().c_str ();
653 }
654 case Type::eInteger: {
655 return As<IntegerType_> ();
656 }
657 case Type::eUnsignedInteger: {
658 return As<UnsignedIntegerType_> ();
659 }
660 case Type::eFloat: {
661 return static_cast<double> (As<FloatType_> ());
662 }
663 case Type::eBLOB:
664 case Type::eDate:
665 case Type::eDateTime: {
666 // not a boost::json type, so convert to string
667 return As<String> ().AsASCII ().c_str ();
668 }
669 case Type::eArray: {
672 json::array result;
673 result.reserve (srcArray.size ());
674 for (const auto& i : srcArray) {
675 result.push_back (i.As<json::value> ());
676 }
677 return result;
678 }
679 case Type::eMap: {
682 json::object result;
683 for (const auto& i : srcMap) {
684 result.insert (json::key_value_pair{i.fKey.As<String> ().AsUTF8<string> ().c_str (), i.fValue.As<json::value> ()});
685 }
686 return result;
687 }
688 default:
690 return json::value{nullptr};
691 }
692}
693#endif
694
695String VariantValue::AsString_ () const
696{
697 if (fVal_ == nullptr) [[unlikely]] {
698 return String{};
699 }
700 switch (fVal_->GetType ()) {
701 case Type::eNull: {
703 return String{};
704 }
705 case Type::eBoolean: {
706 auto v = Debug::UncheckedDynamicCast<const TIRep_<bool>*> (fVal_.get ());
707 AssertNotNull (v);
708 return v->fVal ? "true"sv : "false"sv;
709 }
710 case Type::eBLOB: {
711 auto v = Debug::UncheckedDynamicCast<const TIRep_<Memory::BLOB>*> (fVal_.get ());
712 AssertNotNull (v);
713 // base64 on one line, as RFC 4648 (section 3.1) has it unless a format says otherwise - JSON, for one, does not
714 using namespace Cryptography::Encoding::Algorithm;
715 return String{Base64::Encode (v->fVal, Base64::Options{.fLineBreak = Base64::LineBreak::eNone_LB})};
716 }
717 case Type::eInteger: {
718 auto v = Debug::UncheckedDynamicCast<const TIRep_<IntegerType_>*> (fVal_.get ());
719 AssertNotNull (v);
720 return "{}"_f(v->fVal);
721 }
722 case Type::eUnsignedInteger: {
723 // Note - unsigned numbers converted to DECIMAL still as text representation
724 auto v = Debug::UncheckedDynamicCast<const TIRep_<UnsignedIntegerType_>*> (fVal_.get ());
725 AssertNotNull (v);
726 return "{}"_f(v->fVal);
727 }
728 case Type::eFloat: {
729 auto v = Debug::UncheckedDynamicCast<const TIRep_<FloatType_>*> (fVal_.get ());
730 AssertNotNull (v);
731 using namespace Characters;
732 return FloatConversion::ToString (v->fVal, FloatConversion::SignificantFigures::kFullPrecision);
733 }
734 case Type::eDate: {
735 auto v = Debug::UncheckedDynamicCast<const TIRep_<Date>*> (fVal_.get ());
736 AssertNotNull (v);
737 return v->fVal.Format (Date::kISO8601Format);
738 }
739 case Type::eDateTime: {
740 auto v = Debug::UncheckedDynamicCast<const TIRep_<DateTime>*> (fVal_.get ());
741 AssertNotNull (v);
742 return v->fVal.Format (DateTime::kISO8601Format);
743 }
744 case Type::eString: {
745 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
746 AssertNotNull (v);
747 return v->fVal;
748 }
749 case Type::eArray: {
750 auto v = Debug::UncheckedDynamicCast<const TIRep_<Sequence<VariantValue>>*> (fVal_.get ());
751 AssertNotNull (v);
753 tmp << "["sv;
754 for (auto i = v->fVal.begin (); i != v->fVal.end (); ++i) {
755 if (i != v->fVal.begin ()) {
756 tmp << ", "sv;
757 }
758 tmp << i->As<String> ();
759 }
760 tmp << "]"sv;
761 return tmp;
762 }
763 case Type::eMap: {
764 auto v = Debug::UncheckedDynamicCast<const TIRep_<Mapping<String, VariantValue>>*> (fVal_.get ());
765 AssertNotNull (v);
767 tmp << "{"sv;
768 for (auto i = v->fVal.begin (); i != v->fVal.end (); ++i) {
769 if (i != v->fVal.begin ()) {
770 tmp << ", ";
771 }
772 tmp << i->fKey << " -> "sv << i->fValue.As<String> ();
773 }
774 tmp << "}"sv;
775 return tmp;
776 }
777 default: {
778 AssertNotReached (); // That was all types enumerated, and all types convertable to string
779 return String{};
780 }
781 }
782}
783
784Mapping<String, VariantValue> VariantValue::AsMapping_ () const
785{
786 if (fVal_ == nullptr) [[unlikely]] {
788 }
789 switch (fVal_->GetType ()) {
790 case Type::eMap: {
791 auto v = Debug::UncheckedDynamicCast<const TIRep_<Mapping<String, VariantValue>>*> (fVal_.get ());
792 AssertNotNull (v);
793 return v->fVal;
794 }
795 default: {
796 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue of type {} to map"_f(fVal_->GetType ())});
797 }
798 }
799}
800
801Sequence<VariantValue> VariantValue::AsSequence_ () const
802{
803 if (fVal_ == nullptr) [[unlikely]] {
804 return Sequence<VariantValue>{};
805 }
806 switch (fVal_->GetType ()) {
807 case Type::eArray: {
808 auto v = Debug::UncheckedDynamicCast<const TIRep_<Sequence<VariantValue>>*> (fVal_.get ());
809 AssertNotNull (v);
810 return v->fVal;
811 }
812 default: {
813 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue of type {} to array"_f(fVal_->GetType ())});
814 }
815 }
816}
817
818/*
819 ********************************************************************************
820 ***************** DataExchange::VariantValue::EqualsComparer *******************
821 ********************************************************************************
822 */
823bool Stroika::Foundation::DataExchange::VariantValue::EqualsComparer::operator() (const VariantValue& lhs, const VariantValue& rhs) const
824{
825 VariantValue ln = lhs.Normalize ();
826 VariantValue rn = rhs.Normalize ();
827 VariantValue::Type lt = ln.GetType ();
828 VariantValue::Type rt = rn.GetType ();
829 if (lt != rt) {
830 return false;
831 }
832 switch (lt) {
833 case VariantValue::eNull:
834 return true;
835 case VariantValue::eBoolean:
836 return ln.As<bool> () == rn.As<bool> ();
837 case VariantValue::eFloat:
838 return Math::NearlyEquals (ln.As<FloatType_> (), rn.As<FloatType_> ());
839 case VariantValue::eString:
840 return ln.As<String> () == rn.As<String> ();
841 case VariantValue::eArray: {
842 // same iff all elts same (after normalizing sub-elts above)
843 return ln.As<Sequence<VariantValue>> () == rn.As<Sequence<VariantValue>> ();
844 }
845 case VariantValue::eMap: {
846 // same iff all elts same (importantly after normalizing which sorts)
848 }
849 case VariantValue::eBLOB:
850 case VariantValue::eInteger:
851 case VariantValue::eUnsignedInteger:
852 case VariantValue::eDate:
853 case VariantValue::eDateTime:
854 AssertNotReached (); // cuz normalized
855 default:
857 return false;
858 }
859}
860
861/*
862 ********************************************************************************
863 ************************* VariantValue::ThreeWayComparer ***********************
864 ********************************************************************************
865 */
866strong_ordering VariantValue::ThreeWayComparer::operator() (const VariantValue& lhs, const VariantValue& rhs) const
867{
870 VariantValue::Type lt = ln.GetType ();
871 VariantValue::Type rt = rn.GetType ();
872 if (lt != rt) {
873 return lt <=> rt; // no obvious sort order, so just use numeric type value
874 }
875 switch (lt) {
876 case VariantValue::eNull:
877 return strong_ordering::equal;
878 case VariantValue::eBoolean:
879 return ln.As<bool> () <=> rn.As<bool> ();
880 case VariantValue::eFloat: {
881 // explicit test so we can do NearlyEquals()
882 FloatType_ l = ln.As<FloatType_> ();
883 FloatType_ r = rn.As<FloatType_> ();
884 if (Math::NearlyEquals (l, r)) {
885 return strong_ordering::equal;
886 }
887 else if (l < r) {
888 return strong_ordering::less;
889 }
890 else {
891 return strong_ordering::greater;
892 }
893 }
894 case VariantValue::eString:
895 return ln.As<String> () <=> rn.As<String> ();
896 case VariantValue::eArray: {
897 // same iff all elts same (after normalizing sub-elts above)
899 }
900 case VariantValue::eMap: {
901 //https://github.com/SophistSolutions/Stroika/issues/1117 (STK-985)
902 //
903 // same iff all elts same (importantly after normalizing which sorts)
904 // @todo find way to make this compare work, but for now, just hack and re-create sorted mapping
905 // Maybe add 'virtual' rep method on Mapping - AsSorted - and then say
906 // As<SortedMapping<...>> on the mapping that comes back from VariantValue.
907 // Or add that same 'feature' just in VariantValue - with flag when creating with SortedMapping (maybe additional 'type')
908 // and logic privately in here so you dont need to worry outside.
909 if (false) {
910 //return ln.As<Mapping<String, VariantValue>> () <=> rn.As<Mapping<String, VariantValue>> ();
911 }
912 else {
915 }
916 }
917 case VariantValue::eBLOB:
918 case VariantValue::eInteger:
919 case VariantValue::eUnsignedInteger:
920 case VariantValue::eDate:
921 case VariantValue::eDateTime:
922 AssertNotReached (); // cuz normalized
923 default:
925 return strong_ordering::equal;
926 }
927}
#define AssertNotNull(p)
Definition Assertions.h:336
#define AssertNotReached()
Definition Assertions.h:358
auto MakeSharedPtr(ARGS_TYPE &&... args) -> shared_ptr< T >
same as make_shared, but if type T has block allocation, then use block allocation for the 'shared pa...
conditional_t< qStroika_Foundation_Memory_PreferBlockAllocation and andTrueCheck, BlockAllocationUseHelper< T >, Common::Empty > UseBlockAllocationIfAppropriate
Use this to enable block allocation for a particular class. Beware of subclassing.
#define DbgTrace
Definition Trace.h:327
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual tuple< const wchar_t *, wstring_view > c_str(Memory::StackBuffer< wchar_t > *possibleBackingStore) const
Definition String.inl:1053
Mapping_HashTable<KEY_TYPE, MAPPED_VALUE_TYPE, TRAITS> is a HashTable based concrete implementation o...
Sequence_stdvector<T> is an std::vector-based concrete implementation of the Sequence<T> container pa...
implement hash table support in a lightweight standard template library style. Use traits to describe...
Definition HashTable.h:132
nonvirtual void insert(const pair< KEY_TYPE, MAPPED_TYPE > &p)
somewhat stdlib-like names - that will do what is expected of someone from stdc++,...
A Mapping uniquely associates two elements: a key and a value (use Assocation to allow multiple value...
nonvirtual CONTAINER_OF_Key_T As() const
nonvirtual void insert(ArgByValueType< value_type > kvp)
Definition Mapping.inl:461
A generalization of a vector: a container whose elements are keyed by the natural numbers.
nonvirtual void As(CONTAINER_OF_ADDABLE *into) const
write this Sequence into an existing container
nonvirtual void push_back(ArgByValueType< value_type > item)
Definition Sequence.inl:782
Simple variant-value (case variant union) object, with (variant) basic types analogous to a value in ...
nonvirtual VariantValue ConvertTo(Type to) const
Return this VariantValue converted to the given type (as if by As<T> for the T appropriate to 'Type t...
VariantValue()=default
construct a VariantValue from most any 'basic type' you would expect to find in a weakly typed langua...
static constexpr string_view kISO8601Format
Y-M-D format - locale independent, and ISO-8601 date format standard.
Definition Date.h:465
static Date Parse(const String &rep, const locale &l=locale{})
Definition Date.inl:316
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:234
STRING_TYPE ToString(FLOAT_TYPE f, const ToStringOptions &options={})
void Throw(T &&e2Throw)
like the builtin C++ 'throw', but stamps in the current Activity context and does helpful,...
Definition Throw.inl:54