Stroika Library 3.0d18
 
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/Concrete/Mapping_HashTable.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 Sequence
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_HashTable<String, VariantValue>::DEFAULT_HASHTABLE<> r; // performance tweak, add in STL, avoiding virtual calls for each add, and then move to Stroika mapping
258 for (const auto& i : o) {
259 r.insert ({String::FromUTF8 (span{i.key ()}), mk_ (i.value ())});
260 }
262 } break;
263 default:
265 return VariantValue{};
266 }
267 }
268}
269VariantValue::VariantValue (const boost::json::value& val)
270 : VariantValue{mk_ (val)}
271{
272}
273#endif
274
276{
277 if (fVal_ == nullptr) {
278 return true;
279 }
280 switch (fVal_->GetType ()) {
281 case Type::eBoolean:
282 case Type::eInteger:
283 case Type::eUnsignedInteger: {
284 return false; // cannot be empty
285 }
286 case Type::eBLOB: {
287 auto v = Debug::UncheckedDynamicCast<const TIRep_<Memory::BLOB>*> (fVal_.get ());
288 return v->fVal.empty ();
289 }
290 case Type::eFloat: {
291 auto v = Debug::UncheckedDynamicCast<const TIRep_<FloatType_>*> (fVal_.get ());
292 AssertNotNull (v);
293 return isnan (v->fVal);
294 }
295 case Type::eDate: {
296 return false; // cannot be empty (a change since Stroika v2.1d11 - used to be v->fVal.empty ())
297 }
298 case Type::eDateTime: {
299 return false; // cannot be empty (a change since Stroika v2.1d11 - used to be v->fVal.empty ())
300 }
301 case Type::eString: {
302 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
303 AssertNotNull (v);
304 return v->fVal.empty ();
305 }
306 case Type::eMap: {
307 auto v = Debug::UncheckedDynamicCast<const TIRep_<Mapping<String, VariantValue>>*> (fVal_.get ());
308 AssertNotNull (v);
309 return v->fVal.empty ();
310 }
311 case Type::eArray: {
312 auto v = Debug::UncheckedDynamicCast<const TIRep_<Sequence<VariantValue>>*> (fVal_.get ());
313 AssertNotNull (v);
314 return v->fVal.empty ();
315 }
316 default: {
317 return As<String> ().empty ();
318 }
319 }
320}
321
323{
324 return As<String> ();
325}
326
327bool VariantValue::AsBool_ () const
328{
329 if (fVal_ == nullptr) {
330 return false;
331 }
332 switch (fVal_->GetType ()) {
333 case Type::eBoolean: {
334 auto v = Debug::UncheckedDynamicCast<const TIRep_<bool>*> (fVal_.get ());
335 AssertNotNull (v);
336 return v->fVal;
337 }
338 case Type::eString: {
339 //return tmp != "false"; // no need to worry about case etc - cuz XML-Schema xs:boolean is case-sensitive
340 return As<String> () == "true"sv; // no need to worry about case etc - cuz XML-Schema xs:boolean is case-sensitive
341 }
342 case Type::eInteger: {
343 return As<IntegerType_> () != 0;
344 }
345 case Type::eUnsignedInteger: {
346 return As<UnsignedIntegerType_> () != 0;
347 }
348 default: {
349#if USE_NOISY_TRACE_IN_THIS_MODULE_
350 DbgTrace ("failed coerce-to-bool: type={}, value={}"_f, fVal_->GetType (), *this);
351#endif
352 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue to bool"sv});
353 }
354 }
355}
356
358{
359 if (GetType () == to) [[likely]] {
360 return true; // performance tweak
361 }
362 try {
363 Debug::TraceContextSuppressor suppressTraceInThisBlock;
364 (void)ConvertTo (to);
365 return true;
366 }
367 catch (...) {
368 return false;
369 }
370}
371
373{
374 if (GetType () == to) [[likely]] {
375 return *this; // performance tweak
376 }
377 static const DataExchange::BadFormatException kCannotCoerce2Null_{"Cannot coerce VariantValue to null"sv};
378 switch (to) {
379 case Type::eNull:
380 // Only null (caught above) can translate to null...
381 Execution::Throw (kCannotCoerce2Null_);
382 case Type::eBLOB:
383 return As<Memory::BLOB> ();
384 case Type::eBoolean:
385 return As<bool> ();
386 case Type::eInteger:
387 return As<int> ();
388 case Type::eUnsignedInteger:
389 return As<unsigned int> ();
390 case Type::eFloat:
391 return As<FloatType_> ();
392 case Type::eDate:
393 return As<Time::Date> ();
394 case Type::eDateTime:
395 return As<Time::DateTime> ();
396 case Type::eString:
397 return As<String> ();
398 case Type::eArray:
399 return VariantValue{As<Sequence<VariantValue>> ()};
400 case Type::eMap:
401 return VariantValue{As<Mapping<String, VariantValue>> ()};
402 }
403 static const DataExchange::BadFormatException kCannotCoerce2ThatType_{"Cannot coerce VariantValue to that type"sv};
404 Execution::Throw (kCannotCoerce2ThatType_);
405}
406
408{
410 switch (GetType ()) {
411 case Type::eNull:
412 return *this;
413 case Type::eBLOB:
414 return ConvertTo (Type::eString);
415 case Type::eBoolean:
416 return *this;
417 case Type::eInteger:
418 return ConvertTo (Type::eFloat);
419 case Type::eUnsignedInteger:
420 return ConvertTo (Type::eFloat);
421 case Type::eFloat: {
422 // tricky case - nans and infs must be converted to strings, because they cannot be emitted in JSON as 'numbers'
423 // and so when are read back, they are read back as strings.
424 FloatType_ f = As<FloatType_> ();
425 if (std::isnan (f) or std::isinf (f)) {
427 }
428 return *this;
429 }
430 case Type::eDate:
431 return ConvertTo (Type::eString);
432 case Type::eDateTime:
433 return ConvertTo (Type::eString);
434 case Type::eString:
435 return *this;
436 case Type::eArray:
437 // must recursively normalize all sub-elements
438 return VariantValue{
439 As<Sequence<VariantValue>> ().Map<Sequence<VariantValue>> ([] (const VariantValue& v) { return v.Normalize (); })};
440 case Type::eMap:
441 // must recursively normalize all sub-elements, but also produce a sorted-map
442 return VariantValue{As<Mapping<String, VariantValue>> ().Map<Containers::SortedMapping<String, VariantValue>> (
443 [] (const KVPT& kvp) { return KVPT{kvp.fKey, kvp.fValue.Normalize ()}; })};
444 default:
446 return nullptr;
447 }
448}
449
450Memory::BLOB VariantValue::AsBLOB_ () const
451{
452 if (fVal_ == nullptr) {
453 return Memory::BLOB{};
454 }
455 switch (fVal_->GetType ()) {
456 case Type::eBLOB: {
457 auto v = Debug::UncheckedDynamicCast<const TIRep_<Memory::BLOB>*> (fVal_.get ());
458 AssertNotNull (v);
459 return v->fVal;
460 }
461 default: {
462 return Cryptography::Encoding::Algorithm::Base64::Decode (As<String> ());
463 }
464 }
465}
466
467VariantValue::IntegerType_ VariantValue::AsInteger_ () const
468{
469 if (fVal_ == nullptr) {
470 return 0;
471 }
472 switch (fVal_->GetType ()) {
473 case Type::eBoolean: {
474 auto v = Debug::UncheckedDynamicCast<const TIRep_<bool>*> (fVal_.get ());
475 AssertNotNull (v);
476 return v->fVal;
477 }
478 case Type::eFloat: {
479 auto v = Debug::UncheckedDynamicCast<const TIRep_<FloatType_>*> (fVal_.get ());
480 AssertNotNull (v);
481 return Math::Round<VariantValue::IntegerType_> (v->fVal);
482 }
483 case Type::eInteger: {
484 auto v = Debug::UncheckedDynamicCast<const TIRep_<IntegerType_>*> (fVal_.get ());
485 AssertNotNull (v);
486 return v->fVal;
487 }
488 case Type::eUnsignedInteger: {
489 auto v = Debug::UncheckedDynamicCast<const TIRep_<UnsignedIntegerType_>*> (fVal_.get ());
490 AssertNotNull (v);
491 return v->fVal;
492 }
493 case Type::eString: {
494 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
495 AssertNotNull (v);
496 return Characters::String2Int<IntegerType_> (v->fVal);
497 }
498 default: {
499#if USE_NOISY_TRACE_IN_THIS_MODULE_
500 DbgTrace ("failed coerce-to-int: type={}, value={}"_f, fVal_->GetType (), *this);
501#endif
502 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue to integer"sv});
503 }
504 }
505}
506
507VariantValue::UnsignedIntegerType_ VariantValue::AsUnsignedInteger_ () const
508{
509 if (fVal_ == nullptr) {
510 return 0;
511 }
512 switch (fVal_->GetType ()) {
513 case Type::eFloat: {
514 auto v = Debug::UncheckedDynamicCast<const TIRep_<FloatType_>*> (fVal_.get ());
515 AssertNotNull (v);
516 return Math::Round<VariantValue::UnsignedIntegerType_> (v->fVal);
517 }
518 case Type::eInteger: {
519 auto v = Debug::UncheckedDynamicCast<const TIRep_<IntegerType_>*> (fVal_.get ());
520 AssertNotNull (v);
521 return v->fVal;
522 }
523 case Type::eUnsignedInteger: {
524 auto v = Debug::UncheckedDynamicCast<const TIRep_<UnsignedIntegerType_>*> (fVal_.get ());
525 AssertNotNull (v);
526 return v->fVal;
527 }
528 case Type::eString: {
529 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
530 AssertNotNull (v);
531 // not sure this is right for high order bit set for unsigned long long?
532 // --LGP 2013-08-25
533 return Characters::String2Int<UnsignedIntegerType_> (v->fVal);
534 }
535 default: {
536#if USE_NOISY_TRACE_IN_THIS_MODULE_
537 DbgTrace ("failed coerce-to-uint: type={}, value={}"_f, fVal_->GetType (), *this);
538#endif
539 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue to unsigned integer"sv});
540 }
541 }
542}
543
544VariantValue::FloatType_ VariantValue::AsFloatType_ () const
545{
546 if (fVal_ == nullptr) {
547 return Math::nan<FloatType_> ();
548 }
549 switch (fVal_->GetType ()) {
550 case Type::eInteger: {
551 auto v = Debug::UncheckedDynamicCast<const TIRep_<IntegerType_>*> (fVal_.get ());
552 AssertNotNull (v);
553 return static_cast<FloatType_> (v->fVal);
554 }
555 case Type::eUnsignedInteger: {
556 auto v = Debug::UncheckedDynamicCast<const TIRep_<UnsignedIntegerType_>*> (fVal_.get ());
557 AssertNotNull (v);
558 return static_cast<FloatType_> (v->fVal);
559 }
560 case Type::eFloat: {
561 auto v = Debug::UncheckedDynamicCast<const TIRep_<FloatType_>*> (fVal_.get ());
562 AssertNotNull (v);
563 return v->fVal;
564 }
565 case Type::eString: {
566 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
567 AssertNotNull (v);
568 // NB. this may return NAN if string not a well-formed number (including empty string case)
569 return Characters::FloatConversion::ToFloat<FloatType_> (v->fVal);
570 }
571 default: {
572#if USE_NOISY_TRACE_IN_THIS_MODULE_
573 DbgTrace ("failed coerce-to-float: type={}, value={}"_f, fVal_->GetType (), *this);
574#endif
575 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue to float"sv});
576 }
577 }
578}
579
580Date VariantValue::AsDate_ () const
581{
582 if (fVal_ == nullptr) {
583 Execution::Throw (Date::FormatException::kThe); // until Stroika v2.1d11 this returned Date{}, but no nonger support empty Date objects
584 }
585 switch (fVal_->GetType ()) {
586 case Type::eDate: {
587 auto v = Debug::UncheckedDynamicCast<const TIRep_<Date>*> (fVal_.get ());
588 AssertNotNull (v);
589 return v->fVal;
590 }
591 case Type::eDateTime: {
592 auto v = Debug::UncheckedDynamicCast<const TIRep_<DateTime>*> (fVal_.get ());
593 AssertNotNull (v);
594 return v->fVal.GetDate ();
595 }
596 case Type::eString: {
597 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
598 AssertNotNull (v);
599 return Date::Parse (v->fVal, Date::kISO8601Format);
600 }
601 default: {
602#if USE_NOISY_TRACE_IN_THIS_MODULE_
603 DbgTrace ("failed coerce-to-date: type={}, value={}"_f, fVal_->GetType (), *this);
604#endif
605 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue to date"sv});
606 }
607 }
608}
609
610DateTime VariantValue::AsDateTime_ () const
611{
612 if (fVal_ == nullptr) {
613 Execution::Throw (DateTime::FormatException::kThe); // until Stroika v2.1d11 this returned DateTime{}, but no longer support empty DateTime objects
614 }
615 switch (fVal_->GetType ()) {
616 case Type::eDate: {
617 auto v = Debug::UncheckedDynamicCast<const TIRep_<Date>*> (fVal_.get ());
618 AssertNotNull (v);
619 return DateTime{v->fVal};
620 }
621 case Type::eDateTime: {
622 auto v = Debug::UncheckedDynamicCast<const TIRep_<DateTime>*> (fVal_.get ());
623 AssertNotNull (v);
624 return v->fVal;
625 }
626 case Type::eString: {
627 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
628 AssertNotNull (v);
629 return DateTime::Parse (v->fVal, DateTime::kISO8601Format);
630 }
631 default: {
632#if USE_NOISY_TRACE_IN_THIS_MODULE_
633 DbgTrace ("failed coerce-to-datetime: type={}, value={}"_f, fVal_->GetType (), *this);
634#endif
635 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue to date-time"sv});
636 }
637 }
638}
639
640#if qStroika_HasComponent_boost
641boost::json::value VariantValue::AsBoostJSONValue_ () const
642{
643 using namespace boost;
644 if (fVal_ == nullptr) {
645 return json::value{nullptr};
646 }
647 switch (fVal_->GetType ()) {
648 case Type::eNull: {
649 return json::value{nullptr};
650 }
651 case Type::eBoolean: {
652 return As<bool> ();
653 }
654 case Type::eString: {
655 // I think boost uses / expects UTF8?
656 return As<String> ().AsUTF8<string> ().c_str ();
657 }
658 case Type::eInteger: {
659 return As<IntegerType_> ();
660 }
661 case Type::eUnsignedInteger: {
662 return As<UnsignedIntegerType_> ();
663 }
664 case Type::eFloat: {
665 return static_cast<double> (As<FloatType_> ());
666 }
667 case Type::eBLOB:
668 case Type::eDate:
669 case Type::eDateTime: {
670 // not a boost::json type, so convert to string
671 return As<String> ().AsASCII ().c_str ();
672 }
673 case Type::eArray: {
675 Sequence<VariantValue> srcArray = As<Sequence<VariantValue>> ();
676 json::array result;
677 result.reserve (srcArray.size ());
678 for (const auto& i : srcArray) {
679 result.push_back (i.As<json::value> ());
680 }
681 return result;
682 }
683 case Type::eMap: {
685 Mapping<String, VariantValue> srcMap = As<Mapping<String, VariantValue>> ();
686 json::object result;
687 for (const auto& i : srcMap) {
688 result.insert (json::key_value_pair{i.fKey.As<String> ().AsUTF8<string> ().c_str (), i.fValue.As<json::value> ()});
689 }
690 return result;
691 }
692 default:
694 return json::value{nullptr};
695 }
696}
697#endif
698
699String VariantValue::AsString_ () const
700{
701 if (fVal_ == nullptr) [[unlikely]] {
702 return String{};
703 }
704 switch (fVal_->GetType ()) {
705 case Type::eNull: {
707 return String{};
708 }
709 case Type::eBoolean: {
710 auto v = Debug::UncheckedDynamicCast<const TIRep_<bool>*> (fVal_.get ());
711 AssertNotNull (v);
712 return v->fVal ? "true"sv : "false"sv;
713 }
714 case Type::eBLOB: {
715 auto v = Debug::UncheckedDynamicCast<const TIRep_<Memory::BLOB>*> (fVal_.get ());
716 AssertNotNull (v);
717 return String{Cryptography::Encoding::Algorithm::Base64::Encode (v->fVal)};
718 }
719 case Type::eInteger: {
720 auto v = Debug::UncheckedDynamicCast<const TIRep_<IntegerType_>*> (fVal_.get ());
721 AssertNotNull (v);
722 return "{}"_f(v->fVal);
723 }
724 case Type::eUnsignedInteger: {
725 // Note - unsigned numbers converted to DECIMAL still as text representation
726 auto v = Debug::UncheckedDynamicCast<const TIRep_<UnsignedIntegerType_>*> (fVal_.get ());
727 AssertNotNull (v);
728 return "{}"_f(v->fVal);
729 }
730 case Type::eFloat: {
731 auto v = Debug::UncheckedDynamicCast<const TIRep_<FloatType_>*> (fVal_.get ());
732 AssertNotNull (v);
733 using namespace Characters;
734 return FloatConversion::ToString (v->fVal, FloatConversion::Precision::kFull);
735 }
736 case Type::eDate: {
737 auto v = Debug::UncheckedDynamicCast<const TIRep_<Date>*> (fVal_.get ());
738 AssertNotNull (v);
739 return v->fVal.Format (Date::kISO8601Format);
740 }
741 case Type::eDateTime: {
742 auto v = Debug::UncheckedDynamicCast<const TIRep_<DateTime>*> (fVal_.get ());
743 AssertNotNull (v);
744 return v->fVal.Format (DateTime::kISO8601Format);
745 }
746 case Type::eString: {
747 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
748 AssertNotNull (v);
749 return v->fVal;
750 }
751 case Type::eArray: {
752 auto v = Debug::UncheckedDynamicCast<const TIRep_<Sequence<VariantValue>>*> (fVal_.get ());
753 AssertNotNull (v);
755 tmp << "["sv;
756 for (auto i = v->fVal.begin (); i != v->fVal.end (); ++i) {
757 if (i != v->fVal.begin ()) {
758 tmp << ", "sv;
759 }
760 tmp << i->As<String> ();
761 }
762 tmp << "]"sv;
763 return tmp;
764 }
765 case Type::eMap: {
766 auto v = Debug::UncheckedDynamicCast<const TIRep_<Mapping<String, VariantValue>>*> (fVal_.get ());
767 AssertNotNull (v);
769 tmp << "{"sv;
770 for (auto i = v->fVal.begin (); i != v->fVal.end (); ++i) {
771 if (i != v->fVal.begin ()) {
772 tmp << ", ";
773 }
774 tmp << i->fKey << " -> "sv << i->fValue.As<String> ();
775 }
776 tmp << "}"sv;
777 return tmp;
778 }
779 default: {
780 AssertNotReached (); // That was all types enumerated, and all types convertable to string
781 return String{};
782 }
783 }
784}
785
786Mapping<String, VariantValue> VariantValue::AsMapping_ () const
787{
788 if (fVal_ == nullptr) [[unlikely]] {
790 }
791 switch (fVal_->GetType ()) {
792 case Type::eMap: {
793 auto v = Debug::UncheckedDynamicCast<const TIRep_<Mapping<String, VariantValue>>*> (fVal_.get ());
794 AssertNotNull (v);
795 return v->fVal;
796 }
797 default: {
798 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue of type {} to map"_f(fVal_->GetType ())});
799 }
800 }
801}
802
803Sequence<VariantValue> VariantValue::AsSequence_ () const
804{
805 if (fVal_ == nullptr) [[unlikely]] {
806 return Sequence<VariantValue>{};
807 }
808 switch (fVal_->GetType ()) {
809 case Type::eArray: {
810 auto v = Debug::UncheckedDynamicCast<const TIRep_<Sequence<VariantValue>>*> (fVal_.get ());
811 AssertNotNull (v);
812 return v->fVal;
813 }
814 default: {
815 Execution::Throw (DataExchange::BadFormatException{"Cannot coerce VariantValue of type {} to array"_f(fVal_->GetType ())});
816 }
817 }
818}
819
820/*
821 ********************************************************************************
822 ***************** DataExchange::VariantValue::EqualsComparer *******************
823 ********************************************************************************
824 */
825bool Stroika::Foundation::DataExchange::VariantValue::EqualsComparer::operator() (const VariantValue& lhs, const VariantValue& rhs) const
826{
827 VariantValue ln = lhs.Normalize ();
828 VariantValue rn = rhs.Normalize ();
829 VariantValue::Type lt = ln.GetType ();
830 VariantValue::Type rt = rn.GetType ();
831 if (lt != rt) {
832 return false;
833 }
834 switch (lt) {
835 case VariantValue::eNull:
836 return true;
837 case VariantValue::eBoolean:
838 return ln.As<bool> () == ln.As<bool> ();
839 case VariantValue::eFloat:
840 return Math::NearlyEquals (ln.As<FloatType_> (), ln.As<FloatType_> ());
841 case VariantValue::eString:
842 return ln.As<String> () == ln.As<String> ();
843 case VariantValue::eArray: {
844 // same iff all elts same (after normalizing sub-elts above)
845 return ln.As<Sequence<VariantValue>> () == rn.As<Sequence<VariantValue>> ();
846 }
847 case VariantValue::eMap: {
848 // same iff all elts same (importantly after normalizing which sorts)
850 }
851 case VariantValue::eBLOB:
852 case VariantValue::eInteger:
853 case VariantValue::eUnsignedInteger:
854 case VariantValue::eDate:
855 case VariantValue::eDateTime:
856 AssertNotReached (); // cuz normalized
857 default:
859 return false;
860 }
861}
862
863/*
864 ********************************************************************************
865 ************************* VariantValue::ThreeWayComparer ***********************
866 ********************************************************************************
867 */
868strong_ordering VariantValue::ThreeWayComparer::operator() (const VariantValue& lhs, const VariantValue& rhs) const
869{
870 VariantValue ln = lhs.Normalize ();
871 VariantValue rn = rhs.Normalize ();
872 VariantValue::Type lt = ln.GetType ();
873 VariantValue::Type rt = rn.GetType ();
874 if (lt != rt) {
875 return lt <=> rt; // no obvious sort order, so just use numeric type value
876 }
877 switch (lt) {
878 case VariantValue::eNull:
879 return strong_ordering::equal;
880 case VariantValue::eBoolean:
881 return ln.As<bool> () <=> ln.As<bool> ();
882 case VariantValue::eFloat: {
883 // explicit test so we can do NearlyEquals()
884 FloatType_ l = ln.As<FloatType_> ();
885 FloatType_ r = rn.As<FloatType_> ();
886 if (Math::NearlyEquals (l, r)) {
887 return strong_ordering::equal;
888 }
889 else if (l < r) {
890 return strong_ordering::less;
891 }
892 else {
893 return strong_ordering::greater;
894 }
895 }
896 case VariantValue::eString:
897 return ln.As<String> () <=> ln.As<String> ();
898 case VariantValue::eArray: {
899 // same iff all elts same (after normalizing sub-elts above)
900 return ln.As<Sequence<VariantValue>> () <=> rn.As<Sequence<VariantValue>> ();
901 }
902 case VariantValue::eMap: {
903 //http://stroika-bugs.sophists.com/browse/STK-985
904 //
905 // same iff all elts same (importantly after normalizing which sorts)
906 // @todo find way to make this compare work, but for now, just hack and re-create sorted mapping
907 // Maybe add 'virtual' rep method on Mapping - AsSorted - and then say
908 // As<SortedMapping<...>> on the mapping that comes back from VariantValue.
909 // Or add that same 'feature' just in VariantValue - with flag when creating with SortedMapping (maybe additional 'type')
910 // and logic privately in here so you dont need to worry outside.
911 if (false) {
912 //return ln.As<Mapping<String, VariantValue>> () <=> rn.As<Mapping<String, VariantValue>> ();
913 }
914 else {
916 return SMT{ln.As<Mapping<String, VariantValue>> ()} <=> SMT{rn.As<Mapping<String, VariantValue>> ()};
917 }
918 }
919 case VariantValue::eBLOB:
920 case VariantValue::eInteger:
921 case VariantValue::eUnsignedInteger:
922 case VariantValue::eDate:
923 case VariantValue::eDateTime:
924 AssertNotReached (); // cuz normalized
925 default:
927 return strong_ordering::equal;
928 }
929}
#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:302
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