Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
DataTypes.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include <charconv>
7#include <cmath>
8
9#include "Stroika/Foundation/Characters/FloatConversion.h"
13
14#include "DataTypes.h"
15
16using namespace Stroika::Foundation;
18
19using namespace Stroika::Frameworks;
20using namespace Stroika::Frameworks::UPnP;
22
23namespace {
24 // an integer as UPnP writes it - decimal, a leading + and zeros allowed - whole, and in T's range
25 template <integral T>
26 optional<T> ParseInteger_ (const String& text)
27 {
28 string s = text.AsUTF8<string> ();
29 if (s.starts_with ('+')) {
30 s.erase (0, 1);
31 }
32 T result{};
33 auto [end, ec] = from_chars (s.data (), s.data () + s.size (), result);
34 if (ec != errc{} or end != s.data () + s.size () or s.empty ()) {
35 return nullopt;
36 }
37 return result;
38 }
39
40 // text read as data type t, whose C++ type is T
41 template <IValue T>
42 optional<T> Read_ (const String& text, DataType t)
43 {
44 if constexpr (same_as<T, String>) {
45 return text;
46 }
47 else if constexpr (same_as<T, Character>) {
48 return text.size () == 1 ? optional<T>{text[0]} : nullopt;
49 }
50 else {
51 const String s = text.Trim ();
52 if constexpr (same_as<T, bool>) {
53 // 1 or 0 - and true or false, yes or no, which UPnP says are not to be sent but are to be read
54 const String v = s.ToLowerCase ();
55 if (v == "1"sv or v == "true"sv or v == "yes"sv) {
56 return true;
57 }
58 if (v == "0"sv or v == "false"sv or v == "no"sv) {
59 return false;
60 }
61 return nullopt;
62 }
63 else if constexpr (integral<T>) {
64 return ParseInteger_<T> (s);
65 }
66 else if constexpr (floating_point<T>) {
67 T v = FloatConversion::ToFloat<T> (s); // NaN if it is not a number
68 return std::isfinite (v) ? optional<T>{v} : nullopt;
69 }
70 else if constexpr (same_as<T, Time::Date>) {
72 }
73 else if constexpr (same_as<T, Time::DateTime>) {
74 return Time::DateTime::ParseQuietly (s, Time::DateTime::kISO8601Format);
75 }
76 else if constexpr (same_as<T, Time::TimeOfDay>) {
77 String time = s;
78 if (t == DataType::eTimeTZ) {
79 // its time zone - Z, or +hh:mm or -hh:mm - after it: dropped, as TimeOfDay has none
80 const string u = s.AsUTF8<string> ();
81 time = String{u.substr (0, u.find_first_of ("Z+-"))};
82 }
84 }
85 else {
86 // the rest throw on what they cannot read: none of them blocks, so none is interrupted - any exception is a bad value
87 try {
88 if constexpr (same_as<T, Memory::BLOB>) {
89 return t == DataType::eBinHex ? Memory::BLOB::FromHex (s) : Cryptography::Encoding::Algorithm::Base64::Decode (s);
90 }
91 else if constexpr (same_as<T, IO::Network::URI>) {
92 return IO::Network::URI::Parse (s);
93 }
94 else {
95 static_assert (same_as<T, Common::GUID>);
96 return Common::GUID{s};
97 }
98 }
99 catch (...) {
100 return nullopt;
101 }
102 }
103 }
104 }
105}
106
107/*
108 ********************************************************************************
109 ************************** UPnP::DataTypes::ToText *****************************
110 ********************************************************************************
111 */
113{
114 return visit ([&]<typename T> (const T&) { return ToText (v, DefaultDataType<T> ()); }, v);
115}
116
118{
119 return visit (
120 [t]<typename T> (const T& x) -> String {
121 Require (IsTypeOf<T> (t));
122 if constexpr (same_as<T, bool>) {
123 return x ? "1"sv : "0"sv;
124 }
125 else if constexpr (unsigned_integral<T>) {
126 return "{}"_f(static_cast<uint64_t> (x));
127 }
128 else if constexpr (signed_integral<T>) {
129 return "{}"_f(static_cast<int64_t> (x));
130 }
131 else if constexpr (floating_point<T>) {
132 Require (std::isfinite (x)); // UPnP has no NaN or infinity
133 if (t == DataType::eFixed14_4) {
134 Require (std::abs (x) < 1e14);
135 // 4 digits after the point, then the zeros at the end dropped - and the point, if they were all
136 String s = "{:.4f}"_f(x);
137 while (s.EndsWith ("0"sv)) {
138 s = s.SubString (0, s.size () - 1);
139 }
140 return s.EndsWith ("."sv) ? s.SubString (0, s.size () - 1) : s;
141 }
142 // the fewest digits that read back exactly; and E before an exponent, as UPnP writes it
143 return "{}"_f(x).ReplaceAll ("e"sv, "E"sv);
144 }
145 else if constexpr (same_as<T, Character>) {
146 return String{x};
147 }
148 else if constexpr (same_as<T, String>) {
149 return x;
150 }
151 else if constexpr (same_as<T, Time::Date>) {
152 return x.Format (String{Time::Date::kISO8601Format});
153 }
154 else if constexpr (same_as<T, Time::DateTime>) {
155 if (t == DataType::eDateTime) {
156 // dateTime has no time zone: its date, and time of day, as they are
157 StringBuilder result{x.GetDate ().Format (String{Time::Date::kISO8601Format})};
158 if (optional<Time::TimeOfDay> tod = x.GetTimeOfDay ()) {
159 result << "T"sv << tod->Format (String{Time::TimeOfDay::kISO8601Format});
160 }
161 return result;
162 }
163 return x.Format (Time::DateTime::kISO8601Format);
164 }
165 else if constexpr (same_as<T, Time::TimeOfDay>) {
166 return x.Format (String{Time::TimeOfDay::kISO8601Format});
167 }
168 else if constexpr (same_as<T, Memory::BLOB>) {
169 if (t == DataType::eBinHex) {
170 return x.AsHex ();
171 }
172 using namespace Cryptography::Encoding::Algorithm;
173 return String{Base64::Encode (x, Base64::Options{.fLineBreak = Base64::LineBreak::eNone_LB})};
174 }
175 else {
176 static_assert (same_as<T, IO::Network::URI> or same_as<T, Common::GUID>);
177 return x.template As<String> ();
178 }
179 },
180 v);
181}
182
183/*
184 ********************************************************************************
185 ************************* UPnP::DataTypes::FromText ****************************
186 ********************************************************************************
187 */
188optional<Value> DataTypes::FromText (const String& text, DataType t)
189{
190 // read as t's C++ type
191 auto as = [&]<IValue T> () -> optional<Value> {
192 if (optional<T> v = Read_<T> (text, t)) {
193 return Value{in_place_type<T>, *v};
194 }
195 return nullopt;
196 };
197 switch (t) {
198 case DataType::eUI1:
199 return as.template operator()<uint8_t> ();
200 case DataType::eUI2:
201 return as.template operator()<uint16_t> ();
202 case DataType::eUI4:
203 return as.template operator()<uint32_t> ();
204 case DataType::eUI8:
205 return as.template operator()<uint64_t> ();
206 case DataType::eI1:
207 return as.template operator()<int8_t> ();
208 case DataType::eI2:
209 return as.template operator()<int16_t> ();
210 case DataType::eI4:
211 case DataType::eInt:
212 return as.template operator()<int32_t> ();
213 case DataType::eI8:
214 return as.template operator()<int64_t> ();
215 case DataType::eR4:
216 return as.template operator()<float> ();
217 case DataType::eR8:
218 case DataType::eNumber:
219 case DataType::eFixed14_4:
220 case DataType::eFloat:
221 return as.template operator()<double> ();
222 case DataType::eChar:
223 return as.template operator()<Character> ();
224 case DataType::eString:
225 return as.template operator()<String> ();
226 case DataType::eDate:
227 return as.template operator()<Time::Date> ();
228 case DataType::eDateTime:
229 case DataType::eDateTimeTZ:
230 return as.template operator()<Time::DateTime> ();
231 case DataType::eTime:
232 case DataType::eTimeTZ:
233 return as.template operator()<Time::TimeOfDay> ();
234 case DataType::eBoolean:
235 return as.template operator()<bool> ();
236 case DataType::eBinBase64:
237 case DataType::eBinHex:
238 return as.template operator()<Memory::BLOB> ();
239 case DataType::eURI:
240 return as.template operator()<IO::Network::URI> ();
241 case DataType::eUUID:
242 return as.template operator()<Common::GUID> ();
243 }
245 return nullopt;
246}
#define AssertNotReached()
Definition Assertions.h:358
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 size_t size() const noexcept
Definition String.inl:536
nonvirtual bool EndsWith(const Character &c, CompareOptions co=eWithCase) const
Definition String.cpp:1147
nonvirtual String ToLowerCase() const
Definition String.cpp:1765
nonvirtual String ReplaceAll(const RegularExpression &regEx, const String &with) const
Definition String.cpp:1214
nonvirtual String SubString(SZ from) const
nonvirtual String Trim(bool(*shouldBeTrimmed)(Character)=Character::IsWhitespace) const
Definition String.cpp:1651
nonvirtual String substr(size_t from, size_t count=npos) const
Definition String.inl:1090
static URI Parse(const String &rawURL)
Definition URI.cpp:103
static BLOB FromHex(const char *b)
Convert string of hex bytes to BLOB.
Definition BLOB.inl:127
static constexpr string_view kISO8601Format
Y-M-D format - locale independent, and ISO-8601 date format standard.
Definition Date.h:465
static optional< Date > ParseQuietly(const String &rep, const String &formatPattern)
like Parse(), but returns nullopt on parse error, not throwing exception. if locale is missing,...
Definition Date.inl:393
static constexpr string_view kISO8601Format
Definition TimeOfDay.h:107
static optional< TimeOfDay > ParseQuietly(const String &rep, const String &formatPattern)
like Parse(), but returns nullopt on parse error, not throwing exception. if locale is missing,...
variant< bool, uint8_t, uint16_t, uint32_t, uint64_t, int8_t, int16_t, int32_t, int64_t, float, double, Foundation::Characters::Character, String, Foundation::Time::Date, Foundation::Time::DateTime, Foundation::Time::TimeOfDay, Foundation::Memory::BLOB, Foundation::IO::Network::URI, Foundation::Common::GUID > Value
A value of any of UPnP's data types, as its C++ type (the table above) - for code that learns a value...
Definition DataTypes.h:111
DataType
UPnP's data types - each named, as a service's description names it (Common::DefaultNames): ui2,...
Definition DataTypes.h:69
optional< T > FromText(const String &text)
text read as UPnP reads it: as t, if given, else as T's default data type (the table above) - nullopt...
String ToText(const T &v)
v, as UPnP writes it: as t, if given, else as its C++ type's default data type (the table above)