Stroika Library 3.0d23x
 
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_{val ? kTrueRep_ : kFalseRep_}
106{
107 // not inline so this is guaranteed true (to inline would need to be defs for TIRep_ stuff into .inl file)
108 // due to link time codegen/inlining, probably not needed
109}
110
112 : fVal_{MakeSharedPtr<TIRep_<Memory::BLOB>> (val)}
113{
114}
115
116VariantValue::VariantValue (signed char val)
117 : fVal_{MakeSharedPtr<TIRep_<IntegerType_>> (val)}
118{
119}
120
121VariantValue::VariantValue (short int val)
122 : fVal_{MakeSharedPtr<TIRep_<IntegerType_>> (val)}
123{
124}
125
127 : fVal_{MakeSharedPtr<TIRep_<IntegerType_>> (val)}
128{
129}
130
131VariantValue::VariantValue (long int val)
132 : fVal_{MakeSharedPtr<TIRep_<IntegerType_>> (val)}
133{
134}
135
136VariantValue::VariantValue (long long int val)
137 : fVal_{MakeSharedPtr<TIRep_<IntegerType_>> (val)}
138{
139}
140
141VariantValue::VariantValue (unsigned char val)
142 : fVal_{MakeSharedPtr<TIRep_<UnsignedIntegerType_>> (val)}
143{
144}
145
146VariantValue::VariantValue (unsigned short int val)
147 : fVal_{MakeSharedPtr<TIRep_<UnsignedIntegerType_>> (val)}
148{
149}
150
151VariantValue::VariantValue (unsigned int val)
152 : fVal_{MakeSharedPtr<TIRep_<UnsignedIntegerType_>> (val)}
153{
154}
155
156VariantValue::VariantValue (unsigned long int val)
157 : fVal_{MakeSharedPtr<TIRep_<UnsignedIntegerType_>> (val)}
158{
159}
160
161VariantValue::VariantValue (unsigned long long int val)
162 : fVal_{MakeSharedPtr<TIRep_<UnsignedIntegerType_>> (val)}
163{
164}
165
167 : fVal_{MakeSharedPtr<TIRep_<FloatType_>> (val)}
168{
169}
170
172 : fVal_{MakeSharedPtr<TIRep_<FloatType_>> (val)}
173{
174}
175
176VariantValue::VariantValue (long double val)
177 : fVal_{MakeSharedPtr<TIRep_<FloatType_>> (val)}
178{
179}
180
182 : fVal_{MakeSharedPtr<TIRep_<Date>> (val)}
183{
184}
185
186VariantValue::VariantValue (const DateTime& val)
187 : fVal_{MakeSharedPtr<TIRep_<DateTime>> (val)}
188{
189}
191 : fVal_{MakeSharedPtr<TIRep_<String>> (val)}
192{
193}
194VariantValue::VariantValue (const map<wstring, VariantValue>& val)
196{
197}
198
200 : fVal_{MakeSharedPtr<TIRep_<Mapping<String, VariantValue>>> (val)}
201{
202}
203
205 : fVal_{MakeSharedPtr<TIRep_<Mapping<String, VariantValue>>> (move (val))}
206{
207}
208
210 : fVal_{MakeSharedPtr<TIRep_<Sequence<VariantValue>>> (move (val))}
211{
212}
213
215 : fVal_{MakeSharedPtr<TIRep_<Sequence<VariantValue>>> (val)}
216{
217}
218
220 : fVal_{MakeSharedPtr<TIRep_<Sequence<VariantValue>>> (Sequence<VariantValue> (val))}
221{
222}
223
224#if qStroika_HasComponent_boost
225namespace {
226 inline auto mk_ (const boost::json::value& val) -> VariantValue
227 {
228 using namespace boost;
229 switch (val.kind ()) {
230 case json::kind::null:
231 return VariantValue{};
232 break;
233 case json::kind::bool_:
234 return val.as_bool ();
235 break;
236 case json::kind::double_:
237 return val.as_double ();
238 break;
239 case json::kind::int64:
240 return val.as_int64 ();
241 break;
242 case json::kind::uint64:
243 return val.as_uint64 ();
244 break;
245 case json::kind::string: {
246 const json::string& bs = val.as_string (); // boost::json::string documents it represents a string as a series of UTF-8 characters
247 return String::FromUTF8 (span{bs});
248 } break;
249 case json::kind::array: {
250 const auto& a = val.as_array ();
251 std::vector<VariantValue> r; // performance tweak, add in STL, avoiding virtual calls for each add, and then move to Stroika Sequence
252 r.reserve (a.size ());
253 for (const boost::json::value& i : a) {
254 r.emplace_back (mk_ (i));
255 }
257 } break;
258 case json::kind::object: {
259 const auto& o = val.as_object ();
260 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
261 for (const auto& i : o) {
262 r.insert ({String::FromUTF8 (span{i.key ()}), mk_ (i.value ())});
263 }
265 } break;
266 default:
268 return VariantValue{};
269 }
270 }
271}
272VariantValue::VariantValue (const boost::json::value& val)
273 : VariantValue{mk_ (val)}
274{
275}
276#endif
277
279{
280 if (fVal_ == nullptr) {
281 return true;
282 }
283 switch (fVal_->GetType ()) {
284 case Type::eBoolean:
285 case Type::eInteger:
286 case Type::eUnsignedInteger: {
287 return false; // cannot be empty
288 }
289 case Type::eBLOB: {
290 auto v = Debug::UncheckedDynamicCast<const TIRep_<Memory::BLOB>*> (fVal_.get ());
291 return v->fVal.empty ();
292 }
293 case Type::eFloat: {
294 auto v = Debug::UncheckedDynamicCast<const TIRep_<FloatType_>*> (fVal_.get ());
295 AssertNotNull (v);
296 return isnan (v->fVal);
297 }
298 case Type::eDate: {
299 return false; // cannot be empty (a change since Stroika v2.1d11 - used to be v->fVal.empty ())
300 }
301 case Type::eDateTime: {
302 return false; // cannot be empty (a change since Stroika v2.1d11 - used to be v->fVal.empty ())
303 }
304 case Type::eString: {
305 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
306 AssertNotNull (v);
307 return v->fVal.empty ();
308 }
309 case Type::eMap: {
310 auto v = Debug::UncheckedDynamicCast<const TIRep_<Mapping<String, VariantValue>>*> (fVal_.get ());
311 AssertNotNull (v);
312 return v->fVal.empty ();
313 }
314 case Type::eArray: {
315 auto v = Debug::UncheckedDynamicCast<const TIRep_<Sequence<VariantValue>>*> (fVal_.get ());
316 AssertNotNull (v);
317 return v->fVal.empty ();
318 }
319 default: {
320 return As<String> ().empty ();
321 }
322 }
323}
324
326{
327 return As<String> ();
328}
329
330bool VariantValue::AsBool_ () const
331{
332 if (fVal_ == nullptr) {
333 return false;
334 }
335 switch (fVal_->GetType ()) {
336 case Type::eBoolean: {
337 auto v = Debug::UncheckedDynamicCast<const TIRep_<bool>*> (fVal_.get ());
338 AssertNotNull (v);
339 return v->fVal;
340 }
341 case Type::eString: {
342 //return tmp != "false"; // no need to worry about case etc - cuz XML-Schema xs:boolean is case-sensitive
343 return As<String> () == "true"sv; // no need to worry about case etc - cuz XML-Schema xs:boolean is case-sensitive
344 }
345 case Type::eInteger: {
346 return As<IntegerType_> () != 0;
347 }
348 case Type::eUnsignedInteger: {
349 return As<UnsignedIntegerType_> () != 0;
350 }
351 default: {
352#if USE_NOISY_TRACE_IN_THIS_MODULE_
353 DbgTrace ("failed coerce-to-bool: type={}, value={}"_f, fVal_->GetType (), *this);
354#endif
355 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue to bool"sv});
356 }
357 }
358}
359
361{
362 if (GetType () == to) [[likely]] {
363 return true; // performance tweak
364 }
365 try {
366 Debug::TraceContextSuppressor suppressTraceInThisBlock;
367 (void)ConvertTo (to);
368 return true;
369 }
370 catch (...) {
371 return false;
372 }
373}
374
376{
377 if (GetType () == to) [[likely]] {
378 return *this; // performance tweak
379 }
380 static const DataExchange::BadFormatException kCannotCoerce2Null_{"Cannot coerce VariantValue to null"sv};
381 switch (to) {
382 case Type::eNull:
383 // Only null (caught above) can translate to null...
384 Execution::Throw (kCannotCoerce2Null_);
385 case Type::eBLOB:
386 return As<Memory::BLOB> ();
387 case Type::eBoolean:
388 return As<bool> ();
389 case Type::eInteger:
390 return As<int> ();
391 case Type::eUnsignedInteger:
392 return As<unsigned int> ();
393 case Type::eFloat:
394 return As<FloatType_> ();
395 case Type::eDate:
396 return As<Time::Date> ();
397 case Type::eDateTime:
398 return As<Time::DateTime> ();
399 case Type::eString:
400 return As<String> ();
401 case Type::eArray:
402 return VariantValue{As<Sequence<VariantValue>> ()};
403 case Type::eMap:
404 return VariantValue{As<Mapping<String, VariantValue>> ()};
405 }
406 static const DataExchange::BadFormatException kCannotCoerce2ThatType_{"Cannot coerce VariantValue to that type"sv};
407 Execution::Throw (kCannotCoerce2ThatType_);
408}
409
411{
413 switch (GetType ()) {
414 case Type::eNull:
415 return *this;
416 case Type::eBLOB:
417 return ConvertTo (Type::eString);
418 case Type::eBoolean:
419 return *this;
420 case Type::eInteger:
421 return ConvertTo (Type::eFloat);
422 case Type::eUnsignedInteger:
423 return ConvertTo (Type::eFloat);
424 case Type::eFloat: {
425 // tricky case - nans and infs must be converted to strings, because they cannot be emitted in JSON as 'numbers'
426 // and so when are read back, they are read back as strings.
427 FloatType_ f = As<FloatType_> ();
428 if (std::isnan (f) or std::isinf (f)) {
430 }
431 return *this;
432 }
433 case Type::eDate:
434 return ConvertTo (Type::eString);
435 case Type::eDateTime:
436 return ConvertTo (Type::eString);
437 case Type::eString:
438 return *this;
439 case Type::eArray:
440 // must recursively normalize all sub-elements
441 return VariantValue{
442 As<Sequence<VariantValue>> ().Map<Sequence<VariantValue>> ([] (const VariantValue& v) { return v.Normalize (); })};
443 case Type::eMap:
444 // must recursively normalize all sub-elements, but also produce a sorted-map
445 return VariantValue{As<Mapping<String, VariantValue>> ().Map<Containers::SortedMapping<String, VariantValue>> (
446 [] (const KVPT& kvp) { return KVPT{kvp.fKey, kvp.fValue.Normalize ()}; })};
447 default:
449 return nullptr;
450 }
451}
452
453Memory::BLOB VariantValue::AsBLOB_ () const
454{
455 if (fVal_ == nullptr) {
456 return Memory::BLOB{};
457 }
458 switch (fVal_->GetType ()) {
459 case Type::eBLOB: {
460 auto v = Debug::UncheckedDynamicCast<const TIRep_<Memory::BLOB>*> (fVal_.get ());
461 AssertNotNull (v);
462 return v->fVal;
463 }
464 default: {
465 return Cryptography::Encoding::Algorithm::Base64::Decode (As<String> ());
466 }
467 }
468}
469
470VariantValue::IntegerType_ VariantValue::AsInteger_ () const
471{
472 if (fVal_ == nullptr) {
473 return 0;
474 }
475 switch (fVal_->GetType ()) {
476 case Type::eBoolean: {
477 auto v = Debug::UncheckedDynamicCast<const TIRep_<bool>*> (fVal_.get ());
478 AssertNotNull (v);
479 return v->fVal;
480 }
481 case Type::eFloat: {
482 auto v = Debug::UncheckedDynamicCast<const TIRep_<FloatType_>*> (fVal_.get ());
483 AssertNotNull (v);
484 return Math::Round<VariantValue::IntegerType_> (v->fVal);
485 }
486 case Type::eInteger: {
487 auto v = Debug::UncheckedDynamicCast<const TIRep_<IntegerType_>*> (fVal_.get ());
488 AssertNotNull (v);
489 return v->fVal;
490 }
491 case Type::eUnsignedInteger: {
492 auto v = Debug::UncheckedDynamicCast<const TIRep_<UnsignedIntegerType_>*> (fVal_.get ());
493 AssertNotNull (v);
494 return v->fVal;
495 }
496 case Type::eString: {
497 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
498 AssertNotNull (v);
499 return Characters::String2Int<IntegerType_> (v->fVal);
500 }
501 default: {
502#if USE_NOISY_TRACE_IN_THIS_MODULE_
503 DbgTrace ("failed coerce-to-int: type={}, value={}"_f, fVal_->GetType (), *this);
504#endif
505 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue to integer"sv});
506 }
507 }
508}
509
510VariantValue::UnsignedIntegerType_ VariantValue::AsUnsignedInteger_ () const
511{
512 if (fVal_ == nullptr) {
513 return 0;
514 }
515 switch (fVal_->GetType ()) {
516 case Type::eFloat: {
517 auto v = Debug::UncheckedDynamicCast<const TIRep_<FloatType_>*> (fVal_.get ());
518 AssertNotNull (v);
519 return Math::Round<VariantValue::UnsignedIntegerType_> (v->fVal);
520 }
521 case Type::eInteger: {
522 auto v = Debug::UncheckedDynamicCast<const TIRep_<IntegerType_>*> (fVal_.get ());
523 AssertNotNull (v);
524 return v->fVal;
525 }
526 case Type::eUnsignedInteger: {
527 auto v = Debug::UncheckedDynamicCast<const TIRep_<UnsignedIntegerType_>*> (fVal_.get ());
528 AssertNotNull (v);
529 return v->fVal;
530 }
531 case Type::eString: {
532 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
533 AssertNotNull (v);
534 // not sure this is right for high order bit set for unsigned long long?
535 // --LGP 2013-08-25
536 return Characters::String2Int<UnsignedIntegerType_> (v->fVal);
537 }
538 default: {
539#if USE_NOISY_TRACE_IN_THIS_MODULE_
540 DbgTrace ("failed coerce-to-uint: type={}, value={}"_f, fVal_->GetType (), *this);
541#endif
542 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue to unsigned integer"sv});
543 }
544 }
545}
546
547VariantValue::FloatType_ VariantValue::AsFloatType_ () const
548{
549 if (fVal_ == nullptr) {
550 return Math::nan<FloatType_> ();
551 }
552 switch (fVal_->GetType ()) {
553 case Type::eInteger: {
554 auto v = Debug::UncheckedDynamicCast<const TIRep_<IntegerType_>*> (fVal_.get ());
555 AssertNotNull (v);
556 return static_cast<FloatType_> (v->fVal);
557 }
558 case Type::eUnsignedInteger: {
559 auto v = Debug::UncheckedDynamicCast<const TIRep_<UnsignedIntegerType_>*> (fVal_.get ());
560 AssertNotNull (v);
561 return static_cast<FloatType_> (v->fVal);
562 }
563 case Type::eFloat: {
564 auto v = Debug::UncheckedDynamicCast<const TIRep_<FloatType_>*> (fVal_.get ());
565 AssertNotNull (v);
566 return v->fVal;
567 }
568 case Type::eString: {
569 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
570 AssertNotNull (v);
571 // NB. this may return NAN if string not a well-formed number (including empty string case)
572 return Characters::FloatConversion::ToFloat<FloatType_> (v->fVal);
573 }
574 default: {
575#if USE_NOISY_TRACE_IN_THIS_MODULE_
576 DbgTrace ("failed coerce-to-float: type={}, value={}"_f, fVal_->GetType (), *this);
577#endif
578 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue to float"sv});
579 }
580 }
581}
582
583Date VariantValue::AsDate_ () const
584{
585 if (fVal_ == nullptr) {
586 Execution::Throw (Date::FormatException::kThe); // until Stroika v2.1d11 this returned Date{}, but no nonger support empty Date objects
587 }
588 switch (fVal_->GetType ()) {
589 case Type::eDate: {
590 auto v = Debug::UncheckedDynamicCast<const TIRep_<Date>*> (fVal_.get ());
591 AssertNotNull (v);
592 return v->fVal;
593 }
594 case Type::eDateTime: {
595 auto v = Debug::UncheckedDynamicCast<const TIRep_<DateTime>*> (fVal_.get ());
596 AssertNotNull (v);
597 return v->fVal.GetDate ();
598 }
599 case Type::eString: {
600 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
601 AssertNotNull (v);
602 return Date::Parse (v->fVal, Date::kISO8601Format);
603 }
604 default: {
605#if USE_NOISY_TRACE_IN_THIS_MODULE_
606 DbgTrace ("failed coerce-to-date: type={}, value={}"_f, fVal_->GetType (), *this);
607#endif
608 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue to date"sv});
609 }
610 }
611}
612
613DateTime VariantValue::AsDateTime_ () const
614{
615 if (fVal_ == nullptr) {
616 Execution::Throw (DateTime::FormatException::kThe); // until Stroika v2.1d11 this returned DateTime{}, but no longer support empty DateTime objects
617 }
618 switch (fVal_->GetType ()) {
619 case Type::eDate: {
620 auto v = Debug::UncheckedDynamicCast<const TIRep_<Date>*> (fVal_.get ());
621 AssertNotNull (v);
622 return DateTime{v->fVal};
623 }
624 case Type::eDateTime: {
625 auto v = Debug::UncheckedDynamicCast<const TIRep_<DateTime>*> (fVal_.get ());
626 AssertNotNull (v);
627 return v->fVal;
628 }
629 case Type::eString: {
630 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
631 AssertNotNull (v);
632 return DateTime::Parse (v->fVal, DateTime::kISO8601Format);
633 }
634 default: {
635#if USE_NOISY_TRACE_IN_THIS_MODULE_
636 DbgTrace ("failed coerce-to-datetime: type={}, value={}"_f, fVal_->GetType (), *this);
637#endif
638 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue to date-time"sv});
639 }
640 }
641}
642
643#if qStroika_HasComponent_boost
644boost::json::value VariantValue::AsBoostJSONValue_ () const
645{
646 using namespace boost;
647 if (fVal_ == nullptr) {
648 return json::value{nullptr};
649 }
650 switch (fVal_->GetType ()) {
651 case Type::eNull: {
652 return json::value{nullptr};
653 }
654 case Type::eBoolean: {
655 return As<bool> ();
656 }
657 case Type::eString: {
658 // I think boost uses / expects UTF8?
659 return As<String> ().AsUTF8<string> ().c_str ();
660 }
661 case Type::eInteger: {
662 return As<IntegerType_> ();
663 }
664 case Type::eUnsignedInteger: {
665 return As<UnsignedIntegerType_> ();
666 }
667 case Type::eFloat: {
668 return static_cast<double> (As<FloatType_> ());
669 }
670 case Type::eBLOB:
671 case Type::eDate:
672 case Type::eDateTime: {
673 // not a boost::json type, so convert to string
674 return As<String> ().AsASCII ().c_str ();
675 }
676 case Type::eArray: {
678 Sequence<VariantValue> srcArray = As<Sequence<VariantValue>> ();
679 json::array result;
680 result.reserve (srcArray.size ());
681 for (const auto& i : srcArray) {
682 result.push_back (i.As<json::value> ());
683 }
684 return result;
685 }
686 case Type::eMap: {
688 Mapping<String, VariantValue> srcMap = As<Mapping<String, VariantValue>> ();
689 json::object result;
690 for (const auto& i : srcMap) {
691 result.insert (json::key_value_pair{i.fKey.As<String> ().AsUTF8<string> ().c_str (), i.fValue.As<json::value> ()});
692 }
693 return result;
694 }
695 default:
697 return json::value{nullptr};
698 }
699}
700#endif
701
702String VariantValue::AsString_ () const
703{
704 if (fVal_ == nullptr) [[unlikely]] {
705 return String{};
706 }
707 switch (fVal_->GetType ()) {
708 case Type::eNull: {
710 return String{};
711 }
712 case Type::eBoolean: {
713 auto v = Debug::UncheckedDynamicCast<const TIRep_<bool>*> (fVal_.get ());
714 AssertNotNull (v);
715 return v->fVal ? "true"sv : "false"sv;
716 }
717 case Type::eBLOB: {
718 auto v = Debug::UncheckedDynamicCast<const TIRep_<Memory::BLOB>*> (fVal_.get ());
719 AssertNotNull (v);
720 return String{Cryptography::Encoding::Algorithm::Base64::Encode (v->fVal)};
721 }
722 case Type::eInteger: {
723 auto v = Debug::UncheckedDynamicCast<const TIRep_<IntegerType_>*> (fVal_.get ());
724 AssertNotNull (v);
725 return "{}"_f(v->fVal);
726 }
727 case Type::eUnsignedInteger: {
728 // Note - unsigned numbers converted to DECIMAL still as text representation
729 auto v = Debug::UncheckedDynamicCast<const TIRep_<UnsignedIntegerType_>*> (fVal_.get ());
730 AssertNotNull (v);
731 return "{}"_f(v->fVal);
732 }
733 case Type::eFloat: {
734 auto v = Debug::UncheckedDynamicCast<const TIRep_<FloatType_>*> (fVal_.get ());
735 AssertNotNull (v);
736 using namespace Characters;
737 return FloatConversion::ToString (v->fVal, FloatConversion::Precision::kFull);
738 }
739 case Type::eDate: {
740 auto v = Debug::UncheckedDynamicCast<const TIRep_<Date>*> (fVal_.get ());
741 AssertNotNull (v);
742 return v->fVal.Format (Date::kISO8601Format);
743 }
744 case Type::eDateTime: {
745 auto v = Debug::UncheckedDynamicCast<const TIRep_<DateTime>*> (fVal_.get ());
746 AssertNotNull (v);
747 return v->fVal.Format (DateTime::kISO8601Format);
748 }
749 case Type::eString: {
750 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
751 AssertNotNull (v);
752 return v->fVal;
753 }
754 case Type::eArray: {
755 auto v = Debug::UncheckedDynamicCast<const TIRep_<Sequence<VariantValue>>*> (fVal_.get ());
756 AssertNotNull (v);
758 tmp << "["sv;
759 for (auto i = v->fVal.begin (); i != v->fVal.end (); ++i) {
760 if (i != v->fVal.begin ()) {
761 tmp << ", "sv;
762 }
763 tmp << i->As<String> ();
764 }
765 tmp << "]"sv;
766 return tmp;
767 }
768 case Type::eMap: {
769 auto v = Debug::UncheckedDynamicCast<const TIRep_<Mapping<String, VariantValue>>*> (fVal_.get ());
770 AssertNotNull (v);
772 tmp << "{"sv;
773 for (auto i = v->fVal.begin (); i != v->fVal.end (); ++i) {
774 if (i != v->fVal.begin ()) {
775 tmp << ", ";
776 }
777 tmp << i->fKey << " -> "sv << i->fValue.As<String> ();
778 }
779 tmp << "}"sv;
780 return tmp;
781 }
782 default: {
783 AssertNotReached (); // That was all types enumerated, and all types convertable to string
784 return String{};
785 }
786 }
787}
788
789Mapping<String, VariantValue> VariantValue::AsMapping_ () const
790{
791 if (fVal_ == nullptr) [[unlikely]] {
793 }
794 switch (fVal_->GetType ()) {
795 case Type::eMap: {
796 auto v = Debug::UncheckedDynamicCast<const TIRep_<Mapping<String, VariantValue>>*> (fVal_.get ());
797 AssertNotNull (v);
798 return v->fVal;
799 }
800 default: {
801 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue of type {} to map"_f(fVal_->GetType ())});
802 }
803 }
804}
805
806Sequence<VariantValue> VariantValue::AsSequence_ () const
807{
808 if (fVal_ == nullptr) [[unlikely]] {
809 return Sequence<VariantValue>{};
810 }
811 switch (fVal_->GetType ()) {
812 case Type::eArray: {
813 auto v = Debug::UncheckedDynamicCast<const TIRep_<Sequence<VariantValue>>*> (fVal_.get ());
814 AssertNotNull (v);
815 return v->fVal;
816 }
817 default: {
818 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue of type {} to array"_f(fVal_->GetType ())});
819 }
820 }
821}
822
823/*
824 ********************************************************************************
825 ***************** DataExchange::VariantValue::EqualsComparer *******************
826 ********************************************************************************
827 */
828bool Stroika::Foundation::DataExchange::VariantValue::EqualsComparer::operator() (const VariantValue& lhs, const VariantValue& rhs) const
829{
830 VariantValue ln = lhs.Normalize ();
831 VariantValue rn = rhs.Normalize ();
832 VariantValue::Type lt = ln.GetType ();
833 VariantValue::Type rt = rn.GetType ();
834 if (lt != rt) {
835 return false;
836 }
837 switch (lt) {
838 case VariantValue::eNull:
839 return true;
840 case VariantValue::eBoolean:
841 return ln.As<bool> () == rn.As<bool> ();
842 case VariantValue::eFloat:
843 return Math::NearlyEquals (ln.As<FloatType_> (), rn.As<FloatType_> ());
844 case VariantValue::eString:
845 return ln.As<String> () == rn.As<String> ();
846 case VariantValue::eArray: {
847 // same iff all elts same (after normalizing sub-elts above)
848 return ln.As<Sequence<VariantValue>> () == rn.As<Sequence<VariantValue>> ();
849 }
850 case VariantValue::eMap: {
851 // same iff all elts same (importantly after normalizing which sorts)
853 }
854 case VariantValue::eBLOB:
855 case VariantValue::eInteger:
856 case VariantValue::eUnsignedInteger:
857 case VariantValue::eDate:
858 case VariantValue::eDateTime:
859 AssertNotReached (); // cuz normalized
860 default:
862 return false;
863 }
864}
865
866/*
867 ********************************************************************************
868 ************************* VariantValue::ThreeWayComparer ***********************
869 ********************************************************************************
870 */
871strong_ordering VariantValue::ThreeWayComparer::operator() (const VariantValue& lhs, const VariantValue& rhs) const
872{
873 VariantValue ln = lhs.Normalize ();
874 VariantValue rn = rhs.Normalize ();
875 VariantValue::Type lt = ln.GetType ();
876 VariantValue::Type rt = rn.GetType ();
877 if (lt != rt) {
878 return lt <=> rt; // no obvious sort order, so just use numeric type value
879 }
880 switch (lt) {
881 case VariantValue::eNull:
882 return strong_ordering::equal;
883 case VariantValue::eBoolean:
884 return ln.As<bool> () <=> rn.As<bool> ();
885 case VariantValue::eFloat: {
886 // explicit test so we can do NearlyEquals()
887 FloatType_ l = ln.As<FloatType_> ();
888 FloatType_ r = rn.As<FloatType_> ();
889 if (Math::NearlyEquals (l, r)) {
890 return strong_ordering::equal;
891 }
892 else if (l < r) {
893 return strong_ordering::less;
894 }
895 else {
896 return strong_ordering::greater;
897 }
898 }
899 case VariantValue::eString:
900 return ln.As<String> () <=> rn.As<String> ();
901 case VariantValue::eArray: {
902 // same iff all elts same (after normalizing sub-elts above)
903 return ln.As<Sequence<VariantValue>> () <=> rn.As<Sequence<VariantValue>> ();
904 }
905 case VariantValue::eMap: {
906 //http://stroika-bugs.sophists.com/browse/STK-985
907 //
908 // same iff all elts same (importantly after normalizing which sorts)
909 // @todo find way to make this compare work, but for now, just hack and re-create sorted mapping
910 // Maybe add 'virtual' rep method on Mapping - AsSorted - and then say
911 // As<SortedMapping<...>> on the mapping that comes back from VariantValue.
912 // Or add that same 'feature' just in VariantValue - with flag when creating with SortedMapping (maybe additional 'type')
913 // and logic privately in here so you dont need to worry outside.
914 if (false) {
915 //return ln.As<Mapping<String, VariantValue>> () <=> rn.As<Mapping<String, VariantValue>> ();
916 }
917 else {
919 return SMT{ln.As<Mapping<String, VariantValue>> ()} <=> SMT{rn.As<Mapping<String, VariantValue>> ()};
920 }
921 }
922 case VariantValue::eBLOB:
923 case VariantValue::eInteger:
924 case VariantValue::eUnsignedInteger:
925 case VariantValue::eDate:
926 case VariantValue::eDateTime:
927 AssertNotReached (); // cuz normalized
928 default:
930 return strong_ordering::equal;
931 }
932}
#define AssertNotNull(p)
Definition Assertions.h:333
#define AssertNotReached()
Definition Assertions.h:355
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:309
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:1055
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++,...
nonvirtual void insert(ArgByValueType< value_type > kvp)
Definition Mapping.inl:456
A generalization of a vector: a container whose elements are keyed by the natural numbers.
nonvirtual void push_back(ArgByValueType< value_type > item)
Definition Sequence.inl:436
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:467
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:237
nonvirtual size_t size() const
Returns the number of items contained.
Definition Iterable.inl:303
STRING_TYPE ToString(FLOAT_TYPE f, const ToStringOptions &options={})
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43