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