1 /***
2  * Copyright (C) Microsoft. All rights reserved.
3  * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4  *
5  * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
6  *
7  * Utilities
8  *
9  * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
10  *
11  * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
12  ****/
13 
14 #include "stdafx.h"
15 
16 #include <algorithm>
17 #include <cpprest/asyncrt_utils.h>
18 #include <stdexcept>
19 #include <string>
20 #include <time.h>
21 
22 using namespace web;
23 using namespace utility;
24 using namespace utility::conversions;
25 
26 namespace
27 {
28 struct to_lower_ch_impl
29 {
operator ()__anonefd0a8270111::to_lower_ch_impl30     char operator()(char c) const CPPREST_NOEXCEPT
31     {
32         if (c >= 'A' && c <= 'Z') return static_cast<char>(c - 'A' + 'a');
33         return c;
34     }
35 
operator ()__anonefd0a8270111::to_lower_ch_impl36     wchar_t operator()(wchar_t c) const CPPREST_NOEXCEPT
37     {
38         if (c >= L'A' && c <= L'Z') return static_cast<wchar_t>(c - L'A' + L'a');
39         return c;
40     }
41 };
42 
43 CPPREST_CONSTEXPR to_lower_ch_impl to_lower_ch {};
44 
45 struct eq_lower_ch_impl
46 {
47     template<class CharT>
operator ()__anonefd0a8270111::eq_lower_ch_impl48     inline CharT operator()(const CharT left, const CharT right) const CPPREST_NOEXCEPT
49     {
50         return to_lower_ch(left) == to_lower_ch(right);
51     }
52 };
53 
54 CPPREST_CONSTEXPR eq_lower_ch_impl eq_lower_ch {};
55 
56 struct lt_lower_ch_impl
57 {
58     template<class CharT>
operator ()__anonefd0a8270111::lt_lower_ch_impl59     inline CharT operator()(const CharT left, const CharT right) const CPPREST_NOEXCEPT
60     {
61         return to_lower_ch(left) < to_lower_ch(right);
62     }
63 };
64 
65 CPPREST_CONSTEXPR lt_lower_ch_impl lt_lower_ch {};
66 } // namespace
67 
68 namespace utility
69 {
70 namespace details
71 {
str_iequal(const std::string & left,const std::string & right)72 _ASYNCRTIMP bool __cdecl str_iequal(const std::string& left, const std::string& right) CPPREST_NOEXCEPT
73 {
74     return left.size() == right.size() && std::equal(left.cbegin(), left.cend(), right.cbegin(), eq_lower_ch);
75 }
76 
str_iequal(const std::wstring & left,const std::wstring & right)77 _ASYNCRTIMP bool __cdecl str_iequal(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT
78 {
79     return left.size() == right.size() && std::equal(left.cbegin(), left.cend(), right.cbegin(), eq_lower_ch);
80 }
81 
str_iless(const std::string & left,const std::string & right)82 _ASYNCRTIMP bool __cdecl str_iless(const std::string& left, const std::string& right) CPPREST_NOEXCEPT
83 {
84     return std::lexicographical_compare(left.cbegin(), left.cend(), right.cbegin(), right.cend(), lt_lower_ch);
85 }
86 
str_iless(const std::wstring & left,const std::wstring & right)87 _ASYNCRTIMP bool __cdecl str_iless(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT
88 {
89     return std::lexicographical_compare(left.cbegin(), left.cend(), right.cbegin(), right.cend(), lt_lower_ch);
90 }
91 
inplace_tolower(std::string & target)92 _ASYNCRTIMP void __cdecl inplace_tolower(std::string& target) CPPREST_NOEXCEPT
93 {
94     for (auto& ch : target)
95     {
96         ch = to_lower_ch(ch);
97     }
98 }
99 
inplace_tolower(std::wstring & target)100 _ASYNCRTIMP void __cdecl inplace_tolower(std::wstring& target) CPPREST_NOEXCEPT
101 {
102     for (auto& ch : target)
103     {
104         ch = to_lower_ch(ch);
105     }
106 }
107 
108 #if !defined(ANDROID) && !defined(__ANDROID__)
109 std::once_flag g_c_localeFlag;
110 std::unique_ptr<scoped_c_thread_locale::xplat_locale, void (*)(scoped_c_thread_locale::xplat_locale*)> g_c_locale(
__anonefd0a8270202(scoped_c_thread_locale::xplat_locale*) 111     nullptr, [](scoped_c_thread_locale::xplat_locale*) {});
c_locale()112 scoped_c_thread_locale::xplat_locale scoped_c_thread_locale::c_locale()
113 {
114     std::call_once(g_c_localeFlag, [&]() {
115         scoped_c_thread_locale::xplat_locale* clocale = new scoped_c_thread_locale::xplat_locale();
116 #ifdef _WIN32
117         *clocale = _create_locale(LC_ALL, "C");
118         if (clocale == nullptr || *clocale == nullptr)
119         {
120             throw std::runtime_error("Unable to create 'C' locale.");
121         }
122         auto deleter = [](scoped_c_thread_locale::xplat_locale* clocale) {
123             _free_locale(*clocale);
124             delete clocale;
125         };
126 #else
127         *clocale = newlocale(LC_ALL_MASK, "C", nullptr);
128         if (clocale == nullptr || *clocale == nullptr)
129         {
130             throw std::runtime_error("Unable to create 'C' locale.");
131         }
132         auto deleter = [](scoped_c_thread_locale::xplat_locale *clocale)
133         {
134             freelocale(*clocale);
135             delete clocale;
136         };
137 #endif
138         g_c_locale =
139             std::unique_ptr<scoped_c_thread_locale::xplat_locale, void (*)(scoped_c_thread_locale::xplat_locale*)>(
140                 clocale, deleter);
141     });
142     return *g_c_locale;
143 }
144 #endif
145 
146 #ifdef _WIN32
scoped_c_thread_locale()147 scoped_c_thread_locale::scoped_c_thread_locale() : m_prevLocale(), m_prevThreadSetting(-1)
148 {
149     char* prevLocale = setlocale(LC_ALL, nullptr);
150     if (prevLocale == nullptr)
151     {
152         throw std::runtime_error("Unable to retrieve current locale.");
153     }
154 
155     if (std::strcmp(prevLocale, "C") != 0)
156     {
157         m_prevLocale = prevLocale;
158         m_prevThreadSetting = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
159         if (m_prevThreadSetting == -1)
160         {
161             throw std::runtime_error("Unable to enable per thread locale.");
162         }
163         if (setlocale(LC_ALL, "C") == nullptr)
164         {
165             _configthreadlocale(m_prevThreadSetting);
166             throw std::runtime_error("Unable to set locale");
167         }
168     }
169 }
170 
~scoped_c_thread_locale()171 scoped_c_thread_locale::~scoped_c_thread_locale()
172 {
173     if (m_prevThreadSetting != -1)
174     {
175         setlocale(LC_ALL, m_prevLocale.c_str());
176         _configthreadlocale(m_prevThreadSetting);
177     }
178 }
179 #elif (defined(ANDROID) || defined(__ANDROID__))
scoped_c_thread_locale()180 scoped_c_thread_locale::scoped_c_thread_locale() {}
~scoped_c_thread_locale()181 scoped_c_thread_locale::~scoped_c_thread_locale() {}
182 #else
scoped_c_thread_locale()183 scoped_c_thread_locale::scoped_c_thread_locale() : m_prevLocale(nullptr)
184 {
185     char* prevLocale = setlocale(LC_ALL, nullptr);
186     if (prevLocale == nullptr)
187     {
188         throw std::runtime_error("Unable to retrieve current locale.");
189     }
190 
191     if (std::strcmp(prevLocale, "C") != 0)
192     {
193         m_prevLocale = uselocale(c_locale());
194         if (m_prevLocale == nullptr)
195         {
196             throw std::runtime_error("Unable to set locale");
197         }
198     }
199 }
200 
~scoped_c_thread_locale()201 scoped_c_thread_locale::~scoped_c_thread_locale()
202 {
203     if (m_prevLocale != nullptr)
204     {
205         uselocale(m_prevLocale);
206     }
207 }
208 #endif
209 } // namespace details
210 
211 namespace details
212 {
platform_category()213 const std::error_category& __cdecl platform_category()
214 {
215 #ifdef _WIN32
216     return windows_category();
217 #else
218     return linux_category();
219 #endif
220 }
221 
222 #ifdef _WIN32
223 
224 // Remove once VS 2013 is no longer supported.
225 #if _MSC_VER < 1900
226 static details::windows_category_impl instance;
227 #endif
windows_category()228 const std::error_category& __cdecl windows_category()
229 {
230 #if _MSC_VER >= 1900
231     static details::windows_category_impl instance;
232 #endif
233     return instance;
234 }
235 
message(int errorCode) const236 std::string windows_category_impl::message(int errorCode) const CPPREST_NOEXCEPT
237 {
238     const size_t buffer_size = 4096;
239     DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM;
240     LPCVOID lpSource = NULL;
241 
242 #if !defined(__cplusplus_winrt)
243     if (errorCode >= 12000)
244     {
245         dwFlags = FORMAT_MESSAGE_FROM_HMODULE;
246         lpSource = GetModuleHandleA("winhttp.dll"); // this handle DOES NOT need to be freed
247     }
248 #endif
249 
250     std::wstring buffer(buffer_size, 0);
251 
252     const auto result = ::FormatMessageW(dwFlags, lpSource, errorCode, 0, &buffer[0], buffer_size, NULL);
253 
254     if (result == 0)
255     {
256         return "Unable to get an error message for error code: " + std::to_string(errorCode) + ".";
257     }
258 
259     // strip exceeding characters of the initial resize call
260     buffer.resize(result);
261 
262     return utility::conversions::to_utf8string(buffer);
263 }
264 
default_error_condition(int errorCode) const265 std::error_condition windows_category_impl::default_error_condition(int errorCode) const CPPREST_NOEXCEPT
266 {
267     // First see if the STL implementation can handle the mapping for common cases.
268     const std::error_condition errCondition = std::system_category().default_error_condition(errorCode);
269     const std::string errConditionMsg = errCondition.message();
270     if (!utility::details::str_iequal(errConditionMsg, "unknown error"))
271     {
272         return errCondition;
273     }
274 
275     switch (errorCode)
276     {
277 #ifndef __cplusplus_winrt
278         case ERROR_WINHTTP_TIMEOUT: return std::errc::timed_out;
279         case ERROR_WINHTTP_CANNOT_CONNECT: return std::errc::host_unreachable;
280         case ERROR_WINHTTP_CONNECTION_ERROR: return std::errc::connection_aborted;
281 #endif
282         case INET_E_RESOURCE_NOT_FOUND:
283         case INET_E_CANNOT_CONNECT: return std::errc::host_unreachable;
284         case INET_E_CONNECTION_TIMEOUT: return std::errc::timed_out;
285         case INET_E_DOWNLOAD_FAILURE: return std::errc::connection_aborted;
286         default: break;
287     }
288 
289     return std::error_condition(errorCode, *this);
290 }
291 
292 #else
293 
linux_category()294 const std::error_category& __cdecl linux_category()
295 {
296     // On Linux we are using boost error codes which have the exact same
297     // mapping and are equivalent with std::generic_category error codes.
298     return std::generic_category();
299 }
300 
301 #endif
302 
303 } // namespace details
304 
305 #define LOW_3BITS 0x7
306 #define LOW_4BITS 0xF
307 #define LOW_5BITS 0x1F
308 #define LOW_6BITS 0x3F
309 #define BIT4 0x8
310 #define BIT5 0x10
311 #define BIT6 0x20
312 #define BIT7 0x40
313 #define BIT8 0x80
314 #define L_SURROGATE_START 0xDC00
315 #define L_SURROGATE_END 0xDFFF
316 #define H_SURROGATE_START 0xD800
317 #define H_SURROGATE_END 0xDBFF
318 #define SURROGATE_PAIR_START 0x10000
319 
320 // Create a dedicated type for characters to avoid the issue
321 // of different platforms defaulting char to be either signed
322 // or unsigned.
323 using UtilCharInternal_t = signed char;
324 
count_utf8_to_utf16(const std::string & s)325 inline size_t count_utf8_to_utf16(const std::string& s)
326 {
327     const size_t sSize = s.size();
328     auto const sData = reinterpret_cast<const UtilCharInternal_t*>(s.data());
329     size_t result {sSize};
330 
331     for (size_t index = 0; index < sSize;)
332     {
333         if (sData[index] >= 0)
334         {
335             // use fast inner loop to skip single byte code points (which are
336             // expected to be the most frequent)
337             while ((++index < sSize) && (sData[index] >= 0))
338                 ;
339 
340             if (index >= sSize) break;
341         }
342 
343         // start special handling for multi-byte code points
344         const UtilCharInternal_t c {sData[index++]};
345 
346         if ((c & BIT7) == 0)
347         {
348             throw std::range_error("UTF-8 string character can never start with 10xxxxxx");
349         }
350         else if ((c & BIT6) == 0) // 2 byte character, 0x80 to 0x7FF
351         {
352             if (index == sSize)
353             {
354                 throw std::range_error("UTF-8 string is missing bytes in character");
355             }
356 
357             const UtilCharInternal_t c2 {sData[index++]};
358             if ((c2 & 0xC0) != BIT8)
359             {
360                 throw std::range_error("UTF-8 continuation byte is missing leading bit mask");
361             }
362 
363             // can't require surrogates for 7FF
364             --result;
365         }
366         else if ((c & BIT5) == 0) // 3 byte character, 0x800 to 0xFFFF
367         {
368             if (sSize - index < 2)
369             {
370                 throw std::range_error("UTF-8 string is missing bytes in character");
371             }
372 
373             const UtilCharInternal_t c2 {sData[index++]};
374             const UtilCharInternal_t c3 {sData[index++]};
375             if (((c2 | c3) & 0xC0) != BIT8)
376             {
377                 throw std::range_error("UTF-8 continuation byte is missing leading bit mask");
378             }
379 
380             result -= 2;
381         }
382         else if ((c & BIT4) == 0) // 4 byte character, 0x10000 to 0x10FFFF
383         {
384             if (sSize - index < 3)
385             {
386                 throw std::range_error("UTF-8 string is missing bytes in character");
387             }
388 
389             const UtilCharInternal_t c2 {sData[index++]};
390             const UtilCharInternal_t c3 {sData[index++]};
391             const UtilCharInternal_t c4 {sData[index++]};
392             if (((c2 | c3 | c4) & 0xC0) != BIT8)
393             {
394                 throw std::range_error("UTF-8 continuation byte is missing leading bit mask");
395             }
396 
397             const uint32_t codePoint =
398                 ((c & LOW_3BITS) << 18) | ((c2 & LOW_6BITS) << 12) | ((c3 & LOW_6BITS) << 6) | (c4 & LOW_6BITS);
399             result -= (3 - (codePoint >= SURROGATE_PAIR_START));
400         }
401         else
402         {
403             throw std::range_error("UTF-8 string has invalid Unicode code point");
404         }
405     }
406 
407     return result;
408 }
409 
utf8_to_utf16(const std::string & s)410 utf16string __cdecl conversions::utf8_to_utf16(const std::string& s)
411 {
412     // Save repeated heap allocations, use the length of resulting sequence.
413     const size_t srcSize = s.size();
414     auto const srcData = reinterpret_cast<const UtilCharInternal_t*>(s.data());
415     utf16string dest(count_utf8_to_utf16(s), L'\0');
416     utf16string::value_type* const destData = &dest[0];
417     size_t destIndex = 0;
418 
419     for (size_t index = 0; index < srcSize; ++index)
420     {
421         UtilCharInternal_t src = srcData[index];
422         switch (src & 0xF0)
423         {
424             case 0xF0: // 4 byte character, 0x10000 to 0x10FFFF
425             {
426                 const UtilCharInternal_t c2 {srcData[++index]};
427                 const UtilCharInternal_t c3 {srcData[++index]};
428                 const UtilCharInternal_t c4 {srcData[++index]};
429                 uint32_t codePoint =
430                     ((src & LOW_3BITS) << 18) | ((c2 & LOW_6BITS) << 12) | ((c3 & LOW_6BITS) << 6) | (c4 & LOW_6BITS);
431                 if (codePoint >= SURROGATE_PAIR_START)
432                 {
433                     // In UTF-16 U+10000 to U+10FFFF are represented as two 16-bit code units, surrogate pairs.
434                     //  - 0x10000 is subtracted from the code point
435                     //  - high surrogate is 0xD800 added to the top ten bits
436                     //  - low surrogate is 0xDC00 added to the low ten bits
437                     codePoint -= SURROGATE_PAIR_START;
438                     destData[destIndex++] = static_cast<utf16string::value_type>((codePoint >> 10) | H_SURROGATE_START);
439                     destData[destIndex++] =
440                         static_cast<utf16string::value_type>((codePoint & 0x3FF) | L_SURROGATE_START);
441                 }
442                 else
443                 {
444                     // In UTF-16 U+0000 to U+D7FF and U+E000 to U+FFFF are represented exactly as the Unicode code point
445                     // value. U+D800 to U+DFFF are not valid characters, for simplicity we assume they are not present
446                     // but will encode them if encountered.
447                     destData[destIndex++] = static_cast<utf16string::value_type>(codePoint);
448                 }
449             }
450             break;
451             case 0xE0: // 3 byte character, 0x800 to 0xFFFF
452             {
453                 const UtilCharInternal_t c2 {srcData[++index]};
454                 const UtilCharInternal_t c3 {srcData[++index]};
455                 destData[destIndex++] = static_cast<utf16string::value_type>(
456                     ((src & LOW_4BITS) << 12) | ((c2 & LOW_6BITS) << 6) | (c3 & LOW_6BITS));
457             }
458             break;
459             case 0xD0: // 2 byte character, 0x80 to 0x7FF
460             case 0xC0:
461             {
462                 const UtilCharInternal_t c2 {srcData[++index]};
463                 destData[destIndex++] =
464                     static_cast<utf16string::value_type>(((src & LOW_5BITS) << 6) | (c2 & LOW_6BITS));
465             }
466             break;
467             default: // single byte character, 0x0 to 0x7F
468                 // try to use a fast inner loop for following single byte characters,
469                 // since they are quite probable
470                 do
471                 {
472                     destData[destIndex++] = static_cast<utf16string::value_type>(srcData[index++]);
473                 } while (index < srcSize && srcData[index] > 0);
474                 // adjust index since it will be incremented by the for loop
475                 --index;
476         }
477     }
478     return dest;
479 }
480 
count_utf16_to_utf8(const utf16string & w)481 inline size_t count_utf16_to_utf8(const utf16string& w)
482 {
483     const utf16string::value_type* const srcData = &w[0];
484     const size_t srcSize = w.size();
485     size_t destSize(srcSize);
486     for (size_t index = 0; index < srcSize; ++index)
487     {
488         const utf16string::value_type ch(srcData[index]);
489         if (ch <= 0x7FF)
490         {
491             if (ch > 0x7F) // 2 bytes needed (11 bits used)
492             {
493                 ++destSize;
494             }
495         }
496         // Check for high surrogate.
497         else if (ch >= H_SURROGATE_START && ch <= H_SURROGATE_END) // 4 bytes needed (21 bits used)
498         {
499             ++index;
500             if (index == srcSize)
501             {
502                 throw std::range_error("UTF-16 string is missing low surrogate");
503             }
504 
505             const auto lowSurrogate = srcData[index];
506             if (lowSurrogate < L_SURROGATE_START || lowSurrogate > L_SURROGATE_END)
507             {
508                 throw std::range_error("UTF-16 string has invalid low surrogate");
509             }
510 
511             destSize += 2;
512         }
513         else // 3 bytes needed (16 bits used)
514         {
515             destSize += 2;
516         }
517     }
518 
519     return destSize;
520 }
521 
utf16_to_utf8(const utf16string & w)522 std::string __cdecl conversions::utf16_to_utf8(const utf16string& w)
523 {
524     const size_t srcSize = w.size();
525     const utf16string::value_type* const srcData = &w[0];
526     std::string dest(count_utf16_to_utf8(w), '\0');
527     std::string::value_type* const destData = &dest[0];
528     size_t destIndex(0);
529 
530     for (size_t index = 0; index < srcSize; ++index)
531     {
532         const utf16string::value_type src = srcData[index];
533         if (src <= 0x7FF)
534         {
535             if (src <= 0x7F) // single byte character
536             {
537                 destData[destIndex++] = static_cast<char>(src);
538             }
539             else // 2 bytes needed (11 bits used)
540             {
541                 destData[destIndex++] = static_cast<char>(char((src >> 6) | 0xC0));        // leading 5 bits
542                 destData[destIndex++] = static_cast<char>(char((src & LOW_6BITS) | BIT8)); // trailing 6 bits
543             }
544         }
545         // Check for high surrogate.
546         else if (src >= H_SURROGATE_START && src <= H_SURROGATE_END)
547         {
548             const auto highSurrogate = src;
549             const auto lowSurrogate = srcData[++index];
550 
551             // To get from surrogate pair to Unicode code point:
552             // - subtract 0xD800 from high surrogate, this forms top ten bits
553             // - subtract 0xDC00 from low surrogate, this forms low ten bits
554             // - add 0x10000
555             // Leaves a code point in U+10000 to U+10FFFF range.
556             uint32_t codePoint = highSurrogate - H_SURROGATE_START;
557             codePoint <<= 10;
558             codePoint |= lowSurrogate - L_SURROGATE_START;
559             codePoint += SURROGATE_PAIR_START;
560 
561             // 4 bytes needed (21 bits used)
562             destData[destIndex++] = static_cast<char>((codePoint >> 18) | 0xF0);               // leading 3 bits
563             destData[destIndex++] = static_cast<char>(((codePoint >> 12) & LOW_6BITS) | BIT8); // next 6 bits
564             destData[destIndex++] = static_cast<char>(((codePoint >> 6) & LOW_6BITS) | BIT8);  // next 6 bits
565             destData[destIndex++] = static_cast<char>((codePoint & LOW_6BITS) | BIT8);         // trailing 6 bits
566         }
567         else // 3 bytes needed (16 bits used)
568         {
569             destData[destIndex++] = static_cast<char>((src >> 12) | 0xE0);              // leading 4 bits
570             destData[destIndex++] = static_cast<char>(((src >> 6) & LOW_6BITS) | BIT8); // middle 6 bits
571             destData[destIndex++] = static_cast<char>((src & LOW_6BITS) | BIT8);        // trailing 6 bits
572         }
573     }
574 
575     return dest;
576 }
577 
usascii_to_utf16(const std::string & s)578 utf16string __cdecl conversions::usascii_to_utf16(const std::string& s)
579 {
580     // Ascii is a subset of UTF-8 so just convert to UTF-16
581     return utf8_to_utf16(s);
582 }
583 
latin1_to_utf16(const std::string & s)584 utf16string __cdecl conversions::latin1_to_utf16(const std::string& s)
585 {
586     // Latin1 is the first 256 code points in Unicode.
587     // In UTF-16 encoding each of these is represented as exactly the numeric code point.
588     utf16string dest;
589     // Prefer resize combined with for-loop over constructor dest(s.begin(), s.end())
590     // for faster assignment.
591     dest.resize(s.size());
592     for (size_t i = 0; i < s.size(); ++i)
593     {
594         dest[i] = utf16char(static_cast<unsigned char>(s[i]));
595     }
596     return dest;
597 }
598 
latin1_to_utf8(const std::string & s)599 utf8string __cdecl conversions::latin1_to_utf8(const std::string& s) { return utf16_to_utf8(latin1_to_utf16(s)); }
600 
601 #ifndef _UTF16_STRINGS
to_string_t(utf16string && s)602 utility::string_t __cdecl conversions::to_string_t(utf16string&& s) { return utf16_to_utf8(std::move(s)); }
603 #endif
604 
605 #ifdef _UTF16_STRINGS
to_string_t(std::string && s)606 utility::string_t __cdecl conversions::to_string_t(std::string&& s) { return utf8_to_utf16(std::move(s)); }
607 #endif
608 
609 #ifndef _UTF16_STRINGS
to_string_t(const utf16string & s)610 utility::string_t __cdecl conversions::to_string_t(const utf16string& s) { return utf16_to_utf8(s); }
611 #endif
612 
613 #ifdef _UTF16_STRINGS
to_string_t(const std::string & s)614 utility::string_t __cdecl conversions::to_string_t(const std::string& s) { return utf8_to_utf16(s); }
615 #endif
616 
to_utf8string(const utf16string & value)617 std::string __cdecl conversions::to_utf8string(const utf16string& value) { return utf16_to_utf8(value); }
618 
to_utf16string(const std::string & value)619 utf16string __cdecl conversions::to_utf16string(const std::string& value) { return utf8_to_utf16(value); }
620 
621 static const int64_t NtToUnixOffsetSeconds = 11644473600; // diff between windows and unix epochs (seconds)
622 
year_is_leap_year(int year)623 static bool year_is_leap_year(int year) { return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); }
624 
625 static const int SecondsInMinute = 60;
626 static const int SecondsInHour = SecondsInMinute * 60;
627 static const int SecondsInDay = SecondsInHour * 24;
628 
629 static const int DaysInYear = 365;
630 static const int DaysIn4Years = DaysInYear * 4 + 1;
631 static const int DaysIn100Years = DaysIn4Years * 25 - 1;
632 static const int DaysIn400Years = DaysIn100Years * 4 + 1;
633 
634 static const int SecondsInYear = SecondsInDay * DaysInYear;
635 static const int SecondsIn4Years = SecondsInDay * DaysIn4Years;
636 static const int64_t SecondsIn100Years = static_cast<int64_t>(SecondsInDay) * DaysIn100Years;
637 static const int64_t SecondsIn400Years = static_cast<int64_t>(SecondsInDay) * DaysIn400Years;
638 static const int64_t SecondsFrom1900To2001 = INT64_C(3187296000);
639 
640 static const int64_t NtTo1900OffsetInterval = INT64_C(0x014F373BFDE04000);
641 
count_leap_years(const int yearsSince1900)642 static int count_leap_years(const int yearsSince1900)
643 {
644     int tmpYears = yearsSince1900 + 299; // shift into 1601, the first 400 year cycle including 1900
645 
646     int year400 = tmpYears / 400;
647     tmpYears -= year400 * 400;
648     int result = year400 * 97;
649 
650     int year100 = tmpYears / 100;
651     tmpYears -= year100 * 100;
652     result += year100 * 24;
653 
654     result += tmpYears / 4;
655 
656     // subtract off leap years from 1601
657     result -= 72;
658 
659     return result;
660 }
661 
662 // The following table assumes no leap year; leap year is added separately
663 static const unsigned short cumulative_days_to_month[12] = {
664     0,   // Jan
665     31,  // Feb
666     59,  // Mar
667     90,  // Apr
668     120, // May
669     151, // Jun
670     181, // Jul
671     212, // Aug
672     243, // Sep
673     273, // Oct
674     304, // Nov
675     334  // Dec
676 };
677 
678 static const unsigned short cumulative_days_to_month_leap[12] = {
679     0,   // Jan
680     31,  // Feb
681     60,  // Mar
682     91,  // Apr
683     121, // May
684     152, // Jun
685     182, // Jul
686     213, // Aug
687     244, // Sep
688     274, // Oct
689     305, // Nov
690     335  // Dec
691 };
692 
utc_now()693 datetime __cdecl datetime::utc_now()
694 {
695 #ifdef _WIN32
696     ULARGE_INTEGER largeInt;
697     FILETIME fileTime;
698     GetSystemTimeAsFileTime(&fileTime);
699 
700     largeInt.LowPart = fileTime.dwLowDateTime;
701     largeInt.HighPart = fileTime.dwHighDateTime;
702 
703     return datetime(largeInt.QuadPart);
704 #else // LINUX
705     struct timeval time;
706     gettimeofday(&time, nullptr);
707     int64_t result = NtToUnixOffsetSeconds + time.tv_sec;
708     result *= _secondTicks;      // convert to 10e-7
709     result += time.tv_usec * 10; // convert and add microseconds, 10e-6 to 10e-7
710     return datetime(static_cast<interval_type>(result));
711 #endif
712 }
713 
714 static const char dayNames[] = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
715 static const char monthNames[] = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
716 
717 struct compute_year_result
718 {
719     int year;
720     int secondsLeftThisYear;
721 };
722 
723 static const int64_t secondsFrom1601To1900 = INT64_C(9435484800);
724 
compute_year(int64_t secondsSince1900)725 static compute_year_result compute_year(int64_t secondsSince1900)
726 {
727     int64_t secondsLeft = secondsSince1900 + secondsFrom1601To1900; // shift to start of this 400 year cycle
728 
729     int year400 = static_cast<int>(secondsLeft / SecondsIn400Years);
730     secondsLeft -= year400 * SecondsIn400Years;
731 
732     int year100 = static_cast<int>(secondsLeft / SecondsIn100Years);
733     secondsLeft -= year100 * SecondsIn100Years;
734 
735     int year4 = static_cast<int>(secondsLeft / SecondsIn4Years);
736     int secondsInt = static_cast<int>(secondsLeft - year4 * SecondsIn4Years);
737 
738     int year1 = secondsInt / SecondsInYear;
739     secondsInt -= year1 * SecondsInYear;
740 
741     // shift back to 1900 base from 1601:
742     return {year400 * 400 + year100 * 100 + year4 * 4 + year1 - 299, secondsInt};
743 }
744 
to_string(date_format format) const745 utility::string_t datetime::to_string(date_format format) const
746 {
747     if (m_interval > INT64_C(2650467743990000000))
748     {
749         throw std::out_of_range("The requested year exceeds the year 9999.");
750     }
751 
752     const int64_t epochAdjusted = static_cast<int64_t>(m_interval) - NtTo1900OffsetInterval;
753     const int64_t secondsSince1900 = epochAdjusted / _secondTicks; // convert to seconds
754     const int fracSec = static_cast<int>(epochAdjusted % _secondTicks);
755 
756     const auto yearData = compute_year(secondsSince1900);
757     const int year = yearData.year;
758     const int yearDay = yearData.secondsLeftThisYear / SecondsInDay;
759     int leftover = yearData.secondsLeftThisYear % SecondsInDay;
760     const int hour = leftover / SecondsInHour;
761     leftover = leftover % SecondsInHour;
762     const int minute = leftover / SecondsInMinute;
763     leftover = leftover % SecondsInMinute;
764 
765     const auto& monthTable = year_is_leap_year(year) ? cumulative_days_to_month_leap : cumulative_days_to_month;
766     int month = 0;
767     while (month < 11 && monthTable[month + 1] <= yearDay)
768     {
769         ++month;
770     }
771 
772     const auto monthDay = yearDay - monthTable[month] + 1;
773     const auto weekday = static_cast<int>((secondsSince1900 / SecondsInDay + 1) % 7);
774 
775     char outBuffer[38]; // Thu, 01 Jan 1970 00:00:00 GMT\0
776                         // 1970-01-01T00:00:00.1234567Z\0
777     char* outCursor = outBuffer;
778     switch (format)
779     {
780         case RFC_1123:
781 #ifdef _MSC_VER
782             sprintf_s(outCursor,
783                       26,
784                       "%s, %02d %s %04d %02d:%02d:%02d",
785                       dayNames + 4 * weekday,
786                       monthDay,
787                       monthNames + 4 * month,
788                       year + 1900,
789                       hour,
790                       minute,
791                       leftover);
792 #else  // ^^^ _MSC_VER // !_MSC_VER vvv
793             sprintf(outCursor,
794                     "%s, %02d %s %04d %02d:%02d:%02d",
795                     dayNames + 4 * weekday,
796                     monthDay,
797                     monthNames + 4 * month,
798                     year + 1900,
799                     hour,
800                     minute,
801                     leftover);
802 #endif // _MSC_VER
803             outCursor += 25;
804             memcpy(outCursor, " GMT", 4);
805             outCursor += 4;
806             return utility::string_t(outBuffer, outCursor);
807         case ISO_8601:
808 #ifdef _MSC_VER
809             sprintf_s(outCursor,
810                       20,
811                       "%04d-%02d-%02dT%02d:%02d:%02d",
812                       year + 1900,
813                       month + 1,
814                       monthDay,
815                       hour,
816                       minute,
817                       leftover);
818 #else  // ^^^ _MSC_VER // !_MSC_VER vvv
819             sprintf(
820                 outCursor, "%04d-%02d-%02dT%02d:%02d:%02d", year + 1900, month + 1, monthDay, hour, minute, leftover);
821 #endif // _MSC_VER
822             outCursor += 19;
823             if (fracSec != 0)
824             {
825                 // Append fractional second, which is a 7-digit value with no trailing zeros
826                 // This way, '1200' becomes '00012'
827 #ifdef _MSC_VER
828                 size_t appended = sprintf_s(outCursor, 9, ".%07d", fracSec);
829 #else  // ^^^ _MSC_VER // !_MSC_VER vvv
830                 size_t appended = sprintf(outCursor, ".%07d", fracSec);
831 #endif // _MSC_VER
832                 while (outCursor[appended - 1] == '0')
833                 {
834                     --appended; // trim trailing zeros
835                 }
836 
837                 outCursor += appended;
838             }
839 
840             *outCursor = 'Z';
841             ++outCursor;
842             return utility::string_t(outBuffer, outCursor);
843         default: throw std::invalid_argument("Unrecognized date format.");
844     }
845 }
846 
847 template<class CharT>
string_starts_with(const CharT * haystack,const char * needle)848 static bool string_starts_with(const CharT* haystack, const char* needle)
849 {
850     while (*needle)
851     {
852         if (*haystack != static_cast<CharT>(*needle))
853         {
854             return false;
855         }
856 
857         ++haystack;
858         ++needle;
859     }
860 
861     return true;
862 }
863 
864 #define ascii_isdigit(c) ((unsigned char)((unsigned char)(c) - '0') <= 9)
865 #define ascii_isdigit6(c) ((unsigned char)((unsigned char)(c) - '0') <= 6)
866 #define ascii_isdigit5(c) ((unsigned char)((unsigned char)(c) - '0') <= 5)
867 #define ascii_isdigit3(c) ((unsigned char)((unsigned char)(c) - '0') <= 3)
868 #define ascii_isdigit2(c) ((unsigned char)((unsigned char)(c) - '0') <= 2)
869 #define ascii_isdigit1(c) ((unsigned char)((unsigned char)(c) - '0') <= 1)
870 
871 static const unsigned char max_days_in_month[12] = {
872     31, // Jan
873     00, // Feb, special handling for leap years
874     31, // Mar
875     30, // Apr
876     31, // May
877     30, // Jun
878     31, // Jul
879     31, // Aug
880     30, // Sep
881     31, // Oct
882     30, // Nov
883     31  // Dec
884 };
885 
validate_day_month(int day,int month,int year)886 static bool validate_day_month(int day, int month, int year)
887 {
888     int maxDaysThisMonth;
889     if (month == 1)
890     { // Feb needs leap year testing
891         maxDaysThisMonth = 28 + year_is_leap_year(year);
892     }
893     else
894     {
895         maxDaysThisMonth = max_days_in_month[month];
896     }
897 
898     return day >= 1 && day <= maxDaysThisMonth;
899 }
900 
get_year_day(int month,int monthDay,int year)901 static int get_year_day(int month, int monthDay, int year)
902 {
903     return cumulative_days_to_month[month] + monthDay + (year_is_leap_year(year) && month > 1) - 1;
904 }
905 
906 template<class CharT>
atoi2(const CharT * str)907 static int atoi2(const CharT* str)
908 {
909     return (static_cast<unsigned char>(str[0]) - '0') * 10 + (static_cast<unsigned char>(str[1]) - '0');
910 }
911 
timezone_adjust(int64_t result,unsigned char chSign,int adjustHours,int adjustMinutes)912 static int64_t timezone_adjust(int64_t result, unsigned char chSign, int adjustHours, int adjustMinutes)
913 {
914     if (adjustHours > 23)
915     {
916         return -1;
917     }
918 
919     // adjustMinutes > 59 is impossible due to digit 5 check
920     const int tzAdjust = adjustMinutes * 60 + adjustHours * 60 * 60;
921     if (chSign == '-')
922     {
923         if (INT64_MAX - result < tzAdjust)
924         {
925             return -1;
926         }
927 
928         result += tzAdjust;
929     }
930     else
931     {
932         if (tzAdjust > result)
933         {
934             return -1;
935         }
936 
937         result -= tzAdjust;
938     }
939 
940     return result;
941 }
942 
943 /*
944 https://tools.ietf.org/html/rfc822
945 https://tools.ietf.org/html/rfc1123
946 
947 date-time   =  [ day "," ] date time        ; dd mm yy
948                                             ;  hh:mm:ss zzz
949 
950 day         =  "Mon"  / "Tue" /  "Wed"  / "Thu"
951             /  "Fri"  / "Sat" /  "Sun"
952 
953 date        =  1*2DIGIT month 2DIGIT        ; day month year
954                                             ;  e.g. 20 Jun 82
955 RFC1123 changes this to:
956 date        =  1*2DIGIT month 2*4DIGIT        ; day month year
957                                               ;  e.g. 20 Jun 1982
958 This implementation only accepts 4 digit years.
959 
960 month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr"
961             /  "May"  /  "Jun" /  "Jul"  /  "Aug"
962             /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"
963 
964 time        =  hour zone                    ; ANSI and Military
965 
966 hour        =  2DIGIT ":" 2DIGIT [":" 2DIGIT]
967                                             ; 00:00:00 - 23:59:59
968 
969 zone        =  "UT"  / "GMT"                ; Universal Time
970                                             ; North American : UT
971             /  "EST" / "EDT"                ;  Eastern:  - 5/ - 4
972             /  "CST" / "CDT"                ;  Central:  - 6/ - 5
973             /  "MST" / "MDT"                ;  Mountain: - 7/ - 6
974             /  "PST" / "PDT"                ;  Pacific:  - 8/ - 7
975 
976 // military time deleted by RFC 1123
977 
978             / ( ("+" / "-") 4DIGIT )        ; Local differential
979                                             ;  hours+min. (HHMM)
980 */
981 
982 
from_string(const utility::string_t & dateString,date_format format)983 datetime __cdecl datetime::from_string(const utility::string_t& dateString, date_format format)
984 {
985     datetime result;
986     int64_t secondsSince1900;
987     uint64_t fracSec = 0;
988     auto str = dateString.c_str();
989     if (format == RFC_1123)
990     {
991         int parsedWeekday = 0;
992         for (; parsedWeekday < 7; ++parsedWeekday)
993         {
994             if (string_starts_with(str, dayNames + parsedWeekday * 4) && str[3] == _XPLATSTR(',') &&
995                 str[4] == _XPLATSTR(' '))
996             {
997                 str += 5; // parsed day of week
998                 break;
999             }
1000         }
1001 
1002         int monthDay;
1003         if (ascii_isdigit3(str[0]) && ascii_isdigit(str[1]) && str[2] == _XPLATSTR(' '))
1004         {
1005             monthDay = atoi2(str); // validity checked later
1006             str += 3;              // parsed day
1007         }
1008         else if (ascii_isdigit(str[0]) && str[1] == _XPLATSTR(' '))
1009         {
1010             monthDay = str[0] - _XPLATSTR('0');
1011             str += 2; // parsed day
1012         }
1013         else
1014         {
1015             return result;
1016         }
1017 
1018         if (monthDay == 0)
1019         {
1020             return result;
1021         }
1022 
1023         int month = 0;
1024         for (;;)
1025         {
1026             if (string_starts_with(str, monthNames + month * 4))
1027             {
1028                 break;
1029             }
1030 
1031             ++month;
1032             if (month == 12)
1033             {
1034                 return result;
1035             }
1036         }
1037 
1038         if (str[3] != _XPLATSTR(' '))
1039         {
1040             return result;
1041         }
1042 
1043         str += 4; // parsed month
1044 
1045         if (!ascii_isdigit(str[0]) || !ascii_isdigit(str[1]) || !ascii_isdigit(str[2]) || !ascii_isdigit(str[3]) ||
1046             str[4] != ' ')
1047         {
1048             return result;
1049         }
1050 
1051         int year = (str[0] - _XPLATSTR('0')) * 1000 + (str[1] - _XPLATSTR('0')) * 100 + (str[2] - _XPLATSTR('0')) * 10 +
1052                    (str[3] - _XPLATSTR('0'));
1053         if (year < 1900)
1054         {
1055             return result;
1056         }
1057 
1058         // days in month validity check
1059         if (!validate_day_month(monthDay, month, year))
1060         {
1061             return result;
1062         }
1063 
1064         str += 5; // parsed year
1065         const int yearDay = get_year_day(month, monthDay, year);
1066 
1067         if (!ascii_isdigit2(str[0]) || !ascii_isdigit(str[1]) || str[2] != _XPLATSTR(':') || !ascii_isdigit5(str[3]) ||
1068             !ascii_isdigit(str[4]))
1069         {
1070             return result;
1071         }
1072 
1073         const int hour = atoi2(str);
1074         if (hour > 23)
1075         {
1076             return result;
1077         }
1078 
1079         str += 3; // parsed hour
1080         const int minute = atoi2(str);
1081         str += 2; // parsed mins
1082 
1083         int sec;
1084         if (str[0] == ':')
1085         {
1086             if (!ascii_isdigit6(str[1]) || !ascii_isdigit(str[2]) || str[3] != _XPLATSTR(' '))
1087             {
1088                 return result;
1089             }
1090 
1091             sec = atoi2(str + 1);
1092             str += 4; // parsed seconds
1093         }
1094         else if (str[0] == _XPLATSTR(' '))
1095         {
1096             sec = 0;
1097             str += 1; // parsed seconds
1098         }
1099         else
1100         {
1101             return result;
1102         }
1103 
1104         if (sec > 60)
1105         { // 60 to allow leap seconds
1106             return result;
1107         }
1108 
1109         year -= 1900;
1110         int daysSince1900 = year * DaysInYear + count_leap_years(year) + yearDay;
1111 
1112         if (parsedWeekday != 7)
1113         {
1114             const int actualWeekday = (daysSince1900 + 1) % 7;
1115 
1116             if (parsedWeekday != actualWeekday)
1117             {
1118                 return result;
1119             }
1120         }
1121 
1122         secondsSince1900 =
1123             static_cast<int64_t>(daysSince1900) * SecondsInDay + hour * SecondsInHour + minute * SecondsInMinute + sec;
1124 
1125         if (!string_starts_with(str, "GMT") && !string_starts_with(str, "UT"))
1126         {
1127             // some timezone adjustment necessary
1128             auto tzCh = _XPLATSTR('-');
1129             int tzHours;
1130             int tzMinutes = 0;
1131             if (string_starts_with(str, "EDT"))
1132             {
1133                 tzHours = 4;
1134             }
1135             else if (string_starts_with(str, "EST") || string_starts_with(str, "CDT"))
1136             {
1137                 tzHours = 5;
1138             }
1139             else if (string_starts_with(str, "CST") || string_starts_with(str, "MDT"))
1140             {
1141                 tzHours = 6;
1142             }
1143             else if (string_starts_with(str, "MST") || string_starts_with(str, "PDT"))
1144             {
1145                 tzHours = 7;
1146             }
1147             else if (string_starts_with(str, "PST"))
1148             {
1149                 tzHours = 8;
1150             }
1151             else if ((str[0] == _XPLATSTR('+') || str[0] == _XPLATSTR('-')) && ascii_isdigit2(str[1]) &&
1152                      ascii_isdigit(str[2]) && ascii_isdigit5(str[3]) && ascii_isdigit(str[4]))
1153             {
1154                 tzCh = str[0];
1155                 tzHours = atoi2(str + 1);
1156                 tzMinutes = atoi2(str + 3);
1157             }
1158             else
1159             {
1160                 return result;
1161             }
1162 
1163             secondsSince1900 = timezone_adjust(secondsSince1900, static_cast<unsigned char>(tzCh), tzHours, tzMinutes);
1164             if (secondsSince1900 < 0)
1165             {
1166                 return result;
1167             }
1168         }
1169     }
1170     else if (format == ISO_8601)
1171     {
1172         // parse year
1173         if (!ascii_isdigit(str[0]) || !ascii_isdigit(str[1]) || !ascii_isdigit(str[2]) || !ascii_isdigit(str[3]))
1174         {
1175             return result;
1176         }
1177 
1178         int year = (str[0] - _XPLATSTR('0')) * 1000 + (str[1] - _XPLATSTR('0')) * 100 + (str[2] - _XPLATSTR('0')) * 10 +
1179                    (str[3] - _XPLATSTR('0'));
1180         if (year < 1900)
1181         {
1182             return result;
1183         }
1184 
1185         str += 4;
1186         if (*str == _XPLATSTR('-'))
1187         {
1188             ++str;
1189         }
1190 
1191         // parse month
1192         if (!ascii_isdigit1(str[0]) || !ascii_isdigit(str[1]))
1193         {
1194             return result;
1195         }
1196 
1197         int month = atoi2(str);
1198         if (month < 1 || month > 12)
1199         {
1200             return result;
1201         }
1202 
1203         month -= 1;
1204         str += 2;
1205 
1206         if (*str == _XPLATSTR('-'))
1207         {
1208             ++str;
1209         }
1210 
1211         // parse day
1212         if (!ascii_isdigit3(str[0]) || !ascii_isdigit(str[1]))
1213         {
1214             return result;
1215         }
1216 
1217         int monthDay = atoi2(str);
1218         if (!validate_day_month(monthDay, month, year))
1219         {
1220             return result;
1221         }
1222 
1223         const int yearDay = get_year_day(month, monthDay, year);
1224 
1225         str += 2;
1226         year -= 1900;
1227         int daysSince1900 = year * DaysInYear + count_leap_years(year) + yearDay;
1228 
1229         if (str[0] != _XPLATSTR('T') && str[0] != _XPLATSTR('t'))
1230         {
1231             // No time
1232             secondsSince1900 = static_cast<int64_t>(daysSince1900) * SecondsInDay;
1233 
1234             result.m_interval =
1235                 static_cast<interval_type>(secondsSince1900 * _secondTicks + fracSec + NtTo1900OffsetInterval);
1236             return result;
1237         }
1238 
1239         ++str; // skip 'T'
1240 
1241         // parse hour
1242         if (!ascii_isdigit2(str[0]) || !ascii_isdigit(str[1]))
1243         {
1244             return result;
1245         }
1246 
1247         const int hour = atoi2(str);
1248         str += 2;
1249         if (hour > 23)
1250         {
1251             return result;
1252         }
1253 
1254         if (*str == _XPLATSTR(':'))
1255         {
1256             ++str;
1257         }
1258 
1259         // parse minute
1260         if (!ascii_isdigit5(str[0]) || !ascii_isdigit(str[1]))
1261         {
1262             return result;
1263         }
1264 
1265         const int minute = atoi2(str);
1266         // minute > 59 is impossible because we checked that the first digit is <= 5 in the basic format
1267         // check above
1268 
1269         str += 2;
1270 
1271         if (*str == _XPLATSTR(':'))
1272         {
1273             ++str;
1274         }
1275 
1276         // parse seconds
1277         if (!ascii_isdigit6(str[0]) || !ascii_isdigit(str[1]))
1278         {
1279             return result;
1280         }
1281 
1282         const int sec = atoi2(str);
1283         // We allow 60 to account for leap seconds
1284         if (sec > 60)
1285         {
1286             return result;
1287         }
1288 
1289         str += 2;
1290         if (str[0] == _XPLATSTR('.') && ascii_isdigit(str[1]))
1291         {
1292             ++str;
1293             int digits = 7;
1294             for (;;)
1295             {
1296                 fracSec *= 10;
1297                 fracSec += *str - _XPLATSTR('0');
1298                 --digits;
1299                 ++str;
1300                 if (digits == 0)
1301                 {
1302                     while (ascii_isdigit(*str))
1303                     {
1304                         // consume remaining fractional second digits we can't use
1305                         ++str;
1306                     }
1307 
1308                     break;
1309                 }
1310 
1311                 if (!ascii_isdigit(*str))
1312                 {
1313                     // no more digits in the input, do the remaining multiplies we need
1314                     for (; digits != 0; --digits)
1315                     {
1316                         fracSec *= 10;
1317                     }
1318 
1319                     break;
1320                 }
1321             }
1322         }
1323 
1324         secondsSince1900 =
1325             static_cast<int64_t>(daysSince1900) * SecondsInDay + hour * SecondsInHour + minute * SecondsInMinute + sec;
1326 
1327         if (str[0] == _XPLATSTR('Z') || str[0] == _XPLATSTR('z'))
1328         {
1329             // no adjustment needed for zulu time
1330         }
1331         else if (str[0] == _XPLATSTR('+') || str[0] == _XPLATSTR('-'))
1332         {
1333             const unsigned char offsetDirection = static_cast<unsigned char>(str[0]);
1334             if (!ascii_isdigit2(str[1]) || !ascii_isdigit(str[2]) || str[3] != _XPLATSTR(':') ||
1335                 !ascii_isdigit5(str[4]) || !ascii_isdigit(str[5]))
1336             {
1337                 return result;
1338             }
1339 
1340             secondsSince1900 = timezone_adjust(secondsSince1900, offsetDirection, atoi2(str + 1), atoi2(str + 4));
1341             if (secondsSince1900 < 0)
1342             {
1343                 return result;
1344             }
1345         }
1346         else
1347         {
1348             // the timezone is malformed, but cpprestsdk currently accepts this as no timezone
1349         }
1350     }
1351     else
1352     {
1353         throw std::invalid_argument("unrecognized date format");
1354     }
1355 
1356     result.m_interval = static_cast<interval_type>(secondsSince1900 * _secondTicks + fracSec + NtTo1900OffsetInterval);
1357     return result;
1358 }
1359 
1360 /// <summary>
1361 /// Converts a timespan/interval in seconds to xml duration string as specified by
1362 /// http://www.w3.org/TR/xmlschema-2/#duration
1363 /// </summary>
seconds_to_xml_duration(utility::seconds durationSecs)1364 utility::string_t __cdecl timespan::seconds_to_xml_duration(utility::seconds durationSecs)
1365 {
1366     auto numSecs = durationSecs.count();
1367 
1368     // Find the number of minutes
1369     auto numMins = numSecs / 60;
1370     if (numMins > 0)
1371     {
1372         numSecs = numSecs % 60;
1373     }
1374 
1375     // Hours
1376     auto numHours = numMins / 60;
1377     if (numHours > 0)
1378     {
1379         numMins = numMins % 60;
1380     }
1381 
1382     // Days
1383     auto numDays = numHours / 24;
1384     if (numDays > 0)
1385     {
1386         numHours = numHours % 24;
1387     }
1388 
1389     // The format is:
1390     // PdaysDThoursHminutesMsecondsS
1391     utility::string_t result;
1392     // (approximate mins/hours/secs as 2 digits each + 1 prefix character) + 1 for P prefix + 1 for T
1393     size_t baseReserveSize = ((numHours > 0) + (numMins > 0) + (numSecs > 0)) * 3 + 1;
1394     if (numDays > 0)
1395     {
1396         utility::string_t daysStr = utility::conversions::details::to_string_t(numDays);
1397         result.reserve(baseReserveSize + daysStr.size() + 1);
1398         result += _XPLATSTR('P');
1399         result += daysStr;
1400         result += _XPLATSTR('D');
1401     }
1402     else
1403     {
1404         result.reserve(baseReserveSize);
1405         result += _XPLATSTR('P');
1406     }
1407 
1408     result += _XPLATSTR('T');
1409 
1410     if (numHours > 0)
1411     {
1412         result += utility::conversions::details::to_string_t(numHours);
1413         result += _XPLATSTR('H');
1414     }
1415 
1416     if (numMins > 0)
1417     {
1418         result += utility::conversions::details::to_string_t(numMins);
1419         result += _XPLATSTR('M');
1420     }
1421 
1422     if (numSecs > 0)
1423     {
1424         result += utility::conversions::details::to_string_t(numSecs);
1425         result += _XPLATSTR('S');
1426     }
1427 
1428     return result;
1429 }
1430 
xml_duration_to_seconds(const utility::string_t & timespanString)1431 utility::seconds __cdecl timespan::xml_duration_to_seconds(const utility::string_t& timespanString)
1432 {
1433     // The format is:
1434     // PnDTnHnMnS
1435     // if n == 0 then the field could be omitted
1436     // The final S could be omitted
1437 
1438     int64_t numSecs = 0;
1439     auto cursor = timespanString.c_str();
1440     auto c = *cursor++; // skip 'P'
1441     while (c)
1442     {
1443         int val = 0;
1444         c = *cursor++;
1445 
1446         while (ascii_isdigit(c))
1447         {
1448             val = val * 10 + (c - _XPLATSTR('0'));
1449             c = *cursor++;
1450 
1451             if (c == _XPLATSTR('.'))
1452             {
1453                 // decimal point is not handled
1454                 do
1455                 {
1456                     c = *cursor++;
1457                 } while (ascii_isdigit(c));
1458             }
1459         }
1460 
1461         if (c == _XPLATSTR('D')) numSecs += val * 24 * 3600; // days
1462         if (c == _XPLATSTR('H')) numSecs += val * 3600;      // Hours
1463         if (c == _XPLATSTR('M')) numSecs += val * 60;        // Minutes
1464         if (c == _XPLATSTR('S') || c == _XPLATSTR('\0'))
1465         {
1466             numSecs += val; // seconds
1467             break;
1468         }
1469     }
1470 
1471     return utility::seconds(numSecs);
1472 }
1473 
1474 static const char c_allowed_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
1475 static const int chars_count = static_cast<int>(sizeof(c_allowed_chars) - 1);
1476 
generate()1477 utility::string_t nonce_generator::generate()
1478 {
1479     std::uniform_int_distribution<> distr(0, chars_count - 1);
1480     utility::string_t result;
1481     result.reserve(length());
1482     std::generate_n(std::back_inserter(result), length(), [&] {
1483         return static_cast<utility::char_t>(c_allowed_chars[distr(m_random)]);
1484     });
1485     return result;
1486 }
1487 
1488 } // namespace utility
1489