From 7357e40d3f54de51176efe54fda6d57028837b8c Mon Sep 17 00:00:00 2001 From: yuyoyuppe Date: Fri, 6 Dec 2019 11:40:23 +0300 Subject: [PATCH] Use WinRT JSON parser instead of custom cpprestsdk solution (#822) --- PowerToys.sln | 9 - deps/cpprestsdk/README.md | 12 - deps/cpprestsdk/cpprestsdk.vcxproj | 121 - .../include/cpprest/asyncrt_utils.h | 697 -- deps/cpprestsdk/include/cpprest/base_uri.h | 391 - .../include/cpprest/details/SafeInt3.hpp | 7482 ----------------- .../include/cpprest/details/basic_types.h | 131 - .../include/cpprest/details/cpprest_compat.h | 91 - .../include/cpprest/details/web_utilities.h | 223 - deps/cpprestsdk/include/cpprest/json.h | 1786 ---- deps/cpprestsdk/include/cpprest/uri.h | 21 - deps/cpprestsdk/include/cpprest/uri_builder.h | 295 - deps/cpprestsdk/include/cpprest/version.h | 10 - deps/cpprestsdk/license.txt | 25 - deps/cpprestsdk/pch.cpp | 1 - deps/cpprestsdk/pch.h | 34 - deps/cpprestsdk/src/json/json.cpp | 475 -- deps/cpprestsdk/src/json/json_parsing.cpp | 1279 --- .../src/json/json_serialization.cpp | 254 - .../src/utilities/asyncrt_utils.cpp | 1490 ---- deps/cpprestsdk/src/utilities/base64.cpp | 260 - .../src/utilities/web_utilities.cpp | 157 - doc/coding/organization.md | 3 - .../UnitTests-CommonLib/Settings.Tests.cpp | 33 +- src/common/common.vcxproj | 7 +- src/common/common.vcxproj.filters | 6 + src/common/json.cpp | 26 + src/common/json.h | 50 + src/common/settings_helpers.cpp | 55 +- src/common/settings_helpers.h | 11 +- src/common/settings_objects.cpp | 328 +- src/common/settings_objects.h | 146 +- src/modules/fancyzones/lib/Settings.cpp | 18 +- src/modules/powerrename/dll/dllmain.cpp | 10 +- src/modules/shortcut_guide/shortcut_guide.cpp | 42 +- src/modules/shortcut_guide/shortcut_guide.h | 4 +- src/runner/general_settings.cpp | 93 +- src/runner/general_settings.h | 7 +- src/runner/powertoy_module.cpp | 9 + src/runner/powertoy_module.h | 3 + src/runner/settings_window.cpp | 95 +- 41 files changed, 488 insertions(+), 15702 deletions(-) delete mode 100644 deps/cpprestsdk/README.md delete mode 100644 deps/cpprestsdk/cpprestsdk.vcxproj delete mode 100644 deps/cpprestsdk/include/cpprest/asyncrt_utils.h delete mode 100644 deps/cpprestsdk/include/cpprest/base_uri.h delete mode 100644 deps/cpprestsdk/include/cpprest/details/SafeInt3.hpp delete mode 100644 deps/cpprestsdk/include/cpprest/details/basic_types.h delete mode 100644 deps/cpprestsdk/include/cpprest/details/cpprest_compat.h delete mode 100644 deps/cpprestsdk/include/cpprest/details/web_utilities.h delete mode 100644 deps/cpprestsdk/include/cpprest/json.h delete mode 100644 deps/cpprestsdk/include/cpprest/uri.h delete mode 100644 deps/cpprestsdk/include/cpprest/uri_builder.h delete mode 100644 deps/cpprestsdk/include/cpprest/version.h delete mode 100644 deps/cpprestsdk/license.txt delete mode 100644 deps/cpprestsdk/pch.cpp delete mode 100644 deps/cpprestsdk/pch.h delete mode 100644 deps/cpprestsdk/src/json/json.cpp delete mode 100644 deps/cpprestsdk/src/json/json_parsing.cpp delete mode 100644 deps/cpprestsdk/src/json/json_serialization.cpp delete mode 100644 deps/cpprestsdk/src/utilities/asyncrt_utils.cpp delete mode 100644 deps/cpprestsdk/src/utilities/base64.cpp delete mode 100644 deps/cpprestsdk/src/utilities/web_utilities.cpp create mode 100644 src/common/json.cpp create mode 100644 src/common/json.h diff --git a/PowerToys.sln b/PowerToys.sln index 931fddeb62..dff6bf1525 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -40,10 +40,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fancyzones", "src\modules\f EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests-FancyZones", "src\modules\fancyzones\tests\UnitTests\UnitTests.vcxproj", "{9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cpprestsdk", "deps\cpprestsdk\cpprestsdk.vcxproj", "{4E577735-DFAB-41AF-8A6E-B6E8872A2928}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deps", "deps", "{1FAF749F-0D6F-4BF5-A563-31A4B5279D27}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "common", "common", "{1AFB6476-670D-4E80-A464-657E01DFF482}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests-CommonLib", "src\common\UnitTests-CommonLib\UnitTests-CommonLib.vcxproj", "{1A066C63-64B3-45F8-92FE-664E1CCE8077}" @@ -114,10 +110,6 @@ Global {9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Debug|x64.Build.0 = Debug|x64 {9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Release|x64.ActiveCfg = Release|x64 {9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Release|x64.Build.0 = Release|x64 - {4E577735-DFAB-41AF-8A6E-B6E8872A2928}.Debug|x64.ActiveCfg = Debug|x64 - {4E577735-DFAB-41AF-8A6E-B6E8872A2928}.Debug|x64.Build.0 = Debug|x64 - {4E577735-DFAB-41AF-8A6E-B6E8872A2928}.Release|x64.ActiveCfg = Release|x64 - {4E577735-DFAB-41AF-8A6E-B6E8872A2928}.Release|x64.Build.0 = Release|x64 {1A066C63-64B3-45F8-92FE-664E1CCE8077}.Debug|x64.ActiveCfg = Debug|x64 {1A066C63-64B3-45F8-92FE-664E1CCE8077}.Debug|x64.Build.0 = Debug|x64 {1A066C63-64B3-45F8-92FE-664E1CCE8077}.Release|x64.ActiveCfg = Release|x64 @@ -159,7 +151,6 @@ Global {F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD} {48804216-2A0E-4168-A6D8-9CD068D14227} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD} {9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD} - {4E577735-DFAB-41AF-8A6E-B6E8872A2928} = {1FAF749F-0D6F-4BF5-A563-31A4B5279D27} {1A066C63-64B3-45F8-92FE-664E1CCE8077} = {1AFB6476-670D-4E80-A464-657E01DFF482} {5CCC8468-DEC8-4D36-99D4-5C891BEBD481} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD} {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} diff --git a/deps/cpprestsdk/README.md b/deps/cpprestsdk/README.md deleted file mode 100644 index e7eed0124e..0000000000 --- a/deps/cpprestsdk/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# C++ Rest SDK - JSON library - -This JSON library is taken from the C++ REST SDK in https://github.com/microsoft/cpprestsdk - -Based in the [v2.10.13 release](https://github.com/microsoft/cpprestsdk/tree/v2.10.13/Release), it consists of the needed files to build and use the JSON classes described in `include/cpprest/json.h`. - -Changes made to the files in order to build in the PowerToys project: -- Removal of `#include` references to files that are not needed. -- `#include "pch.h"` instead of `#include "stdafx.h"` to use the PowerToys pre-compiled header. -- `#define _NO_ASYNCRTIMP` in [`include/cpprest/details/cpprest_compat.h`](./include/cpprest/details/cpprest_compat.h) since this class will be statically linked. - -The contents of the C++ Rest SDK license are included in [license.txt](./license.txt). diff --git a/deps/cpprestsdk/cpprestsdk.vcxproj b/deps/cpprestsdk/cpprestsdk.vcxproj deleted file mode 100644 index 26444752a8..0000000000 --- a/deps/cpprestsdk/cpprestsdk.vcxproj +++ /dev/null @@ -1,121 +0,0 @@ - - - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {4E577735-DFAB-41AF-8A6E-B6E8872A2928} - Win32Proj - common - 10.0 - cpprestsdk - - - - StaticLibrary - true - v142 - Unicode - - - StaticLibrary - false - v142 - true - Unicode - - - - - - - - - - - - - - - true - - - false - - - - Use - Level3 - Disabled - true - _DEBUG;_LIB;%(PreprocessorDefinitions) - true - pch.h - stdcpplatest - MultiThreadedDebug - include;%(AdditionalIncludeDirectories) - - - Windows - true - - - - - Use - Level3 - MaxSpeed - true - true - true - NDEBUG;_LIB;%(PreprocessorDefinitions) - true - stdcpplatest - pch.h - MultiThreaded - include;%(AdditionalIncludeDirectories) - - - Windows - true - true - true - - - - - - - - - - - - - - - - - - - - - - - - Create - Create - - - - - - \ No newline at end of file diff --git a/deps/cpprestsdk/include/cpprest/asyncrt_utils.h b/deps/cpprestsdk/include/cpprest/asyncrt_utils.h deleted file mode 100644 index 19880fa47c..0000000000 --- a/deps/cpprestsdk/include/cpprest/asyncrt_utils.h +++ /dev/null @@ -1,697 +0,0 @@ -/*** - * Copyright (C) Microsoft. All rights reserved. - * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. - * - * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - * - * Various common utilities. - * - * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk - * - * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - ****/ - -#pragma once - -#include "cpprest/details/basic_types.h" -//#include "pplx/pplxtasks.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef _WIN32 -#include -#if !defined(ANDROID) && !defined(__ANDROID__) && defined(HAVE_XLOCALE_H) // CodePlex 269 -/* Systems using glibc: xlocale.h has been removed from glibc 2.26 - The above include of locale.h is sufficient - Further details: https://sourceware.org/git/?p=glibc.git;a=commit;h=f0be25b6336db7492e47d2e8e72eb8af53b5506d */ -#include -#endif -#endif - -/// Various utilities for string conversions and date and time manipulation. -namespace utility -{ -// Left over from VS2010 support, remains to avoid breaking. -typedef std::chrono::seconds seconds; - -/// Functions for converting to/from std::chrono::seconds to xml string. -namespace timespan -{ -/// -/// Converts a timespan/interval in seconds to xml duration string as specified by -/// http://www.w3.org/TR/xmlschema-2/#duration -/// -_ASYNCRTIMP utility::string_t __cdecl seconds_to_xml_duration(utility::seconds numSecs); - -/// -/// Converts an xml duration to timespan/interval in seconds -/// http://www.w3.org/TR/xmlschema-2/#duration -/// -_ASYNCRTIMP utility::seconds __cdecl xml_duration_to_seconds(const utility::string_t& timespanString); -} // namespace timespan - -/// Functions for Unicode string conversions. -namespace conversions -{ -/// -/// Converts a UTF-16 string to a UTF-8 string. -/// -/// A two byte character UTF-16 string. -/// A single byte character UTF-8 string. -_ASYNCRTIMP std::string __cdecl utf16_to_utf8(const utf16string& w); - -/// -/// Converts a UTF-8 string to a UTF-16 -/// -/// A single byte character UTF-8 string. -/// A two byte character UTF-16 string. -_ASYNCRTIMP utf16string __cdecl utf8_to_utf16(const std::string& s); - -/// -/// Converts a ASCII (us-ascii) string to a UTF-16 string. -/// -/// A single byte character us-ascii string. -/// A two byte character UTF-16 string. -_ASYNCRTIMP utf16string __cdecl usascii_to_utf16(const std::string& s); - -/// -/// Converts a Latin1 (iso-8859-1) string to a UTF-16 string. -/// -/// A single byte character UTF-8 string. -/// A two byte character UTF-16 string. -_ASYNCRTIMP utf16string __cdecl latin1_to_utf16(const std::string& s); - -/// -/// Converts a Latin1 (iso-8859-1) string to a UTF-8 string. -/// -/// A single byte character UTF-8 string. -/// A single byte character UTF-8 string. -_ASYNCRTIMP utf8string __cdecl latin1_to_utf8(const std::string& s); - -/// -/// Converts to a platform dependent Unicode string type. -/// -/// A single byte character UTF-8 string. -/// A platform dependent string type. -#ifdef _UTF16_STRINGS -_ASYNCRTIMP utility::string_t __cdecl to_string_t(std::string&& s); -#else -inline utility::string_t&& to_string_t(std::string&& s) { return std::move(s); } -#endif - -/// -/// Converts to a platform dependent Unicode string type. -/// -/// A two byte character UTF-16 string. -/// A platform dependent string type. -#ifdef _UTF16_STRINGS -inline utility::string_t&& to_string_t(utf16string&& s) { return std::move(s); } -#else -_ASYNCRTIMP utility::string_t __cdecl to_string_t(utf16string&& s); -#endif -/// -/// Converts to a platform dependent Unicode string type. -/// -/// A single byte character UTF-8 string. -/// A platform dependent string type. -#ifdef _UTF16_STRINGS -_ASYNCRTIMP utility::string_t __cdecl to_string_t(const std::string& s); -#else -inline const utility::string_t& to_string_t(const std::string& s) { return s; } -#endif - -/// -/// Converts to a platform dependent Unicode string type. -/// -/// A two byte character UTF-16 string. -/// A platform dependent string type. -#ifdef _UTF16_STRINGS -inline const utility::string_t& to_string_t(const utf16string& s) { return s; } -#else -_ASYNCRTIMP utility::string_t __cdecl to_string_t(const utf16string& s); -#endif - -/// -/// Converts to a UTF-16 from string. -/// -/// A single byte character UTF-8 string. -/// A two byte character UTF-16 string. -_ASYNCRTIMP utf16string __cdecl to_utf16string(const std::string& value); - -/// -/// Converts to a UTF-16 from string. -/// -/// A two byte character UTF-16 string. -/// A two byte character UTF-16 string. -inline const utf16string& to_utf16string(const utf16string& value) { return value; } -/// -/// Converts to a UTF-16 from string. -/// -/// A two byte character UTF-16 string. -/// A two byte character UTF-16 string. -inline utf16string&& to_utf16string(utf16string&& value) { return std::move(value); } - -/// -/// Converts to a UTF-8 string. -/// -/// A single byte character UTF-8 string. -/// A single byte character UTF-8 string. -inline std::string&& to_utf8string(std::string&& value) { return std::move(value); } - -/// -/// Converts to a UTF-8 string. -/// -/// A single byte character UTF-8 string. -/// A single byte character UTF-8 string. -inline const std::string& to_utf8string(const std::string& value) { return value; } - -/// -/// Converts to a UTF-8 string. -/// -/// A two byte character UTF-16 string. -/// A single byte character UTF-8 string. -_ASYNCRTIMP std::string __cdecl to_utf8string(const utf16string& value); - -/// -/// Encode the given byte array into a base64 string -/// -_ASYNCRTIMP utility::string_t __cdecl to_base64(const std::vector& data); - -/// -/// Encode the given 8-byte integer into a base64 string -/// -_ASYNCRTIMP utility::string_t __cdecl to_base64(uint64_t data); - -/// -/// Decode the given base64 string to a byte array -/// -_ASYNCRTIMP std::vector __cdecl from_base64(const utility::string_t& str); - -template -CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if " - "locale support is required.") -utility::string_t print_string(const Source& val, const std::locale& loc = std::locale()) -{ - utility::ostringstream_t oss; - oss.imbue(loc); - oss << val; - if (oss.bad()) - { - throw std::bad_cast(); - } - return oss.str(); -} - -CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if " - "locale support is required.") -inline utility::string_t print_string(const utility::string_t& val) { return val; } - -namespace details -{ -#if defined(__ANDROID__) -template -inline std::string to_string(const T t) -{ - std::ostringstream os; - os.imbue(std::locale::classic()); - os << t; - return os.str(); -} -#endif - -template -inline utility::string_t to_string_t(const T t) -{ -#ifdef _UTF16_STRINGS - using std::to_wstring; - return to_wstring(t); -#else -#if !defined(__ANDROID__) - using std::to_string; -#endif - return to_string(t); -#endif -} - -template -utility::string_t print_string(const Source& val) -{ - utility::ostringstream_t oss; - oss.imbue(std::locale::classic()); - oss << val; - if (oss.bad()) - { - throw std::bad_cast(); - } - return oss.str(); -} - -inline const utility::string_t& print_string(const utility::string_t& val) { return val; } - -template -utf8string print_utf8string(const Source& val) -{ - return conversions::to_utf8string(print_string(val)); -} -inline const utf8string& print_utf8string(const utf8string& val) { return val; } - -template -Target scan_string(const utility::string_t& str) -{ - Target t; - utility::istringstream_t iss(str); - iss.imbue(std::locale::classic()); - iss >> t; - if (iss.bad()) - { - throw std::bad_cast(); - } - return t; -} - -inline const utility::string_t& scan_string(const utility::string_t& str) { return str; } -} // namespace details - -template -CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if " - "locale support is required.") -Target scan_string(const utility::string_t& str, const std::locale& loc = std::locale()) -{ - Target t; - utility::istringstream_t iss(str); - iss.imbue(loc); - iss >> t; - if (iss.bad()) - { - throw std::bad_cast(); - } - return t; -} - -CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if " - "locale support is required.") -inline utility::string_t scan_string(const utility::string_t& str) { return str; } -} // namespace conversions - -namespace details -{ -/// -/// Cross platform RAII container for setting thread local locale. -/// -class scoped_c_thread_locale -{ -public: - _ASYNCRTIMP scoped_c_thread_locale(); - _ASYNCRTIMP ~scoped_c_thread_locale(); - -#if !defined(ANDROID) && !defined(__ANDROID__) // CodePlex 269 -#ifdef _WIN32 - typedef _locale_t xplat_locale; -#else - typedef locale_t xplat_locale; -#endif - - static _ASYNCRTIMP xplat_locale __cdecl c_locale(); -#endif -private: -#ifdef _WIN32 - std::string m_prevLocale; - int m_prevThreadSetting; -#elif !(defined(ANDROID) || defined(__ANDROID__)) - locale_t m_prevLocale; -#endif - scoped_c_thread_locale(const scoped_c_thread_locale&); - scoped_c_thread_locale& operator=(const scoped_c_thread_locale&); -}; - -/// -/// Our own implementation of alpha numeric instead of std::isalnum to avoid -/// taking global lock for performance reasons. -/// -inline bool __cdecl is_alnum(const unsigned char uch) CPPREST_NOEXCEPT -{ // test if uch is an alnum character - // special casing char to avoid branches - // clang-format off - static CPPREST_CONSTEXPR bool is_alnum_table[UCHAR_MAX + 1] = { - /* X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 XA XB XC XD XE XF */ - /* 0X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* 1X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* 2X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* 3X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0-9 */ - /* 4X */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A-Z */ - /* 5X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, - /* 6X */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* a-z */ - /* 7X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 - /* non-ASCII values initialized to 0 */ - }; - // clang-format on - return (is_alnum_table[uch]); -} - -/// -/// Our own implementation of alpha numeric instead of std::isalnum to avoid -/// taking global lock for performance reasons. -/// -inline bool __cdecl is_alnum(const char ch) CPPREST_NOEXCEPT { return (is_alnum(static_cast(ch))); } - -/// -/// Our own implementation of alpha numeric instead of std::isalnum to avoid -/// taking global lock for performance reasons. -/// -template -inline bool __cdecl is_alnum(Elem ch) CPPREST_NOEXCEPT -{ - // assumes 'x' == L'x' for the ASCII range - typedef typename std::make_unsigned::type UElem; - const auto uch = static_cast(ch); - return (uch <= static_cast('z') && is_alnum(static_cast(uch))); -} - -/// -/// Simplistic implementation of make_unique. A better implementation would be based on variadic templates -/// and therefore not be compatible with Dev10. -/// -template -std::unique_ptr<_Type> make_unique() -{ - return std::unique_ptr<_Type>(new _Type()); -} - -template -std::unique_ptr<_Type> make_unique(_Arg1&& arg1) -{ - return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1))); -} - -template -std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2) -{ - return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2))); -} - -template -std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3) -{ - return std::unique_ptr<_Type>( - new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3))); -} - -template -std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4) -{ - return std::unique_ptr<_Type>(new _Type( - std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3), std::forward<_Arg4>(arg4))); -} - -template -std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4, _Arg5&& arg5) -{ - return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), - std::forward<_Arg2>(arg2), - std::forward<_Arg3>(arg3), - std::forward<_Arg4>(arg4), - std::forward<_Arg5>(arg5))); -} - -template -std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4, _Arg5&& arg5, _Arg6&& arg6) -{ - return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), - std::forward<_Arg2>(arg2), - std::forward<_Arg3>(arg3), - std::forward<_Arg4>(arg4), - std::forward<_Arg5>(arg5), - std::forward<_Arg6>(arg6))); -} - -/// -/// Cross platform utility function for performing case insensitive string equality comparison. -/// -/// First string to compare. -/// Second strong to compare. -/// true if the strings are equivalent, false otherwise -_ASYNCRTIMP bool __cdecl str_iequal(const std::string& left, const std::string& right) CPPREST_NOEXCEPT; - -/// -/// Cross platform utility function for performing case insensitive string equality comparison. -/// -/// First string to compare. -/// Second strong to compare. -/// true if the strings are equivalent, false otherwise -_ASYNCRTIMP bool __cdecl str_iequal(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT; - -/// -/// Cross platform utility function for performing case insensitive string less-than comparison. -/// -/// First string to compare. -/// Second strong to compare. -/// true if a lowercase view of left is lexicographically less than a lowercase view of right; otherwise, -/// false. -_ASYNCRTIMP bool __cdecl str_iless(const std::string& left, const std::string& right) CPPREST_NOEXCEPT; - -/// -/// Cross platform utility function for performing case insensitive string less-than comparison. -/// -/// First string to compare. -/// Second strong to compare. -/// true if a lowercase view of left is lexicographically less than a lowercase view of right; otherwise, -/// false. -_ASYNCRTIMP bool __cdecl str_iless(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT; - -/// -/// Convert a string to lowercase in place. -/// -/// The string to convert to lowercase. -_ASYNCRTIMP void __cdecl inplace_tolower(std::string& target) CPPREST_NOEXCEPT; - -/// -/// Convert a string to lowercase in place. -/// -/// The string to convert to lowercase. -_ASYNCRTIMP void __cdecl inplace_tolower(std::wstring& target) CPPREST_NOEXCEPT; - -#ifdef _WIN32 - -/// -/// Category error type for Windows OS errors. -/// -class windows_category_impl : public std::error_category -{ -public: - virtual const char* name() const CPPREST_NOEXCEPT { return "windows"; } - - virtual std::string message(int errorCode) const CPPREST_NOEXCEPT; - - virtual std::error_condition default_error_condition(int errorCode) const CPPREST_NOEXCEPT; -}; - -/// -/// Gets the one global instance of the windows error category. -/// -/// An error category instance. -_ASYNCRTIMP const std::error_category& __cdecl windows_category(); - -#else - -/// -/// Gets the one global instance of the linux error category. -/// -/// An error category instance. -_ASYNCRTIMP const std::error_category& __cdecl linux_category(); - -#endif - -/// -/// Gets the one global instance of the current platform's error category. -/// -_ASYNCRTIMP const std::error_category& __cdecl platform_category(); - -/// -/// Creates an instance of std::system_error from a OS error code. -/// -inline std::system_error __cdecl create_system_error(unsigned long errorCode) -{ - std::error_code code((int)errorCode, platform_category()); - return std::system_error(code, code.message()); -} - -/// -/// Creates a std::error_code from a OS error code. -/// -inline std::error_code __cdecl create_error_code(unsigned long errorCode) -{ - return std::error_code((int)errorCode, platform_category()); -} - -/// -/// Creates the corresponding error message from a OS error code. -/// -inline utility::string_t __cdecl create_error_message(unsigned long errorCode) -{ - return utility::conversions::to_string_t(create_error_code(errorCode).message()); -} - -} // namespace details - -class datetime -{ -public: - typedef uint64_t interval_type; - - /// - /// Defines the supported date and time string formats. - /// - enum date_format - { - RFC_1123, - ISO_8601 - }; - - /// - /// Returns the current UTC time. - /// - static _ASYNCRTIMP datetime __cdecl utc_now(); - - /// - /// An invalid UTC timestamp value. - /// - enum : interval_type - { - utc_timestamp_invalid = static_cast(-1) - }; - - /// - /// Returns seconds since Unix/POSIX time epoch at 01-01-1970 00:00:00. - /// If time is before epoch, utc_timestamp_invalid is returned. - /// - static interval_type utc_timestamp() - { - const auto seconds = utc_now().to_interval() / _secondTicks; - if (seconds >= 11644473600LL) - { - return seconds - 11644473600LL; - } - else - { - return utc_timestamp_invalid; - } - } - - datetime() : m_interval(0) {} - - /// - /// Creates datetime from a string representing time in UTC in RFC 1123 format. - /// - /// Returns a datetime of zero if not successful. - static _ASYNCRTIMP datetime __cdecl from_string(const utility::string_t& timestring, date_format format = RFC_1123); - - /// - /// Returns a string representation of the datetime. - /// - _ASYNCRTIMP utility::string_t to_string(date_format format = RFC_1123) const; - - /// - /// Returns the integral time value. - /// - interval_type to_interval() const { return m_interval; } - - datetime operator-(interval_type value) const { return datetime(m_interval - value); } - - datetime operator+(interval_type value) const { return datetime(m_interval + value); } - - bool operator==(datetime dt) const { return m_interval == dt.m_interval; } - - bool operator!=(const datetime& dt) const { return !(*this == dt); } - - static interval_type from_milliseconds(unsigned int milliseconds) { return milliseconds * _msTicks; } - - static interval_type from_seconds(unsigned int seconds) { return seconds * _secondTicks; } - - static interval_type from_minutes(unsigned int minutes) { return minutes * _minuteTicks; } - - static interval_type from_hours(unsigned int hours) { return hours * _hourTicks; } - - static interval_type from_days(unsigned int days) { return days * _dayTicks; } - - bool is_initialized() const { return m_interval != 0; } - -private: - friend int operator-(datetime t1, datetime t2); - - static const interval_type _msTicks = static_cast(10000); - static const interval_type _secondTicks = 1000 * _msTicks; - static const interval_type _minuteTicks = 60 * _secondTicks; - static const interval_type _hourTicks = 60 * 60 * _secondTicks; - static const interval_type _dayTicks = 24 * 60 * 60 * _secondTicks; - - // Private constructor. Use static methods to create an instance. - datetime(interval_type interval) : m_interval(interval) {} - - // Storing as hundreds of nanoseconds 10e-7, i.e. 1 here equals 100ns. - interval_type m_interval; -}; - -inline int operator-(datetime t1, datetime t2) -{ - auto diff = (t1.m_interval - t2.m_interval); - - // Round it down to seconds - diff /= 10 * 1000 * 1000; - - return static_cast(diff); -} - -/// -/// Nonce string generator class. -/// -class nonce_generator -{ -public: - /// - /// Define default nonce length. - /// - enum - { - default_length = 32 - }; - - /// - /// Nonce generator constructor. - /// - /// Length of the generated nonce string. - nonce_generator(int length = default_length) - : m_random(static_cast(utility::datetime::utc_timestamp())), m_length(length) - { - } - - /// - /// Generate a nonce string containing random alphanumeric characters (A-Za-z0-9). - /// Length of the generated string is set by length(). - /// - /// The generated nonce string. - _ASYNCRTIMP utility::string_t generate(); - - /// - /// Get length of generated nonce string. - /// - /// Nonce string length. - int length() const { return m_length; } - - /// - /// Set length of the generated nonce string. - /// - /// Lenght of nonce string. - void set_length(int length) { m_length = length; } - -private: - std::mt19937 m_random; - int m_length; -}; - -} // namespace utility diff --git a/deps/cpprestsdk/include/cpprest/base_uri.h b/deps/cpprestsdk/include/cpprest/base_uri.h deleted file mode 100644 index 7c6943119c..0000000000 --- a/deps/cpprestsdk/include/cpprest/base_uri.h +++ /dev/null @@ -1,391 +0,0 @@ -/*** - * Copyright (C) Microsoft. All rights reserved. - * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. - * - * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - * - * Protocol independent support for URIs. - * - * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk - * - * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - ****/ - -#pragma once - -#include "cpprest/asyncrt_utils.h" -#include "cpprest/details/basic_types.h" -#include -#include -#include -#include - -namespace web -{ -namespace details -{ -struct uri_components -{ - uri_components() : m_path(_XPLATSTR("/")), m_port(-1) {} - - uri_components(const uri_components&) = default; - uri_components& operator=(const uri_components&) = default; - - // This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped. - uri_components(uri_components&& other) CPPREST_NOEXCEPT : m_scheme(std::move(other.m_scheme)), - m_host(std::move(other.m_host)), - m_user_info(std::move(other.m_user_info)), - m_path(std::move(other.m_path)), - m_query(std::move(other.m_query)), - m_fragment(std::move(other.m_fragment)), - m_port(other.m_port) - { - } - - // This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped. - uri_components& operator=(uri_components&& other) CPPREST_NOEXCEPT - { - if (this != &other) - { - m_scheme = std::move(other.m_scheme); - m_host = std::move(other.m_host); - m_user_info = std::move(other.m_user_info); - m_path = std::move(other.m_path); - m_query = std::move(other.m_query); - m_fragment = std::move(other.m_fragment); - m_port = other.m_port; - } - return *this; - } - - _ASYNCRTIMP utility::string_t join(); - - utility::string_t m_scheme; - utility::string_t m_host; - utility::string_t m_user_info; - utility::string_t m_path; - utility::string_t m_query; - utility::string_t m_fragment; - int m_port; -}; -} // namespace details - -/// -/// A single exception type to represent errors in parsing, encoding, and decoding URIs. -/// -class uri_exception : public std::exception -{ -public: - uri_exception(std::string msg) : m_msg(std::move(msg)) {} - - ~uri_exception() CPPREST_NOEXCEPT {} - - const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); } - -private: - std::string m_msg; -}; - -/// -/// A flexible, protocol independent URI implementation. -/// -/// URI instances are immutable. Querying the various fields on an empty URI will return empty strings. Querying -/// various diagnostic members on an empty URI will return false. -/// -/// -/// This implementation accepts both URIs ('http://msn.com/path') and URI relative-references -/// ('/path?query#frag'). -/// -/// This implementation does not provide any scheme-specific handling -- an example of this -/// would be the following: 'http://path1/path'. This is a valid URI, but it's not a valid -/// http-uri -- that is, it's syntactically correct but does not conform to the requirements -/// of the http scheme (http requires a host). -/// We could provide this by allowing a pluggable 'scheme' policy-class, which would provide -/// extra capability for validating and canonicalizing a URI according to scheme, and would -/// introduce a layer of type-safety for URIs of differing schemes, and thus differing semantics. -/// -/// One issue with implementing a scheme-independent URI facility is that of comparing for equality. -/// For instance, these URIs are considered equal 'http://msn.com', 'http://msn.com:80'. That is -- -/// the 'default' port can be either omitted or explicit. Since we don't have a way to map a scheme -/// to it's default port, we don't have a way to know these are equal. This is just one of a class of -/// issues with regard to scheme-specific behavior. -/// -class uri -{ -public: - /// - /// The various components of a URI. This enum is used to indicate which - /// URI component is being encoded to the encode_uri_component. This allows - /// specific encoding to be performed. - /// - /// Scheme and port don't allow '%' so they don't need to be encoded. - /// - class components - { - public: - enum component - { - user_info, - host, - path, - query, - fragment, - full_uri - }; - }; - - /// - /// Encodes a URI component according to RFC 3986. - /// Note if a full URI is specified instead of an individual URI component all - /// characters not in the unreserved set are escaped. - /// - /// The URI as a string. - /// The encoded string. - _ASYNCRTIMP static utility::string_t __cdecl encode_uri(const utility::string_t& raw, - uri::components::component = components::full_uri); - - /// - /// Encodes a string by converting all characters except for RFC 3986 unreserved characters to their - /// hexadecimal representation. - /// - /// The encoded string. - _ASYNCRTIMP static utility::string_t __cdecl encode_data_string(const utility::string_t& data); - - /// - /// Decodes an encoded string. - /// - /// The URI as a string. - /// The decoded string. - _ASYNCRTIMP static utility::string_t __cdecl decode(const utility::string_t& encoded); - - /// - /// Splits a path into its hierarchical components. - /// - /// The path as a string - /// A std::vector<utility::string_t> containing the segments in the path. - _ASYNCRTIMP static std::vector __cdecl split_path(const utility::string_t& path); - - /// - /// Splits a query into its key-value components. - /// - /// The query string - /// A std::map<utility::string_t, utility::string_t> containing the key-value components of - /// the query. - _ASYNCRTIMP static std::map __cdecl split_query( - const utility::string_t& query); - - /// - /// Validates a string as a URI. - /// - /// - /// This function accepts both uris ('http://msn.com') and uri relative-references ('path1/path2?query'). - /// - /// The URI string to be validated. - /// true if the given string represents a valid URI, false otherwise. - _ASYNCRTIMP static bool __cdecl validate(const utility::string_t& uri_string); - - /// - /// Creates an empty uri - /// - uri() : m_uri(_XPLATSTR("/")) {} - - /// - /// Creates a URI from the given encoded string. This will throw an exception if the string - /// does not contain a valid URI. Use uri::validate if processing user-input. - /// - /// A pointer to an encoded string to create the URI instance. - _ASYNCRTIMP uri(const utility::char_t* uri_string); - - /// - /// Creates a URI from the given encoded string. This will throw an exception if the string - /// does not contain a valid URI. Use uri::validate if processing user-input. - /// - /// An encoded URI string to create the URI instance. - _ASYNCRTIMP uri(const utility::string_t& uri_string); - - /// - /// Copy constructor. - /// - uri(const uri&) = default; - - /// - /// Copy assignment operator. - /// - uri& operator=(const uri&) = default; - - /// - /// Move constructor. - /// - // This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped. - uri(uri&& other) CPPREST_NOEXCEPT : m_uri(std::move(other.m_uri)), m_components(std::move(other.m_components)) {} - - /// - /// Move assignment operator - /// - // This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped. - uri& operator=(uri&& other) CPPREST_NOEXCEPT - { - if (this != &other) - { - m_uri = std::move(other.m_uri); - m_components = std::move(other.m_components); - } - return *this; - } - - /// - /// Get the scheme component of the URI as an encoded string. - /// - /// The URI scheme as a string. - const utility::string_t& scheme() const { return m_components.m_scheme; } - - /// - /// Get the user information component of the URI as an encoded string. - /// - /// The URI user information as a string. - const utility::string_t& user_info() const { return m_components.m_user_info; } - - /// - /// Get the host component of the URI as an encoded string. - /// - /// The URI host as a string. - const utility::string_t& host() const { return m_components.m_host; } - - /// - /// Get the port component of the URI. Returns -1 if no port is specified. - /// - /// The URI port as an integer. - int port() const { return m_components.m_port; } - - /// - /// Get the path component of the URI as an encoded string. - /// - /// The URI path as a string. - const utility::string_t& path() const { return m_components.m_path; } - - /// - /// Get the query component of the URI as an encoded string. - /// - /// The URI query as a string. - const utility::string_t& query() const { return m_components.m_query; } - - /// - /// Get the fragment component of the URI as an encoded string. - /// - /// The URI fragment as a string. - const utility::string_t& fragment() const { return m_components.m_fragment; } - - /// - /// Creates a new uri object with the same authority portion as this one, omitting the resource and query portions. - /// - /// The new uri object with the same authority. - _ASYNCRTIMP uri authority() const; - - /// - /// Gets the path, query, and fragment portion of this uri, which may be empty. - /// - /// The new URI object with the path, query and fragment portion of this URI. - _ASYNCRTIMP uri resource() const; - - /// - /// An empty URI specifies no components, and serves as a default value - /// - bool is_empty() const { return this->m_uri.empty() || this->m_uri == _XPLATSTR("/"); } - - /// - /// A loopback URI is one which refers to a hostname or ip address with meaning only on the local machine. - /// - /// - /// Examples include "localhost", or ip addresses in the loopback range (127.0.0.0/24). - /// - /// true if this URI references the local host, false otherwise. - bool is_host_loopback() const - { - return !is_empty() && - ((host() == _XPLATSTR("localhost")) || (host().size() > 4 && host().substr(0, 4) == _XPLATSTR("127."))); - } - - /// - /// A wildcard URI is one which refers to all hostnames that resolve to the local machine (using the * or +) - /// - /// - /// http://*:80 - /// - bool is_host_wildcard() const - { - return !is_empty() && (this->host() == _XPLATSTR("*") || this->host() == _XPLATSTR("+")); - } - - /// - /// A portable URI is one with a hostname that can be resolved globally (used from another machine). - /// - /// true if this URI can be resolved globally (used from another machine), false - /// otherwise. The hostname "localhost" is a reserved name that is guaranteed to resolve to the - /// local machine, and cannot be used for inter-machine communication. Likewise the hostnames "*" and "+" on Windows - /// represent wildcards, and do not map to a resolvable address. - /// - bool is_host_portable() const { return !(is_empty() || is_host_loopback() || is_host_wildcard()); } - - /// - /// A default port is one where the port is unspecified, and will be determined by the operating system. - /// The choice of default port may be dictated by the scheme (http -> 80) or not. - /// - /// true if this URI instance has a default port, false otherwise. - bool is_port_default() const { return !is_empty() && this->port() == 0; } - - /// - /// An "authority" URI is one with only a scheme, optional userinfo, hostname, and (optional) port. - /// - /// true if this is an "authority" URI, false otherwise. - bool is_authority() const { return !is_empty() && is_path_empty() && query().empty() && fragment().empty(); } - - /// - /// Returns whether the other URI has the same authority as this one - /// - /// The URI to compare the authority with. - /// true if both the URI's have the same authority, false otherwise. - bool has_same_authority(const uri& other) const { return !is_empty() && this->authority() == other.authority(); } - - /// - /// Returns whether the path portion of this URI is empty - /// - /// true if the path portion of this URI is empty, false otherwise. - bool is_path_empty() const { return path().empty() || path() == _XPLATSTR("/"); } - - /// - /// Returns the full (encoded) URI as a string. - /// - /// The full encoded URI string. - utility::string_t to_string() const { return m_uri; } - - /// - /// Returns an URI resolved against this as the base URI - /// according to RFC3986, Section 5 (https://tools.ietf.org/html/rfc3986#section-5). - /// - /// The relative URI to be resolved against this as base. - /// The new resolved URI string. - _ASYNCRTIMP utility::string_t resolve_uri(const utility::string_t& relativeUri) const; - - _ASYNCRTIMP bool operator==(const uri& other) const; - - bool operator<(const uri& other) const { return m_uri < other.m_uri; } - - bool operator!=(const uri& other) const { return !(this->operator==(other)); } - -private: - friend class uri_builder; - - /// - /// Creates a URI from the given URI components. - /// - /// A URI components object to create the URI instance. - _ASYNCRTIMP uri(const details::uri_components& components); - - // Used by uri_builder - static utility::string_t __cdecl encode_query_impl(const utf8string& raw); - - utility::string_t m_uri; - details::uri_components m_components; -}; - -} // namespace web diff --git a/deps/cpprestsdk/include/cpprest/details/SafeInt3.hpp b/deps/cpprestsdk/include/cpprest/details/SafeInt3.hpp deleted file mode 100644 index 0a9dbdd76a..0000000000 --- a/deps/cpprestsdk/include/cpprest/details/SafeInt3.hpp +++ /dev/null @@ -1,7482 +0,0 @@ -/*----------------------------------------------------------------------------------------------------------- -SafeInt.hpp -Version 3.0.18p - -This software is licensed under the Microsoft Public License (Ms-PL). -For more information about Microsoft open source licenses, refer to -http://www.microsoft.com/opensource/licenses.mspx - -This license governs use of the accompanying software. If you use the software, you accept this license. -If you do not accept the license, do not use the software. - -Definitions -The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here -as under U.S. copyright law. A "contribution" is the original software, or any additions or changes to -the software. A "contributor" is any person that distributes its contribution under this license. -"Licensed patents" are a contributor's patent claims that read directly on its contribution. - -Grant of Rights -(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations -in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to -reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution -or any derivative works that you create. - -(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in -section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed -patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution -in the software or derivative works of the contribution in the software. - -Conditions and Limitations -(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, - or trademarks. -(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the - software, your patent license from such contributor to the software ends automatically. -(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and - attribution notices that are present in the software. -(D) If you distribute any portion of the software in source code form, you may do so only under this license - by including a complete copy of this license with your distribution. If you distribute any portion of the - software in compiled or object code form, you may only do so under a license that complies with this license. -(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, - guarantees, or conditions. You may have additional consumer rights under your local laws which this license - cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties - of merchantability, fitness for a particular purpose and non-infringement. - - -Copyright (c) Microsoft Corporation. All rights reserved. - -This header implements an integer handling class designed to catch -unsafe integer operations - -This header compiles properly at Wall on Visual Studio, -Wall on gcc, and -Weverything on clang. - -Please read the leading comments before using the class. ----------------------------------------------------------------*/ -#pragma once - -// It is a bit tricky to sort out what compiler we are actually using, -// do this once here, and avoid cluttering the code -#define VISUAL_STUDIO_COMPILER 0 -#define CLANG_COMPILER 1 -#define GCC_COMPILER 2 -#define UNKNOWN_COMPILER -1 - -// Clang will sometimes pretend to be Visual Studio -// and does pretend to be gcc. Check it first, as nothing else pretends to be clang -#if defined __clang__ -#define SAFEINT_COMPILER CLANG_COMPILER -#elif defined __GNUC__ -#define SAFEINT_COMPILER GCC_COMPILER -#elif defined _MSC_VER -#define SAFEINT_COMPILER VISUAL_STUDIO_COMPILER -#else -#define SAFEINT_COMPILER UNKNOWN_COMPILER -#endif - -// Enable compiling with /Wall under VC -#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER -#pragma warning(push) -// Disable warnings coming from headers -#pragma warning(disable : 4987 4820 4987 4820) - -#endif - -// Need this for ptrdiff_t on some compilers -#include -#include - -#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER && defined _M_AMD64 -#include -#define SAFEINT_USE_INTRINSICS 1 -#else -#define SAFEINT_USE_INTRINSICS 0 -#endif - -#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER -#pragma warning(pop) -#endif - -// Various things needed for GCC -#if SAFEINT_COMPILER == GCC_COMPILER || SAFEINT_COMPILER == CLANG_COMPILER - -#define NEEDS_INT_DEFINED - -#if !defined NULL -#define NULL 0 -#endif - -// GCC warning suppression -#if SAFEINT_COMPILER == GCC_COMPILER -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-local-typedefs" -#endif - -#include - -// clang only -#if SAFEINT_COMPILER == CLANG_COMPILER - -#if __has_feature(cxx_nullptr) -#define NEEDS_NULLPTR_DEFINED 0 -#endif - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wc++11-long-long" -#pragma clang diagnostic ignored "-Wold-style-cast" -#pragma clang diagnostic ignored "-Wunused-local-typedef" -#endif - -#endif - -// If the user made a choice, respect it #if !defined -#if !defined NEEDS_NULLPTR_DEFINED -// Visual Studio 2010 and higher support this -#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER -#if (_MSC_VER < 1600) -#define NEEDS_NULLPTR_DEFINED 1 -#else -#define NEEDS_NULLPTR_DEFINED 0 -#endif -#else -// Let everything else trigger based on whether we use c++11 or above -#if __cplusplus >= 201103L -#define NEEDS_NULLPTR_DEFINED 0 -#else -#define NEEDS_NULLPTR_DEFINED 1 -#endif -#endif -#endif - -#if NEEDS_NULLPTR_DEFINED -#define nullptr NULL -#endif - -#ifndef C_ASSERT -#define C_ASSERT_DEFINED_SAFEINT -#define C_ASSERT(e) typedef char __C_ASSERT__[(e) ? 1 : -1] -#endif - -// Let's test some assumptions -// We're assuming two's complement negative numbers -C_ASSERT(-1 == static_cast(0xffffffff)); - -/************* Compiler Options -***************************************************************************************************** - -SafeInt supports several compile-time options that can change the behavior of the class. - -Compiler options: -SAFEINT_WARN_64BIT_PORTABILITY - this re-enables various warnings that happen when /Wp64 is used. Enabling this -option is not recommended. NEEDS_INT_DEFINED - if your compiler does not support __int8, __int16, -__int32 and __int64, you can enable this. SAFEINT_ASSERT_ON_EXCEPTION - it is often easier to stop on an assert -and figure out a problem than to try and figure out how you landed in the catch block. SafeIntDefaultExceptionHandler - -if you'd like to replace the exception handlers SafeInt provides, define your replacement and define this. Note - two -built in (Windows-specific) options exist: - - SAFEINT_FAILFAST - bypasses all exception handlers, exits the app with an -exception - - SAFEINT_RAISE_EXCEPTION - throws Win32 exceptions, which can be caught -SAFEINT_DISALLOW_UNSIGNED_NEGATION - Invoking the unary negation operator creates warnings, but if you'd like it to -completely fail to compile, define this. ANSI_CONVERSIONS - This changes the class to use default -comparison behavior, which may be unsafe. Enabling this option is not recommended. SAFEINT_DISABLE_BINARY_ASSERT - -binary AND, OR or XOR operations on mixed size types can produce unexpected results. If you do this, the default is to -assert. Set this if you prefer not to assert under these conditions. SIZE_T_CAST_NEEDED - some compilers -complain if there is not a cast to size_t, others complain if there is one. This lets you not have your compiler -complain. SAFEINT_DISABLE_SHIFT_ASSERT - Set this option if you don't want to assert when shifting more bits than -the type has. Enabling this option is not recommended. - -************************************************************************************************************************************/ - -/* -* The SafeInt class is designed to have as low an overhead as possible -* while still ensuring that all integer operations are conducted safely. -* Nearly every operator has been overloaded, with a very few exceptions. -* -* A usability-safety trade-off has been made to help ensure safety. This -* requires that every operation return either a SafeInt or a bool. If we -* allowed an operator to return a base integer type T, then the following -* can happen: -* -* char i = SafeInt(32) * 2 + SafeInt(16) * 4; -* -* The * operators take precedence, get overloaded, return a char, and then -* you have: -* -* char i = (char)64 + (char)64; //overflow! -* -* This situation would mean that safety would depend on usage, which isn't -* acceptable. -* -* One key operator that is missing is an implicit cast to type T. The reason for -* this is that if there is an implicit cast operator, then we end up with -* an ambiguous compile-time precedence. Because of this amiguity, there -* are two methods that are provided: -* -* Casting operators for every native integer type -* Version 3 note - it now compiles correctly for size_t without warnings -* -* SafeInt::Ptr() - returns the address of the internal integer -* Note - the '&' (address of) operator has been overloaded and returns -* the address of the internal integer. -* -* The SafeInt class should be used in any circumstances where ensuring -* integrity of the calculations is more important than performance. See Performance -* Notes below for additional information. -* -* Many of the conditionals will optimize out or be inlined for a release -* build (especially with /Ox), but it does have significantly more overhead, -* especially for signed numbers. If you do not _require_ negative numbers, use -* unsigned integer types - certain types of problems cannot occur, and this class -* performs most efficiently. -* -* Here's an example of when the class should ideally be used - -* -* void* AllocateMemForStructs(int StructSize, int HowMany) -* { -* SafeInt s(StructSize); -* -* s *= HowMany; -* -* return malloc(s); -* -* } -* -* Here's when it should NOT be used: -* -* void foo() -* { -* int i; -* -* for(i = 0; i < 0xffff; i++) -* .... -* } -* -* Error handling - a SafeInt class will throw exceptions if something -* objectionable happens. The exceptions are SafeIntException classes, -* which contain an enum as a code. -* -* Typical usage might be: -* -* bool foo() -* { -* SafeInt s; //note that s == 0 unless set -* -* try{ -* s *= 23; -* .... -* } -* catch(SafeIntException err) -* { -* //handle errors here -* } -* } -* -* Update for 3.0 - the exception class is now a template parameter. -* You can replace the exception class with any exception class you like. This is accomplished by: -* 1) Create a class that has the following interface: -* - template <> class YourSafeIntExceptionHandler < YourException > - { - public: - static __declspec(noreturn) void __stdcall SafeIntOnOverflow() - { - throw YourException( YourSafeIntArithmeticOverflowError ); - } - - static __declspec(noreturn) void __stdcall SafeIntOnDivZero() - { - throw YourException( YourSafeIntDivideByZeroError ); - } - }; -* -* Note that you don't have to throw C++ exceptions, you can throw Win32 exceptions, or do -* anything you like, just don't return from the call back into the code. -* -* 2) Either explicitly declare SafeInts like so: -* SafeInt< int, YourSafeIntExceptionHandler > si; -* or -* #define SafeIntDefaultExceptionHandler YourSafeIntExceptionHandler -* -* Performance: -* -* Due to the highly nested nature of this class, you can expect relatively poor -* performance in unoptimized code. In tests of optimized code vs. correct inline checks -* in native code, this class has been found to take approximately 8% more CPU time (this varies), -* most of which is due to exception handling. Solutions: -* -* 1) Compile optimized code - /Ox is best, /O2 also performs well. Interestingly, /O1 -* (optimize for size) does not work as well. -* 2) If that 8% hit is really a serious problem, walk through the code and inline the -* exact same checks as the class uses. -* 3) Some operations are more difficult than others - avoid using signed integers, and if -* possible keep them all the same size. 64-bit integers are also expensive. Mixing -* different integer sizes and types may prove expensive. Be aware that literals are -* actually ints. For best performance, cast literals to the type desired. -* -* -* Performance update -* The current version of SafeInt uses template specialization to force the compiler to invoke only the -* operator implementation needed for any given pair of types. This will dramatically improve the perf -* of debug builds. -* -* 3.0 update - not only have we maintained the specialization, there were some cases that were overly complex, -* and using some additional cases (e.g. signed __int64 and unsigned __int64) resulted in some simplification. -* Additionally, there was a lot of work done to better optimize the 64-bit multiplication. -* -* Binary Operators -* -* All of the binary operators have certain assumptions built into the class design. -* This is to ensure correctness. Notes on each class of operator follow: -* -* Arithmetic Operators (*,/,+,-,%) -* There are three possible variants: -* SafeInt< T, E > op SafeInt< T, E > -* SafeInt< T, E > op U -* U op SafeInt< T, E > -* -* The SafeInt< T, E > op SafeInt< U, E > variant is explicitly not supported, and if you try to do -* this the compiler with throw the following error: -* -* error C2593: 'operator *' is ambiguous -* -* This is because the arithmetic operators are required to return a SafeInt of some type. -* The compiler cannot know whether you'd prefer to get a type T or a type U returned. If -* you need to do this, you need to extract the value contained within one of the two using -* the casting operator. For example: -* -* SafeInt< T, E > t, result; -* SafeInt< U, E > u; -* -* result = t * (U)u; -* -* Comparison Operators -* Because each of these operators return type bool, mixing SafeInts of differing types is -* allowed. -* -* Shift Operators -* Shift operators always return the type on the left hand side of the operator. Mixed type -* operations are allowed because the return type is always known. -* -* Boolean Operators -* Like comparison operators, these overloads always return type bool, and mixed-type SafeInts -* are allowed. Additionally, specific overloads exist for type bool on both sides of the -* operator. -* -* Binary Operators -* Mixed-type operations are discouraged, however some provision has been made in order to -* enable things like: -* -* SafeInt c = 2; -* -* if(c & 0x02) -* ... -* -* The "0x02" is actually an int, and it needs to work. -* In the case of binary operations on integers smaller than 32-bit, or of mixed type, corner -* cases do exist where you could get unexpected results. In any case where SafeInt returns a different -* result than the underlying operator, it will call assert(). You should examine your code and cast things -* properly so that you are not programming with side effects. -* -* Documented issues: -* -* This header compiles correctly at /W4 using VC++ 8 (Version 14.00.50727.42) and later. -* As of this writing, I believe it will also work for VC 7.1, but not for VC 7.0 or below. -* If you need a version that will work with lower level compilers, try version 1.0.7. None -* of them work with Visual C++ 6, and gcc didn't work very well, either, though this hasn't -* been tried recently. -* -* It is strongly recommended that any code doing integer manipulation be compiled at /W4 -* - there are a number of warnings which pertain to integer manipulation enabled that are -* not enabled at /W3 (default for VC++) -* -* Perf note - postfix operators are slightly more costly than prefix operators. -* Unless you're actually assigning it to something, ++SafeInt is less expensive than SafeInt++ -* -* The comparison operator behavior in this class varies from the ANSI definition, which is -* arguably broken. As an example, consider the following: -* -* unsigned int l = 0xffffffff; -* char c = -1; -* -* if(c == l) -* printf("Why is -1 equal to 4 billion???\n"); -* -* The problem here is that c gets cast to an int, now has a value of 0xffffffff, and then gets -* cast again to an unsigned int, losing the true value. This behavior is despite the fact that -* an __int64 exists, and the following code will yield a different (and intuitively correct) -* answer: -* -* if((__int64)c == (__int64)l)) -* printf("Why is -1 equal to 4 billion???\n"); -* else -* printf("Why doesn't the compiler upcast to 64-bits when needed?\n"); -* -* Note that combinations with smaller integers won't display the problem - if you -* changed "unsigned int" above to "unsigned short", you'd get the right answer. -* -* If you prefer to retain the ANSI standard behavior insert -* #define ANSI_CONVERSIONS -* into your source. Behavior differences occur in the following cases: -* 8, 16, and 32-bit signed int, unsigned 32-bit int -* any signed int, unsigned 64-bit int -* Note - the signed int must be negative to show the problem -* -* -* Revision history: -* -* Oct 12, 2003 - Created -* Author - David LeBlanc - dleblanc@microsoft.com -* -* Oct 27, 2003 - fixed numerous items pointed out by michmarc and bdawson -* Dec 28, 2003 - 1.0 -* added support for mixed-type operations -* thanks to vikramh -* also fixed broken __int64 multiplication section -* added extended support for mixed-type operations where possible -* Jan 28, 2004 - 1.0.1 -* changed WCHAR to wchar_t -* fixed a construct in two mixed-type assignment overloads that was -* not compiling on some compilers -* Also changed name of private method to comply with standards on -* reserved names -* Thanks to Niels Dekker for the input -* Feb 12, 2004 - 1.0.2 -* Minor changes to remove dependency on Windows headers -* Consistently used __int16, __int32 and __int64 to ensure -* portability -* May 10, 2004 - 1.0.3 -* Corrected bug in one case of GreaterThan -* July 22, 2004 - 1.0.4 -* Tightened logic in addition check (saving 2 instructions) -* Pulled error handler out into function to enable user-defined replacement -* Made internal type of SafeIntException an enum (as per Niels' suggestion) -* Added casts for base integer types (as per Scott Meyers' suggestion) -* Updated usage information - see important new perf notes. -* Cleaned up several const issues (more thanks to Niels) -* -* Oct 1, 2004 - 1.0.5 -* Added support for SEH exceptions instead of C++ exceptions - Win32 only -* Made handlers for DIV0 and overflows individually overridable -* Commented out the destructor - major perf gains here -* Added cast operator for type long, since long != __int32 -* Corrected a couple of missing const modifiers -* Fixed broken >= and <= operators for type U op SafeInt< T, E > -* Nov 5, 2004 - 1.0.6 -* Implemented new logic in binary operators to resolve issues with -* implicit casts -* Fixed casting operator because char != signed char -* Defined __int32 as int instead of long -* Removed unsafe SafeInt::Value method -* Re-implemented casting operator as a result of removing Value method -* Dec 1, 2004 - 1.0.7 -* Implemented specialized operators for pointer arithmetic -* Created overloads for cases of U op= SafeInt. What you do with U -* after that may be dangerous. -* Fixed bug in corner case of MixedSizeModulus -* Fixed bug in MixedSizeMultiply and MixedSizeDivision with input of 0 -* Added throw() decorations -* -* Apr 12, 2005 - 2.0 -* Extensive revisions to leverage template specialization. -* April, 2007 Extensive revisions for version 3.0 -* Nov 22, 2009 Forked from MS internal code -* Changes needed to support gcc compiler - many thanks to Niels Dekker -* for determining not just the issues, but also suggesting fixes. -* Also updating some of the header internals to be the same as the upcoming Visual Studio version. -* -* Jan 16, 2010 64-bit gcc has long == __int64, which means that many of the existing 64-bit -* templates are over-specialized. This forces a redefinition of all the 64-bit -* multiplication routines to use pointers instead of references for return -* values. Also, let's use some intrinsics for x64 Microsoft compiler to -* reduce code size, and hopefully improve efficiency. -* -* June 21, 2014 Better support for clang, higher warning levels supported for all 3 primary supported - compilers (Visual Studio, clang, gcc). - Also started to converge the code base such that the public CodePlex version will - be a drop-in replacement for the Visual Studio version. - -* Note about code style - throughout this class, casts will be written using C-style (T), -* not C++ style static_cast< T >. This is because the class is nearly always dealing with integer -* types, and in this case static_cast and a C cast are equivalent. Given the large number of casts, -* the code is a little more readable this way. In the event a cast is needed where static_cast couldn't -* be substituted, we'll use the new templatized cast to make it explicit what the operation is doing. -* -************************************************************************************************************ -* Version 3.0 changes: -* -* 1) The exception type thrown is now replacable, and you can throw your own exception types. This should help -* those using well-developed exception classes. -* 2) The 64-bit multiplication code has had a lot of perf work done, and should be faster than 2.0. -* 3) There is now limited floating point support. You can initialize a SafeInt with a floating point type, -* and you can cast it out (or assign) to a float as well. -* 4) There is now an Align method. I noticed people use this a lot, and rarely check errors, so now you have one. -* -* Another major improvement is the addition of external functions - if you just want to check an operation, this can now -happen: -* All of the following can be invoked without dealing with creating a class, or managing exceptions. This is especially -handy -* for 64-bit porting, since SafeCast compiles away for a 32-bit cast from size_t to unsigned long, but checks it for -64-bit. -* -* inline bool SafeCast( const T From, U& To ) throw() -* inline bool SafeEquals( const T t, const U u ) throw() -* inline bool SafeNotEquals( const T t, const U u ) throw() -* inline bool SafeGreaterThan( const T t, const U u ) throw() -* inline bool SafeGreaterThanEquals( const T t, const U u ) throw() -* inline bool SafeLessThan( const T t, const U u ) throw() -* inline bool SafeLessThanEquals( const T t, const U u ) throw() -* inline bool SafeModulus( const T& t, const U& u, T& result ) throw() -* inline bool SafeMultiply( T t, U u, T& result ) throw() -* inline bool SafeDivide( T t, U u, T& result ) throw() -* inline bool SafeAdd( T t, U u, T& result ) throw() -* inline bool SafeSubtract( T t, U u, T& result ) throw() -* -*/ - -// use these if the compiler does not support _intXX -#ifdef NEEDS_INT_DEFINED -#define __int8 char -#define __int16 short -#define __int32 int -#define __int64 long long -#endif - -namespace msl -{ -namespace safeint3 -{ -// catch these to handle errors -// Currently implemented code values: -// ERROR_ARITHMETIC_OVERFLOW -// EXCEPTION_INT_DIVIDE_BY_ZERO -enum SafeIntError -{ - SafeIntNoError = 0, - SafeIntArithmeticOverflow, - SafeIntDivideByZero -}; - -} // namespace safeint3 -} // namespace msl - -/* - * Error handler classes - * Using classes to deal with exceptions is going to allow the most - * flexibility, and we can mix different error handlers in the same project - * or even the same file. It isn't advisable to do this in the same function - * because a SafeInt< int, MyExceptionHandler > isn't the same thing as - * SafeInt< int, YourExceptionHander >. - * If for some reason you have to translate between the two, cast one of them back to its - * native type. - * - * To use your own exception class with SafeInt, first create your exception class, - * which may look something like the SafeIntException class below. The second step is to - * create a template specialization that implements SafeIntOnOverflow and SafeIntOnDivZero. - * For example: - * - * template <> class SafeIntExceptionHandler < YourExceptionClass > - * { - * static __declspec(noreturn) void __stdcall SafeIntOnOverflow() - * { - * throw YourExceptionClass( EXCEPTION_INT_OVERFLOW ); - * } - * - * static __declspec(noreturn) void __stdcall SafeIntOnDivZero() - * { - * throw YourExceptionClass( EXCEPTION_INT_DIVIDE_BY_ZERO ); - * } - * }; - * - * typedef SafeIntExceptionHandler < YourExceptionClass > YourSafeIntExceptionHandler - * You'd then declare your SafeInt objects like this: - * SafeInt< int, YourSafeIntExceptionHandler > - * - * Unfortunately, there is no such thing as partial template specialization in typedef - * statements, so you have three options if you find this cumbersome: - * - * 1) Create a holder class: - * - * template < typename T > - * class MySafeInt - * { - * public: - * SafeInt< T, MyExceptionClass> si; - * }; - * - * You'd then declare an instance like so: - * MySafeInt< int > i; - * - * You'd lose handy things like initialization - it would have to be initialized as: - * - * i.si = 0; - * - * 2) You could create a typedef for every int type you deal with: - * - * typedef SafeInt< int, MyExceptionClass > MySafeInt; - * typedef SafeInt< char, MyExceptionClass > MySafeChar; - * - * and so on. The second approach is probably more usable, and will just drop into code - * better, which is the original intent of the SafeInt class. - * - * 3) If you're going to consistently use a different class to handle your exceptions, - * you can override the default typedef like so: - * - * #define SafeIntDefaultExceptionHandler YourSafeIntExceptionHandler - * - * Overall, this is probably the best approach. - * */ - -// On the Microsoft compiler, violating a throw() annotation is a silent error. -// Other compilers might turn these into exceptions, and some users may want to not have throw() enabled. -// In addition, some error handlers may not throw C++ exceptions, which makes everything no throw. -#if defined SAFEINT_REMOVE_NOTHROW -#define SAFEINT_NOTHROW -#else -#define SAFEINT_NOTHROW throw() -#endif - -namespace msl -{ -namespace safeint3 -{ -// If you would like to use your own custom assert -// Define SAFEINT_ASSERT -#if !defined SAFEINT_ASSERT -#include -#define SAFEINT_ASSERT(x) assert(x) -#endif - -#if defined SAFEINT_ASSERT_ON_EXCEPTION -inline void SafeIntExceptionAssert() SAFEINT_NOTHROW { SAFEINT_ASSERT(false); } -#else -inline void SafeIntExceptionAssert() SAFEINT_NOTHROW {} -#endif - -#if SAFEINT_COMPILER == GCC_COMPILER || SAFEINT_COMPILER == CLANG_COMPILER -#define SAFEINT_NORETURN __attribute__((noreturn)) -#define SAFEINT_STDCALL -#define SAFEINT_VISIBLE __attribute__((__visibility__("default"))) -#define SAFEINT_WEAK __attribute__((weak)) -#else -#define SAFEINT_NORETURN __declspec(noreturn) -#define SAFEINT_STDCALL __stdcall -#define SAFEINT_VISIBLE -#define SAFEINT_WEAK -#endif - -class SAFEINT_VISIBLE SafeIntException -{ -public: - SafeIntException() SAFEINT_NOTHROW { m_code = SafeIntNoError; } - SafeIntException(SafeIntError code) SAFEINT_NOTHROW { m_code = code; } - SafeIntError m_code; -}; - -namespace SafeIntInternal -{ -// Visual Studio version of SafeInt provides for two possible error -// handlers: -// SafeIntErrorPolicy_SafeIntException - C++ exception, default if not otherwise defined -// SafeIntErrorPolicy_InvalidParameter - Calls fail fast (Windows-specific), bypasses any exception handlers, -// exits the app with a crash -template -class SafeIntExceptionHandler; - -template<> -class SafeIntExceptionHandler -{ -public: - static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnOverflow() - { - SafeIntExceptionAssert(); - throw SafeIntException(SafeIntArithmeticOverflow); - } - - static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnDivZero() - { - SafeIntExceptionAssert(); - throw SafeIntException(SafeIntDivideByZero); - } -}; - -#if !defined _CRT_SECURE_INVALID_PARAMETER -// Calling fail fast is somewhat more robust than calling abort, -// but abort is the closest we can manage without Visual Studio support -// Need the header for abort() -#include -#define _CRT_SECURE_INVALID_PARAMETER(msg) abort() -#endif - -class SafeInt_InvalidParameter -{ -public: - static SAFEINT_NORETURN void SafeIntOnOverflow() SAFEINT_NOTHROW - { - SafeIntExceptionAssert(); - _CRT_SECURE_INVALID_PARAMETER("SafeInt Arithmetic Overflow"); - } - - static SAFEINT_NORETURN void SafeIntOnDivZero() SAFEINT_NOTHROW - { - SafeIntExceptionAssert(); - _CRT_SECURE_INVALID_PARAMETER("SafeInt Divide By Zero"); - } -}; - -#if defined _WINDOWS_ - -class SafeIntWin32ExceptionHandler -{ -public: - static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnOverflow() SAFEINT_NOTHROW - { - SafeIntExceptionAssert(); - RaiseException(static_cast(EXCEPTION_INT_OVERFLOW), EXCEPTION_NONCONTINUABLE, 0, 0); - } - - static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnDivZero() SAFEINT_NOTHROW - { - SafeIntExceptionAssert(); - RaiseException(static_cast(EXCEPTION_INT_DIVIDE_BY_ZERO), EXCEPTION_NONCONTINUABLE, 0, 0); - } -}; - -#endif - -} // namespace SafeIntInternal - -// both of these have cross-platform support -typedef SafeIntInternal::SafeIntExceptionHandler CPlusPlusExceptionHandler; -typedef SafeIntInternal::SafeInt_InvalidParameter InvalidParameterExceptionHandler; - -// This exception handler is no longer recommended, but is left here in order not to break existing users -#if defined _WINDOWS_ -typedef SafeIntInternal::SafeIntWin32ExceptionHandler Win32ExceptionHandler; -#endif - -// For Visual Studio compatibility -#if defined VISUAL_STUDIO_SAFEINT_COMPAT -typedef CPlusPlusExceptionHandler SafeIntErrorPolicy_SafeIntException; -typedef InvalidParameterExceptionHandler SafeIntErrorPolicy_InvalidParameter; -#endif - -// If the user hasn't defined a default exception handler, -// define one now, depending on whether they would like Win32 or C++ exceptions - -// This library will use conditional noexcept soon, but not in this release -// Some users might mix exception handlers, which is not advised, but is supported -#if !defined SafeIntDefaultExceptionHandler -#if defined SAFEINT_RAISE_EXCEPTION -#if !defined _WINDOWS_ -#error Include windows.h in order to use Win32 exceptions -#endif - -#define SafeIntDefaultExceptionHandler Win32ExceptionHandler -#elif defined SAFEINT_FAILFAST -#define SafeIntDefaultExceptionHandler InvalidParameterExceptionHandler -#else -#define SafeIntDefaultExceptionHandler CPlusPlusExceptionHandler -#if !defined SAFEINT_EXCEPTION_HANDLER_CPP -#define SAFEINT_EXCEPTION_HANDLER_CPP 1 -#endif -#endif -#endif - -#if !defined SAFEINT_EXCEPTION_HANDLER_CPP -#define SAFEINT_EXCEPTION_HANDLER_CPP 0 -#endif - -// If an error handler is chosen other than C++ exceptions, such as Win32 exceptions, fail fast, -// or abort, then all methods become no throw. Some teams track throw() annotations closely, -// and the following option provides for this. -#if SAFEINT_EXCEPTION_HANDLER_CPP -#define SAFEINT_CPP_THROW -#else -#define SAFEINT_CPP_THROW SAFEINT_NOTHROW -#endif - -// Turns out we can fool the compiler into not seeing compile-time constants with -// a simple template specialization -template -class CompileConst; -template<> -class CompileConst -{ -public: - static bool Value() SAFEINT_NOTHROW { return true; } -}; -template<> -class CompileConst -{ -public: - static bool Value() SAFEINT_NOTHROW { return false; } -}; - -// The following template magic is because we're now not allowed -// to cast a float to an enum. This means that if we happen to assign -// an enum to a SafeInt of some type, it won't compile, unless we prevent -// isFloat = ( (T)( (float)1.1 ) > (T)1 ) -// from compiling in the case of an enum, which is the point of the specialization -// that follows. - -// If we have support for std, then we can do this easily, and detect enums as well -template -class NumericType; - -#if defined _LIBCPP_TYPE_TRAITS || defined _TYPE_TRAITS_ -// Continue to special case bool -template<> -class NumericType -{ -public: - enum - { - isBool = true, - isFloat = false, - isInt = false - }; -}; -template -class NumericType -{ -public: - enum - { - isBool = false, // We specialized out a bool - isFloat = std::is_floating_point::value, - // If it is an enum, then consider it an int type - // This does allow someone to make a SafeInt from an enum type, which is not recommended, - // but it also allows someone to add an enum value to a SafeInt, which is handy. - isInt = std::is_integral::value || std::is_enum::value - }; -}; - -#else - -template<> -class NumericType -{ -public: - enum - { - isBool = true, - isFloat = false, - isInt = false - }; -}; -template<> -class NumericType -{ -public: - enum - { - isBool = false, - isFloat = false, - isInt = true - }; -}; -template<> -class NumericType -{ -public: - enum - { - isBool = false, - isFloat = false, - isInt = true - }; -}; -template<> -class NumericType -{ -public: - enum - { - isBool = false, - isFloat = false, - isInt = true - }; -}; -template<> -class NumericType -{ -public: - enum - { - isBool = false, - isFloat = false, - isInt = true - }; -}; -template<> -class NumericType -{ -public: - enum - { - isBool = false, - isFloat = false, - isInt = true - }; -}; -#if defined SAFEINT_USE_WCHAR_T || defined _NATIVE_WCHAR_T_DEFINED -template<> -class NumericType -{ -public: - enum - { - isBool = false, - isFloat = false, - isInt = true - }; -}; -#endif -template<> -class NumericType -{ -public: - enum - { - isBool = false, - isFloat = false, - isInt = true - }; -}; -template<> -class NumericType -{ -public: - enum - { - isBool = false, - isFloat = false, - isInt = true - }; -}; -template<> -class NumericType -{ -public: - enum - { - isBool = false, - isFloat = false, - isInt = true - }; -}; -template<> -class NumericType -{ -public: - enum - { - isBool = false, - isFloat = false, - isInt = true - }; -}; -template<> -class NumericType<__int64> -{ -public: - enum - { - isBool = false, - isFloat = false, - isInt = true - }; -}; -template<> -class NumericType -{ -public: - enum - { - isBool = false, - isFloat = false, - isInt = true - }; -}; -template<> -class NumericType -{ -public: - enum - { - isBool = false, - isFloat = true, - isInt = false - }; -}; -template<> -class NumericType -{ -public: - enum - { - isBool = false, - isFloat = true, - isInt = false - }; -}; -template<> -class NumericType -{ -public: - enum - { - isBool = false, - isFloat = true, - isInt = false - }; -}; -// Catch-all for anything not supported -template -class NumericType -{ -public: - // We have some unknown type, which could be an enum. For parity with the code that uses , - // We can try a static_cast - it if compiles, then it might be an enum, and should work. - // If it is something else that just happens to have a constructor that takes an int, and a casting operator, - // then it is possible something will go wrong, and for best results, cast it directly to an int before letting it - // interact with a SafeInt - - enum - { - isBool = false, - isFloat = false, - isInt = static_cast(static_cast(0)) == 0 - }; -}; -#endif // type traits - -// Use this to avoid compile-time const truncation warnings -template -class SafeIntMinMax; - -template<> -class SafeIntMinMax -{ -public: - const static signed __int8 min = (-0x7f - 1); - const static signed __int8 max = 0x7f; -}; -template<> -class SafeIntMinMax -{ -public: - const static __int16 min = (-0x7fff - 1); - const static __int16 max = 0x7fff; -}; -template<> -class SafeIntMinMax -{ -public: - const static __int32 min = (-0x7fffffff - 1); - const static __int32 max = 0x7fffffff; -}; -template<> -class SafeIntMinMax -{ -public: - const static __int64 min = static_cast<__int64>(0x8000000000000000LL); - const static __int64 max = 0x7fffffffffffffffLL; -}; - -template<> -class SafeIntMinMax -{ -public: - const static unsigned __int8 min = 0; - const static unsigned __int8 max = 0xff; -}; -template<> -class SafeIntMinMax -{ -public: - const static unsigned __int16 min = 0; - const static unsigned __int16 max = 0xffff; -}; -template<> -class SafeIntMinMax -{ -public: - const static unsigned __int32 min = 0; - const static unsigned __int32 max = 0xffffffff; -}; -template<> -class SafeIntMinMax -{ -public: - const static unsigned __int64 min = 0; - const static unsigned __int64 max = 0xffffffffffffffffULL; -}; - -template -class IntTraits -{ -public: - C_ASSERT(NumericType::isInt); - enum - { - isSigned = ((T)(-1) < 0), - is64Bit = (sizeof(T) == 8), - is32Bit = (sizeof(T) == 4), - is16Bit = (sizeof(T) == 2), - is8Bit = (sizeof(T) == 1), - isLT32Bit = (sizeof(T) < 4), - isLT64Bit = (sizeof(T) < 8), - isInt8 = (sizeof(T) == 1 && isSigned), - isUint8 = (sizeof(T) == 1 && !isSigned), - isInt16 = (sizeof(T) == 2 && isSigned), - isUint16 = (sizeof(T) == 2 && !isSigned), - isInt32 = (sizeof(T) == 4 && isSigned), - isUint32 = (sizeof(T) == 4 && !isSigned), - isInt64 = (sizeof(T) == 8 && isSigned), - isUint64 = (sizeof(T) == 8 && !isSigned), - bitCount = (sizeof(T) * 8), - isBool = ((T)2 == (T)1) - }; - - // On version 13.10 enums cannot define __int64 values - // so we'll use const statics instead! - // These must be cast to deal with the possibility of a SafeInt being given an enum as an argument - const static T maxInt = static_cast(SafeIntMinMax::max); - const static T minInt = static_cast(SafeIntMinMax::min); -}; - -template -const T IntTraits::maxInt; -template -const T IntTraits::minInt; - -template -class SafeIntCompare -{ -public: - enum - { - isBothSigned = (IntTraits::isSigned && IntTraits::isSigned), - isBothUnsigned = (!IntTraits::isSigned && !IntTraits::isSigned), - isLikeSigned = ((bool)(IntTraits::isSigned) == (bool)(IntTraits::isSigned)), - isCastOK = ((isLikeSigned && sizeof(T) >= sizeof(U)) || (IntTraits::isSigned && sizeof(T) > sizeof(U))), - isBothLT32Bit = (IntTraits::isLT32Bit && IntTraits::isLT32Bit), - isBothLT64Bit = (IntTraits::isLT64Bit && IntTraits::isLT64Bit) - }; -}; - -// all of the arithmetic operators can be solved by the same code within -// each of these regions without resorting to compile-time constant conditionals -// most operators collapse the problem into less than the 22 zones, but this is used -// as the first cut -// using this also helps ensure that we handle all of the possible cases correctly - -template -class IntRegion -{ -public: - enum - { - // unsigned-unsigned zone - IntZone_UintLT32_UintLT32 = SafeIntCompare::isBothUnsigned && SafeIntCompare::isBothLT32Bit, - IntZone_Uint32_UintLT64 = - SafeIntCompare::isBothUnsigned && IntTraits::is32Bit && IntTraits::isLT64Bit, - IntZone_UintLT32_Uint32 = - SafeIntCompare::isBothUnsigned && IntTraits::isLT32Bit && IntTraits::is32Bit, - IntZone_Uint64_Uint = SafeIntCompare::isBothUnsigned && IntTraits::is64Bit, - IntZone_UintLT64_Uint64 = - SafeIntCompare::isBothUnsigned && IntTraits::isLT64Bit && IntTraits::is64Bit, - // unsigned-signed - IntZone_UintLT32_IntLT32 = - !IntTraits::isSigned && IntTraits::isSigned && SafeIntCompare::isBothLT32Bit, - IntZone_Uint32_IntLT64 = IntTraits::isUint32 && IntTraits::isSigned && IntTraits::isLT64Bit, - IntZone_UintLT32_Int32 = !IntTraits::isSigned && IntTraits::isLT32Bit && IntTraits::isInt32, - IntZone_Uint64_Int = IntTraits::isUint64 && IntTraits::isSigned && IntTraits::isLT64Bit, - IntZone_UintLT64_Int64 = !IntTraits::isSigned && IntTraits::isLT64Bit && IntTraits::isInt64, - IntZone_Uint64_Int64 = IntTraits::isUint64 && IntTraits::isInt64, - // signed-signed - IntZone_IntLT32_IntLT32 = SafeIntCompare::isBothSigned && SafeIntCompare::isBothLT32Bit, - IntZone_Int32_IntLT64 = SafeIntCompare::isBothSigned && IntTraits::is32Bit && IntTraits::isLT64Bit, - IntZone_IntLT32_Int32 = SafeIntCompare::isBothSigned && IntTraits::isLT32Bit && IntTraits::is32Bit, - IntZone_Int64_Int64 = SafeIntCompare::isBothSigned && IntTraits::isInt64 && IntTraits::isInt64, - IntZone_Int64_Int = SafeIntCompare::isBothSigned && IntTraits::is64Bit && IntTraits::isLT64Bit, - IntZone_IntLT64_Int64 = SafeIntCompare::isBothSigned && IntTraits::isLT64Bit && IntTraits::is64Bit, - // signed-unsigned - IntZone_IntLT32_UintLT32 = - IntTraits::isSigned && !IntTraits::isSigned && SafeIntCompare::isBothLT32Bit, - IntZone_Int32_UintLT32 = IntTraits::isInt32 && !IntTraits::isSigned && IntTraits::isLT32Bit, - IntZone_IntLT64_Uint32 = IntTraits::isSigned && IntTraits::isLT64Bit && IntTraits::isUint32, - IntZone_Int64_UintLT64 = IntTraits::isInt64 && !IntTraits::isSigned && IntTraits::isLT64Bit, - IntZone_Int_Uint64 = IntTraits::isSigned && IntTraits::isUint64 && IntTraits::isLT64Bit, - IntZone_Int64_Uint64 = IntTraits::isInt64 && IntTraits::isUint64 - }; -}; - -// In all of the following functions, we have two versions -// One for SafeInt, which throws C++ (or possibly SEH) exceptions -// The non-throwing versions are for use by the helper functions that return success and failure. -// Some of the non-throwing functions are not used, but are maintained for completeness. - -// There's no real alternative to duplicating logic, but keeping the two versions -// immediately next to one another will help reduce problems - -// useful function to help with getting the magnitude of a negative number -enum AbsMethod -{ - AbsMethodInt, - AbsMethodInt64, - AbsMethodNoop -}; - -template -class GetAbsMethod -{ -public: - enum - { - method = IntTraits::isLT64Bit && IntTraits::isSigned - ? AbsMethodInt - : IntTraits::isInt64 ? AbsMethodInt64 : AbsMethodNoop - }; -}; - -// let's go ahead and hard-code a dependency on the -// representation of negative numbers to keep compilers from getting overly -// happy with optimizing away things like -MIN_INT. -template -class AbsValueHelper; - -template -class AbsValueHelper -{ -public: - static unsigned __int32 Abs(T t) SAFEINT_NOTHROW - { - SAFEINT_ASSERT(t < 0); - return ~(unsigned __int32)t + 1; - } -}; - -template -class AbsValueHelper -{ -public: - static unsigned __int64 Abs(T t) SAFEINT_NOTHROW - { - SAFEINT_ASSERT(t < 0); - return ~(unsigned __int64)t + 1; - } -}; - -template -class AbsValueHelper -{ -public: - static T Abs(T t) SAFEINT_NOTHROW - { - // Why are you calling Abs on an unsigned number ??? - SAFEINT_ASSERT(false); - return t; - } -}; - -template -class NegationHelper; -// Previous versions had an assert that the type being negated was 32-bit or higher -// In retrospect, this seems like something to just document -// Negation will normally upcast to int -// For example -(unsigned short)0xffff == (int)0xffff0001 -// This class will retain the type, and will truncate, which may not be what -// you wanted -// If you want normal operator casting behavior, do this: -// SafeInt ss = 0xffff; -// then: -// -(SafeInt(ss)) -// will then emit a signed int with the correct value and bitfield - -template -class NegationHelper // Signed -{ -public: - template - static T NegativeThrow(T t) SAFEINT_CPP_THROW - { - // corner case - if (t != IntTraits::minInt) - { - // cast prevents unneeded checks in the case of small ints - return -t; - } - E::SafeIntOnOverflow(); - } - - static bool Negative(T t, T& ret) SAFEINT_NOTHROW - { - // corner case - if (t != IntTraits::minInt) - { - // cast prevents unneeded checks in the case of small ints - ret = -t; - return true; - } - return false; - } -}; - -// Helper classes to work keep compilers from -// optimizing away negation -template -class SignedNegation; - -template<> -class SignedNegation -{ -public: - static signed __int32 Value(unsigned __int64 in) SAFEINT_NOTHROW - { - return (signed __int32)(~(unsigned __int32)in + 1); - } - - static signed __int32 Value(unsigned __int32 in) SAFEINT_NOTHROW { return (signed __int32)(~in + 1); } -}; - -template<> -class SignedNegation -{ -public: - static signed __int64 Value(unsigned __int64 in) SAFEINT_NOTHROW { return (signed __int64)(~in + 1); } -}; - -template -class NegationHelper // unsigned -{ -public: - template - static T NegativeThrow(T t) SAFEINT_CPP_THROW - { -#if defined SAFEINT_DISALLOW_UNSIGNED_NEGATION - C_ASSERT(sizeof(T) == 0); -#endif - -#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER -#pragma warning(push) -// this avoids warnings from the unary '-' operator being applied to unsigned numbers -#pragma warning(disable : 4146) -#endif - // Note - this could be quenched on gcc - // by doing something like: - // return (T)-((__int64)t); - // but it seems like you would want a warning when doing this. - return (T)-t; - -#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER -#pragma warning(pop) -#endif - } - - static bool Negative(T t, T& ret) SAFEINT_NOTHROW - { - if (IntTraits::isLT32Bit) - { - // See above - SAFEINT_ASSERT(false); - } -#if defined SAFEINT_DISALLOW_UNSIGNED_NEGATION - C_ASSERT(sizeof(T) == 0); -#endif - // Do it this way to avoid warning - ret = -t; - return true; - } -}; - -// core logic to determine casting behavior -enum CastMethod -{ - CastOK = 0, - CastCheckLTZero, - CastCheckGTMax, - CastCheckSafeIntMinMaxUnsigned, - CastCheckSafeIntMinMaxSigned, - CastToFloat, - CastFromFloat, - CastToBool, - CastFromBool -}; - -template -class GetCastMethod -{ -public: - enum - { - method = (IntTraits::isBool && !IntTraits::isBool) - ? CastFromBool - : - - (!IntTraits::isBool && IntTraits::isBool) - ? CastToBool - : - - (SafeIntCompare::isCastOK) - ? CastOK - : - - ((IntTraits::isSigned && !IntTraits::isSigned && - sizeof(FromType) >= sizeof(ToType)) || - (SafeIntCompare::isBothUnsigned && sizeof(FromType) > sizeof(ToType))) - ? CastCheckGTMax - : - - (!IntTraits::isSigned && IntTraits::isSigned && - sizeof(ToType) >= sizeof(FromType)) - ? CastCheckLTZero - : - - (!IntTraits::isSigned) ? CastCheckSafeIntMinMaxUnsigned - : CastCheckSafeIntMinMaxSigned - }; -}; - -template -class GetCastMethod -{ -public: - enum - { - method = CastOK - }; -}; - -template -class GetCastMethod -{ -public: - enum - { - method = CastOK - }; -}; - -template -class GetCastMethod -{ -public: - enum - { - method = CastOK - }; -}; - -template -class GetCastMethod -{ -public: - enum - { - method = CastFromFloat - }; -}; - -template -class GetCastMethod -{ -public: - enum - { - method = CastFromFloat - }; -}; - -template -class GetCastMethod -{ -public: - enum - { - method = CastFromFloat - }; -}; - -template -class SafeCastHelper; - -template -class SafeCastHelper -{ -public: - static bool Cast(U u, T& t) SAFEINT_NOTHROW - { - t = (T)u; - return true; - } - - template - static void CastThrow(U u, T& t) SAFEINT_CPP_THROW - { - t = (T)u; - } -}; - -// special case floats and doubles -// tolerate loss of precision -template -class SafeCastHelper -{ -public: - static bool Cast(U u, T& t) SAFEINT_NOTHROW - { - if (u <= (U)IntTraits::maxInt && u >= (U)IntTraits::minInt) - { - t = (T)u; - return true; - } - return false; - } - - template - static void CastThrow(U u, T& t) SAFEINT_CPP_THROW - { - if (u <= (U)IntTraits::maxInt && u >= (U)IntTraits::minInt) - { - t = (T)u; - return; - } - E::SafeIntOnOverflow(); - } -}; - -// Match on any method where a bool is cast to type T -template -class SafeCastHelper -{ -public: - static bool Cast(bool b, T& t) SAFEINT_NOTHROW - { - t = (T)(b ? 1 : 0); - return true; - } - - template - static void CastThrow(bool b, T& t) SAFEINT_CPP_THROW - { - t = (T)(b ? 1 : 0); - } -}; - -template -class SafeCastHelper -{ -public: - static bool Cast(T t, bool& b) SAFEINT_NOTHROW - { - b = !!t; - return true; - } - - template - static void CastThrow(bool b, T& t) SAFEINT_CPP_THROW - { - b = !!t; - } -}; - -template -class SafeCastHelper -{ -public: - static bool Cast(U u, T& t) SAFEINT_NOTHROW - { - if (u < 0) return false; - - t = (T)u; - return true; - } - - template - static void CastThrow(U u, T& t) SAFEINT_CPP_THROW - { - if (u < 0) E::SafeIntOnOverflow(); - - t = (T)u; - } -}; - -template -class SafeCastHelper -{ -public: - static bool Cast(U u, T& t) SAFEINT_NOTHROW - { - if (u > (U)IntTraits::maxInt) return false; - - t = (T)u; - return true; - } - - template - static void CastThrow(U u, T& t) SAFEINT_CPP_THROW - { - if (u > (U)IntTraits::maxInt) E::SafeIntOnOverflow(); - - t = (T)u; - } -}; - -template -class SafeCastHelper -{ -public: - static bool Cast(U u, T& t) SAFEINT_NOTHROW - { - // U is signed - T could be either signed or unsigned - if (u > IntTraits::maxInt || u < 0) return false; - - t = (T)u; - return true; - } - - template - static void CastThrow(U u, T& t) SAFEINT_CPP_THROW - { - // U is signed - T could be either signed or unsigned - if (u > IntTraits::maxInt || u < 0) E::SafeIntOnOverflow(); - - t = (T)u; - } -}; - -template -class SafeCastHelper -{ -public: - static bool Cast(U u, T& t) SAFEINT_NOTHROW - { - // T, U are signed - if (u > IntTraits::maxInt || u < IntTraits::minInt) return false; - - t = (T)u; - return true; - } - - template - static void CastThrow(U u, T& t) SAFEINT_CPP_THROW - { - // T, U are signed - if (u > IntTraits::maxInt || u < IntTraits::minInt) E::SafeIntOnOverflow(); - - t = (T)u; - } -}; - -// core logic to determine whether a comparison is valid, or needs special treatment -enum ComparisonMethod -{ - ComparisonMethod_Ok = 0, - ComparisonMethod_CastInt, - ComparisonMethod_CastInt64, - ComparisonMethod_UnsignedT, - ComparisonMethod_UnsignedU -}; - -// Note - the standard is arguably broken in the case of some integer -// conversion operations -// For example, signed char a = -1 = 0xff -// unsigned int b = 0xffffffff -// If you then test if a < b, a value-preserving cast -// is made, and you're essentially testing -// (unsigned int)a < b == false -// -// I do not think this makes sense - if you perform -// a cast to an __int64, which can clearly preserve both value and signedness -// then you get a different and intuitively correct answer -// IMHO, -1 should be less than 4 billion -// If you prefer to retain the ANSI standard behavior -// insert #define ANSI_CONVERSIONS into your source -// Behavior differences occur in the following cases: -// 8, 16, and 32-bit signed int, unsigned 32-bit int -// any signed int, unsigned 64-bit int -// Note - the signed int must be negative to show the problem - -template -class ValidComparison -{ -public: - enum - { -#ifdef ANSI_CONVERSIONS - method = ComparisonMethod_Ok -#else - method = ((SafeIntCompare::isLikeSigned) - ? ComparisonMethod_Ok - : ((IntTraits::isSigned && sizeof(T) < 8 && sizeof(U) < 4) || - (IntTraits::isSigned && sizeof(T) < 4 && sizeof(U) < 8)) - ? ComparisonMethod_CastInt - : ((IntTraits::isSigned && sizeof(U) < 8) || (IntTraits::isSigned && sizeof(T) < 8)) - ? ComparisonMethod_CastInt64 - : (!IntTraits::isSigned) ? ComparisonMethod_UnsignedT : ComparisonMethod_UnsignedU) -#endif - }; -}; - -template -class EqualityTest; - -template -class EqualityTest -{ -public: - static bool IsEquals(const T t, const U u) SAFEINT_NOTHROW { return (t == u); } -}; - -template -class EqualityTest -{ -public: - static bool IsEquals(const T t, const U u) SAFEINT_NOTHROW { return ((int)t == (int)u); } -}; - -template -class EqualityTest -{ -public: - static bool IsEquals(const T t, const U u) SAFEINT_NOTHROW { return ((__int64)t == (__int64)u); } -}; - -template -class EqualityTest -{ -public: - static bool IsEquals(const T t, const U u) SAFEINT_NOTHROW - { - // one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller - if (u < 0) return false; - - // else safe to cast to type T - return (t == (T)u); - } -}; - -template -class EqualityTest -{ -public: - static bool IsEquals(const T t, const U u) SAFEINT_NOTHROW - { - // one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller - if (t < 0) return false; - - // else safe to cast to type U - return ((U)t == u); - } -}; - -template -class GreaterThanTest; - -template -class GreaterThanTest -{ -public: - static bool GreaterThan(const T t, const U u) SAFEINT_NOTHROW { return (t > u); } -}; - -template -class GreaterThanTest -{ -public: - static bool GreaterThan(const T t, const U u) SAFEINT_NOTHROW { return ((int)t > (int)u); } -}; - -template -class GreaterThanTest -{ -public: - static bool GreaterThan(const T t, const U u) SAFEINT_NOTHROW { return ((__int64)t > (__int64)u); } -}; - -template -class GreaterThanTest -{ -public: - static bool GreaterThan(const T t, const U u) SAFEINT_NOTHROW - { - // one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller - if (u < 0) return true; - - // else safe to cast to type T - return (t > (T)u); - } -}; - -template -class GreaterThanTest -{ -public: - static bool GreaterThan(const T t, const U u) SAFEINT_NOTHROW - { - // one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller - if (t < 0) return false; - - // else safe to cast to type U - return ((U)t > u); - } -}; - -// Modulus is simpler than comparison, but follows much the same logic -// using this set of functions, it can't fail except in a div 0 situation -template -class ModulusHelper; - -template -class ModulusHelper -{ -public: - static SafeIntError Modulus(const T& t, const U& u, T& result) SAFEINT_NOTHROW - { - if (u == 0) return SafeIntDivideByZero; - - // trap corner case - if (CompileConst::isSigned>::Value()) - { - // Some compilers don't notice that this only compiles when u is signed - // Add cast to make them happy - if (u == (U)-1) - { - result = 0; - return SafeIntNoError; - } - } - - result = (T)(t % u); - return SafeIntNoError; - } - - template - static void ModulusThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW - { - if (u == 0) E::SafeIntOnDivZero(); - - // trap corner case - if (CompileConst::isSigned>::Value()) - { - if (u == (U)-1) - { - result = 0; - return; - } - } - - result = (T)(t % u); - } -}; - -template -class ModulusHelper -{ -public: - static SafeIntError Modulus(const T& t, const U& u, T& result) SAFEINT_NOTHROW - { - if (u == 0) return SafeIntDivideByZero; - - // trap corner case - if (CompileConst::isSigned>::Value()) - { - if (u == (U)-1) - { - result = 0; - return SafeIntNoError; - } - } - - result = (T)(t % u); - return SafeIntNoError; - } - - template - static void ModulusThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW - { - if (u == 0) E::SafeIntOnDivZero(); - - // trap corner case - if (CompileConst::isSigned>::Value()) - { - if (u == (U)-1) - { - result = 0; - return; - } - } - - result = (T)(t % u); - } -}; - -template -class ModulusHelper -{ -public: - static SafeIntError Modulus(const T& t, const U& u, T& result) SAFEINT_NOTHROW - { - if (u == 0) return SafeIntDivideByZero; - - // trap corner case - if (CompileConst::isSigned>::Value()) - { - if (u == (U)-1) - { - result = 0; - return SafeIntNoError; - } - } - - result = (T)((__int64)t % (__int64)u); - return SafeIntNoError; - } - - template - static void ModulusThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW - { - if (u == 0) E::SafeIntOnDivZero(); - - if (CompileConst::isSigned>::Value()) - { - if (u == (U)-1) - { - result = 0; - return; - } - } - - result = (T)((__int64)t % (__int64)u); - } -}; - -// T is unsigned __int64, U is any signed int -template -class ModulusHelper -{ -public: - static SafeIntError Modulus(const T& t, const U& u, T& result) SAFEINT_NOTHROW - { - if (u == 0) return SafeIntDivideByZero; - - // u could be negative - if so, need to convert to positive - // casts below are always safe due to the way modulus works - if (u < 0) - result = (T)(t % AbsValueHelper::method>::Abs(u)); - else - result = (T)(t % u); - - return SafeIntNoError; - } - - template - static void ModulusThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW - { - if (u == 0) E::SafeIntOnDivZero(); - - // u could be negative - if so, need to convert to positive - if (u < 0) - result = (T)(t % AbsValueHelper::method>::Abs(u)); - else - result = (T)(t % u); - } -}; - -// U is unsigned __int64, T any signed int -template -class ModulusHelper -{ -public: - static SafeIntError Modulus(const T& t, const U& u, T& result) SAFEINT_NOTHROW - { - if (u == 0) return SafeIntDivideByZero; - - // t could be negative - if so, need to convert to positive - if (t < 0) - result = (T)(~(AbsValueHelper::method>::Abs(t) % u) + 1); - else - result = (T)((T)t % u); - - return SafeIntNoError; - } - - template - static void ModulusThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW - { - if (u == 0) E::SafeIntOnDivZero(); - - // t could be negative - if so, need to convert to positive - if (t < 0) - result = (T)(~(AbsValueHelper::method>::Abs(t) % u) + 1); - else - result = (T)((T)t % u); - } -}; - -// core logic to determine method to check multiplication -enum MultiplicationState -{ - MultiplicationState_CastInt = 0, // One or both signed, smaller than 32-bit - MultiplicationState_CastInt64, // One or both signed, smaller than 64-bit - MultiplicationState_CastUint, // Both are unsigned, smaller than 32-bit - MultiplicationState_CastUint64, // Both are unsigned, both 32-bit or smaller - MultiplicationState_Uint64Uint, // Both are unsigned, lhs 64-bit, rhs 32-bit or smaller - MultiplicationState_Uint64Uint64, // Both are unsigned int64 - MultiplicationState_Uint64Int, // lhs is unsigned int64, rhs int32 - MultiplicationState_Uint64Int64, // lhs is unsigned int64, rhs signed int64 - MultiplicationState_UintUint64, // Both are unsigned, lhs 32-bit or smaller, rhs 64-bit - MultiplicationState_UintInt64, // lhs unsigned 32-bit or less, rhs int64 - MultiplicationState_Int64Uint, // lhs int64, rhs unsigned int32 - MultiplicationState_Int64Int64, // lhs int64, rhs int64 - MultiplicationState_Int64Int, // lhs int64, rhs int32 - MultiplicationState_IntUint64, // lhs int, rhs unsigned int64 - MultiplicationState_IntInt64, // lhs int, rhs int64 - MultiplicationState_Int64Uint64, // lhs int64, rhs uint64 - MultiplicationState_Error -}; - -template -class MultiplicationMethod -{ -public: - enum - { - // unsigned-unsigned - method = - (IntRegion::IntZone_UintLT32_UintLT32 - ? MultiplicationState_CastUint - : (IntRegion::IntZone_Uint32_UintLT64 || IntRegion::IntZone_UintLT32_Uint32) - ? MultiplicationState_CastUint64 - : SafeIntCompare::isBothUnsigned && IntTraits::isUint64 && IntTraits::isUint64 - ? MultiplicationState_Uint64Uint64 - : (IntRegion::IntZone_Uint64_Uint) - ? MultiplicationState_Uint64Uint - : (IntRegion::IntZone_UintLT64_Uint64) ? MultiplicationState_UintUint64 : - // unsigned-signed - (IntRegion::IntZone_UintLT32_IntLT32) - ? MultiplicationState_CastInt - : (IntRegion::IntZone_Uint32_IntLT64 || - IntRegion::IntZone_UintLT32_Int32) - ? MultiplicationState_CastInt64 - : (IntRegion::IntZone_Uint64_Int) - ? MultiplicationState_Uint64Int - : (IntRegion::IntZone_UintLT64_Int64) - ? MultiplicationState_UintInt64 - : (IntRegion::IntZone_Uint64_Int64) - ? MultiplicationState_Uint64Int64 - : - // signed-signed - (IntRegion::IntZone_IntLT32_IntLT32) - ? MultiplicationState_CastInt - : (IntRegion::IntZone_Int32_IntLT64 || - IntRegion::IntZone_IntLT32_Int32) - ? MultiplicationState_CastInt64 - : (IntRegion::IntZone_Int64_Int64) - ? MultiplicationState_Int64Int64 - : (IntRegion::IntZone_Int64_Int) - ? MultiplicationState_Int64Int - : (IntRegion:: - IntZone_IntLT64_Int64) - ? MultiplicationState_IntInt64 - : - // signed-unsigned - (IntRegion:: - IntZone_IntLT32_UintLT32) - ? MultiplicationState_CastInt - : (IntRegion:: - IntZone_Int32_UintLT32 || - IntRegion:: - IntZone_IntLT64_Uint32) - ? MultiplicationState_CastInt64 - : (IntRegion< - T, - U>:: - IntZone_Int64_UintLT64) - ? MultiplicationState_Int64Uint - : (IntRegion< - T, - U>:: - IntZone_Int_Uint64) - ? MultiplicationState_IntUint64 - : (IntRegion< - T, - U>::IntZone_Int64_Uint64 - ? MultiplicationState_Int64Uint64 - : MultiplicationState_Error)) - }; -}; - -template -class MultiplicationHelper; - -template -class MultiplicationHelper -{ -public: - // accepts signed, both less than 32-bit - static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW - { - int tmp = t * u; - - if (tmp > IntTraits::maxInt || tmp < IntTraits::minInt) return false; - - ret = (T)tmp; - return true; - } - - template - static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW - { - int tmp = t * u; - - if (tmp > IntTraits::maxInt || tmp < IntTraits::minInt) E::SafeIntOnOverflow(); - - ret = (T)tmp; - } -}; - -template -class MultiplicationHelper -{ -public: - // accepts unsigned, both less than 32-bit - static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW - { - unsigned int tmp = (unsigned int)(t * u); - - if (tmp > IntTraits::maxInt) return false; - - ret = (T)tmp; - return true; - } - - template - static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW - { - unsigned int tmp = (unsigned int)(t * u); - - if (tmp > IntTraits::maxInt) E::SafeIntOnOverflow(); - - ret = (T)tmp; - } -}; - -template -class MultiplicationHelper -{ -public: - // mixed signed or both signed where at least one argument is 32-bit, and both a 32-bit or less - static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW - { - __int64 tmp = (__int64)t * (__int64)u; - - if (tmp > (__int64)IntTraits::maxInt || tmp < (__int64)IntTraits::minInt) return false; - - ret = (T)tmp; - return true; - } - - template - static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW - { - __int64 tmp = (__int64)t * (__int64)u; - - if (tmp > (__int64)IntTraits::maxInt || tmp < (__int64)IntTraits::minInt) E::SafeIntOnOverflow(); - - ret = (T)tmp; - } -}; - -template -class MultiplicationHelper -{ -public: - // both unsigned where at least one argument is 32-bit, and both are 32-bit or less - static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW - { - unsigned __int64 tmp = (unsigned __int64)t * (unsigned __int64)u; - - if (tmp > (unsigned __int64)IntTraits::maxInt) return false; - - ret = (T)tmp; - return true; - } - - template - static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW - { - unsigned __int64 tmp = (unsigned __int64)t * (unsigned __int64)u; - - if (tmp > (unsigned __int64)IntTraits::maxInt) E::SafeIntOnOverflow(); - - ret = (T)tmp; - } -}; - -// T = left arg and return type -// U = right arg -template -class LargeIntRegMultiply; - -#if SAFEINT_USE_INTRINSICS -// As usual, unsigned is easy -inline bool IntrinsicMultiplyUint64(const unsigned __int64& a, - const unsigned __int64& b, - unsigned __int64* pRet) SAFEINT_NOTHROW -{ - unsigned __int64 ulHigh = 0; - *pRet = _umul128(a, b, &ulHigh); - return ulHigh == 0; -} - -// Signed, is not so easy -inline bool IntrinsicMultiplyInt64(const signed __int64& a, - const signed __int64& b, - signed __int64* pRet) SAFEINT_NOTHROW -{ - __int64 llHigh = 0; - *pRet = _mul128(a, b, &llHigh); - - // Now we need to figure out what we expect - // If llHigh is 0, then treat *pRet as unsigned - // If llHigh is < 0, then treat *pRet as signed - - if ((a ^ b) < 0) - { - // Negative result expected - if (llHigh == -1 && *pRet < 0 || llHigh == 0 && *pRet == 0) - { - // Everything is within range - return true; - } - } - else - { - // Result should be positive - // Check for overflow - if (llHigh == 0 && (unsigned __int64)*pRet <= IntTraits::maxInt) return true; - } - return false; -} - -#endif - -template<> -class LargeIntRegMultiply -{ -public: - static bool RegMultiply(const unsigned __int64& a, - const unsigned __int64& b, - unsigned __int64* pRet) SAFEINT_NOTHROW - { -#if SAFEINT_USE_INTRINSICS - return IntrinsicMultiplyUint64(a, b, pRet); -#else - unsigned __int32 aHigh, aLow, bHigh, bLow; - - // Consider that a*b can be broken up into: - // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) - // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) - // Note - same approach applies for 128 bit math on a 64-bit system - - aHigh = (unsigned __int32)(a >> 32); - aLow = (unsigned __int32)a; - bHigh = (unsigned __int32)(b >> 32); - bLow = (unsigned __int32)b; - - *pRet = 0; - - if (aHigh == 0) - { - if (bHigh != 0) - { - *pRet = (unsigned __int64)aLow * (unsigned __int64)bHigh; - } - } - else if (bHigh == 0) - { - if (aHigh != 0) - { - *pRet = (unsigned __int64)aHigh * (unsigned __int64)bLow; - } - } - else - { - return false; - } - - if (*pRet != 0) - { - unsigned __int64 tmp; - - if ((unsigned __int32)(*pRet >> 32) != 0) return false; - - *pRet <<= 32; - tmp = (unsigned __int64)aLow * (unsigned __int64)bLow; - *pRet += tmp; - - if (*pRet < tmp) return false; - - return true; - } - - *pRet = (unsigned __int64)aLow * (unsigned __int64)bLow; - return true; -#endif - } - - template - static void RegMultiplyThrow(const unsigned __int64& a, - const unsigned __int64& b, - unsigned __int64* pRet) SAFEINT_CPP_THROW - { -#if SAFEINT_USE_INTRINSICS - if (!IntrinsicMultiplyUint64(a, b, pRet)) E::SafeIntOnOverflow(); -#else - unsigned __int32 aHigh, aLow, bHigh, bLow; - - // Consider that a*b can be broken up into: - // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) - // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) - // Note - same approach applies for 128 bit math on a 64-bit system - - aHigh = (unsigned __int32)(a >> 32); - aLow = (unsigned __int32)a; - bHigh = (unsigned __int32)(b >> 32); - bLow = (unsigned __int32)b; - - *pRet = 0; - - if (aHigh == 0) - { - if (bHigh != 0) - { - *pRet = (unsigned __int64)aLow * (unsigned __int64)bHigh; - } - } - else if (bHigh == 0) - { - if (aHigh != 0) - { - *pRet = (unsigned __int64)aHigh * (unsigned __int64)bLow; - } - } - else - { - E::SafeIntOnOverflow(); - } - - if (*pRet != 0) - { - unsigned __int64 tmp; - - if ((unsigned __int32)(*pRet >> 32) != 0) E::SafeIntOnOverflow(); - - *pRet <<= 32; - tmp = (unsigned __int64)aLow * (unsigned __int64)bLow; - *pRet += tmp; - - if (*pRet < tmp) E::SafeIntOnOverflow(); - - return; - } - - *pRet = (unsigned __int64)aLow * (unsigned __int64)bLow; -#endif - } -}; - -template<> -class LargeIntRegMultiply -{ -public: - static bool RegMultiply(const unsigned __int64& a, unsigned __int32 b, unsigned __int64* pRet) SAFEINT_NOTHROW - { -#if SAFEINT_USE_INTRINSICS - return IntrinsicMultiplyUint64(a, (unsigned __int64)b, pRet); -#else - unsigned __int32 aHigh, aLow; - - // Consider that a*b can be broken up into: - // (aHigh * 2^32 + aLow) * b - // => (aHigh * b * 2^32) + (aLow * b) - - aHigh = (unsigned __int32)(a >> 32); - aLow = (unsigned __int32)a; - - *pRet = 0; - - if (aHigh != 0) - { - *pRet = (unsigned __int64)aHigh * (unsigned __int64)b; - - unsigned __int64 tmp; - - if ((unsigned __int32)(*pRet >> 32) != 0) return false; - - *pRet <<= 32; - tmp = (unsigned __int64)aLow * (unsigned __int64)b; - *pRet += tmp; - - if (*pRet < tmp) return false; - - return true; - } - - *pRet = (unsigned __int64)aLow * (unsigned __int64)b; - return true; -#endif - } - - template - static void RegMultiplyThrow(const unsigned __int64& a, - unsigned __int32 b, - unsigned __int64* pRet) SAFEINT_CPP_THROW - { -#if SAFEINT_USE_INTRINSICS - if (!IntrinsicMultiplyUint64(a, (unsigned __int64)b, pRet)) E::SafeIntOnOverflow(); -#else - unsigned __int32 aHigh, aLow; - - // Consider that a*b can be broken up into: - // (aHigh * 2^32 + aLow) * b - // => (aHigh * b * 2^32) + (aLow * b) - - aHigh = (unsigned __int32)(a >> 32); - aLow = (unsigned __int32)a; - - *pRet = 0; - - if (aHigh != 0) - { - *pRet = (unsigned __int64)aHigh * (unsigned __int64)b; - - unsigned __int64 tmp; - - if ((unsigned __int32)(*pRet >> 32) != 0) E::SafeIntOnOverflow(); - - *pRet <<= 32; - tmp = (unsigned __int64)aLow * (unsigned __int64)b; - *pRet += tmp; - - if (*pRet < tmp) E::SafeIntOnOverflow(); - - return; - } - - *pRet = (unsigned __int64)aLow * (unsigned __int64)b; - return; -#endif - } -}; - -template<> -class LargeIntRegMultiply -{ -public: - // Intrinsic not needed - static bool RegMultiply(const unsigned __int64& a, signed __int32 b, unsigned __int64* pRet) SAFEINT_NOTHROW - { - if (b < 0 && a != 0) return false; - -#if SAFEINT_USE_INTRINSICS - return IntrinsicMultiplyUint64(a, (unsigned __int64)b, pRet); -#else - return LargeIntRegMultiply::RegMultiply(a, (unsigned __int32)b, pRet); -#endif - } - - template - static void RegMultiplyThrow(const unsigned __int64& a, signed __int32 b, unsigned __int64* pRet) SAFEINT_CPP_THROW - { - if (b < 0 && a != 0) E::SafeIntOnOverflow(); - -#if SAFEINT_USE_INTRINSICS - if (!IntrinsicMultiplyUint64(a, (unsigned __int64)b, pRet)) E::SafeIntOnOverflow(); -#else - LargeIntRegMultiply::template RegMultiplyThrow( - a, (unsigned __int32)b, pRet); -#endif - } -}; - -template<> -class LargeIntRegMultiply -{ -public: - static bool RegMultiply(const unsigned __int64& a, signed __int64 b, unsigned __int64* pRet) SAFEINT_NOTHROW - { - if (b < 0 && a != 0) return false; - -#if SAFEINT_USE_INTRINSICS - return IntrinsicMultiplyUint64(a, (unsigned __int64)b, pRet); -#else - return LargeIntRegMultiply::RegMultiply(a, (unsigned __int64)b, pRet); -#endif - } - - template - static void RegMultiplyThrow(const unsigned __int64& a, signed __int64 b, unsigned __int64* pRet) SAFEINT_CPP_THROW - { - if (b < 0 && a != 0) E::SafeIntOnOverflow(); - -#if SAFEINT_USE_INTRINSICS - if (!IntrinsicMultiplyUint64(a, (unsigned __int64)b, pRet)) E::SafeIntOnOverflow(); -#else - LargeIntRegMultiply::template RegMultiplyThrow( - a, (unsigned __int64)b, pRet); -#endif - } -}; - -template<> -class LargeIntRegMultiply -{ -public: - // Devolves into ordinary 64-bit calculation - static bool RegMultiply(signed __int32 a, const unsigned __int64& b, signed __int32* pRet) SAFEINT_NOTHROW - { - unsigned __int32 bHigh, bLow; - bool fIsNegative = false; - - // Consider that a*b can be broken up into: - // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) - // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) - // aHigh == 0 implies: - // ( aLow * bHigh * 2^32 ) + ( aLow + bLow ) - // If the first part is != 0, fail - - bHigh = (unsigned __int32)(b >> 32); - bLow = (unsigned __int32)b; - - *pRet = 0; - - if (bHigh != 0 && a != 0) return false; - - if (a < 0) - { - a = (signed __int32)AbsValueHelper::method>::Abs(a); - fIsNegative = true; - } - - unsigned __int64 tmp = (unsigned __int32)a * (unsigned __int64)bLow; - - if (!fIsNegative) - { - if (tmp <= (unsigned __int64)IntTraits::maxInt) - { - *pRet = (signed __int32)tmp; - return true; - } - } - else - { - if (tmp <= (unsigned __int64)IntTraits::maxInt + 1) - { - *pRet = SignedNegation::Value(tmp); - return true; - } - } - - return false; - } - - template - static void RegMultiplyThrow(signed __int32 a, const unsigned __int64& b, signed __int32* pRet) SAFEINT_CPP_THROW - { - unsigned __int32 bHigh, bLow; - bool fIsNegative = false; - - // Consider that a*b can be broken up into: - // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) - // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) - - bHigh = (unsigned __int32)(b >> 32); - bLow = (unsigned __int32)b; - - *pRet = 0; - - if (bHigh != 0 && a != 0) E::SafeIntOnOverflow(); - - if (a < 0) - { - a = (signed __int32)AbsValueHelper::method>::Abs(a); - fIsNegative = true; - } - - unsigned __int64 tmp = (unsigned __int32)a * (unsigned __int64)bLow; - - if (!fIsNegative) - { - if (tmp <= (unsigned __int64)IntTraits::maxInt) - { - *pRet = (signed __int32)tmp; - return; - } - } - else - { - if (tmp <= (unsigned __int64)IntTraits::maxInt + 1) - { - *pRet = SignedNegation::Value(tmp); - return; - } - } - - E::SafeIntOnOverflow(); - } -}; - -template<> -class LargeIntRegMultiply -{ -public: - // Becomes ordinary 64-bit multiplication, intrinsic not needed - static bool RegMultiply(unsigned __int32 a, const unsigned __int64& b, unsigned __int32* pRet) SAFEINT_NOTHROW - { - // Consider that a*b can be broken up into: - // (bHigh * 2^32 + bLow) * a - // => (bHigh * a * 2^32) + (bLow * a) - // In this case, the result must fit into 32-bits - // If bHigh != 0 && a != 0, immediate error. - - if ((unsigned __int32)(b >> 32) != 0 && a != 0) return false; - - unsigned __int64 tmp = b * (unsigned __int64)a; - - if ((unsigned __int32)(tmp >> 32) != 0) // overflow - return false; - - *pRet = (unsigned __int32)tmp; - return true; - } - - template - static void RegMultiplyThrow(unsigned __int32 a, - const unsigned __int64& b, - unsigned __int32* pRet) SAFEINT_CPP_THROW - { - if ((unsigned __int32)(b >> 32) != 0 && a != 0) E::SafeIntOnOverflow(); - - unsigned __int64 tmp = b * (unsigned __int64)a; - - if ((unsigned __int32)(tmp >> 32) != 0) // overflow - E::SafeIntOnOverflow(); - - *pRet = (unsigned __int32)tmp; - } -}; - -template<> -class LargeIntRegMultiply -{ -public: - static bool RegMultiply(unsigned __int32 a, const signed __int64& b, unsigned __int32* pRet) SAFEINT_NOTHROW - { - if (b < 0 && a != 0) return false; - return LargeIntRegMultiply::RegMultiply(a, (unsigned __int64)b, pRet); - } - - template - static void RegMultiplyThrow(unsigned __int32 a, const signed __int64& b, unsigned __int32* pRet) SAFEINT_CPP_THROW - { - if (b < 0 && a != 0) E::SafeIntOnOverflow(); - - LargeIntRegMultiply::template RegMultiplyThrow( - a, (unsigned __int64)b, pRet); - } -}; - -template<> -class LargeIntRegMultiply -{ -public: - static bool RegMultiply(const signed __int64& a, const signed __int64& b, signed __int64* pRet) SAFEINT_NOTHROW - { -#if SAFEINT_USE_INTRINSICS - return IntrinsicMultiplyInt64(a, b, pRet); -#else - bool aNegative = false; - bool bNegative = false; - - unsigned __int64 tmp; - __int64 a1 = a; - __int64 b1 = b; - - if (a1 < 0) - { - aNegative = true; - a1 = (signed __int64)AbsValueHelper::method>::Abs(a1); - } - - if (b1 < 0) - { - bNegative = true; - b1 = (signed __int64)AbsValueHelper::method>::Abs(b1); - } - - if (LargeIntRegMultiply::RegMultiply( - (unsigned __int64)a1, (unsigned __int64)b1, &tmp)) - { - // The unsigned multiplication didn't overflow - if (aNegative ^ bNegative) - { - // Result must be negative - if (tmp <= (unsigned __int64)IntTraits::minInt) - { - *pRet = SignedNegation::Value(tmp); - return true; - } - } - else - { - // Result must be positive - if (tmp <= (unsigned __int64)IntTraits::maxInt) - { - *pRet = (signed __int64)tmp; - return true; - } - } - } - - return false; -#endif - } - - template - static void RegMultiplyThrow(const signed __int64& a, - const signed __int64& b, - signed __int64* pRet) SAFEINT_CPP_THROW - { -#if SAFEINT_USE_INTRINSICS - if (!IntrinsicMultiplyInt64(a, b, pRet)) E::SafeIntOnOverflow(); -#else - bool aNegative = false; - bool bNegative = false; - - unsigned __int64 tmp; - __int64 a1 = a; - __int64 b1 = b; - - if (a1 < 0) - { - aNegative = true; - a1 = (signed __int64)AbsValueHelper::method>::Abs(a1); - } - - if (b1 < 0) - { - bNegative = true; - b1 = (signed __int64)AbsValueHelper::method>::Abs(b1); - } - - LargeIntRegMultiply::template RegMultiplyThrow( - (unsigned __int64)a1, (unsigned __int64)b1, &tmp); - - // The unsigned multiplication didn't overflow or we'd be in the exception handler - if (aNegative ^ bNegative) - { - // Result must be negative - if (tmp <= (unsigned __int64)IntTraits::minInt) - { - *pRet = SignedNegation::Value(tmp); - return; - } - } - else - { - // Result must be positive - if (tmp <= (unsigned __int64)IntTraits::maxInt) - { - *pRet = (signed __int64)tmp; - return; - } - } - - E::SafeIntOnOverflow(); -#endif - } -}; - -template<> -class LargeIntRegMultiply -{ -public: - static bool RegMultiply(const signed __int64& a, unsigned __int32 b, signed __int64* pRet) SAFEINT_NOTHROW - { -#if SAFEINT_USE_INTRINSICS - return IntrinsicMultiplyInt64(a, (signed __int64)b, pRet); -#else - bool aNegative = false; - unsigned __int64 tmp; - __int64 a1 = a; - - if (a1 < 0) - { - aNegative = true; - a1 = (signed __int64)AbsValueHelper::method>::Abs(a1); - } - - if (LargeIntRegMultiply::RegMultiply((unsigned __int64)a1, b, &tmp)) - { - // The unsigned multiplication didn't overflow - if (aNegative) - { - // Result must be negative - if (tmp <= (unsigned __int64)IntTraits::minInt) - { - *pRet = SignedNegation::Value(tmp); - return true; - } - } - else - { - // Result must be positive - if (tmp <= (unsigned __int64)IntTraits::maxInt) - { - *pRet = (signed __int64)tmp; - return true; - } - } - } - - return false; -#endif - } - - template - static void RegMultiplyThrow(const signed __int64& a, unsigned __int32 b, signed __int64* pRet) SAFEINT_CPP_THROW - { -#if SAFEINT_USE_INTRINSICS - if (!IntrinsicMultiplyInt64(a, (signed __int64)b, pRet)) E::SafeIntOnOverflow(); -#else - bool aNegative = false; - unsigned __int64 tmp; - __int64 a1 = a; - - if (a1 < 0) - { - aNegative = true; - a1 = (signed __int64)AbsValueHelper::method>::Abs(a1); - } - - LargeIntRegMultiply::template RegMultiplyThrow( - (unsigned __int64)a1, b, &tmp); - - // The unsigned multiplication didn't overflow - if (aNegative) - { - // Result must be negative - if (tmp <= (unsigned __int64)IntTraits::minInt) - { - *pRet = SignedNegation::Value(tmp); - return; - } - } - else - { - // Result must be positive - if (tmp <= (unsigned __int64)IntTraits::maxInt) - { - *pRet = (signed __int64)tmp; - return; - } - } - - E::SafeIntOnOverflow(); -#endif - } -}; - -template<> -class LargeIntRegMultiply -{ -public: - static bool RegMultiply(const signed __int64& a, signed __int32 b, signed __int64* pRet) SAFEINT_NOTHROW - { -#if SAFEINT_USE_INTRINSICS - return IntrinsicMultiplyInt64(a, (signed __int64)b, pRet); -#else - bool aNegative = false; - bool bNegative = false; - - unsigned __int64 tmp; - __int64 a1 = a; - __int64 b1 = b; - - if (a1 < 0) - { - aNegative = true; - a1 = (signed __int64)AbsValueHelper::method>::Abs(a1); - } - - if (b1 < 0) - { - bNegative = true; - b1 = (signed __int64)AbsValueHelper::method>::Abs(b1); - } - - if (LargeIntRegMultiply::RegMultiply( - (unsigned __int64)a1, (unsigned __int32)b1, &tmp)) - { - // The unsigned multiplication didn't overflow - if (aNegative ^ bNegative) - { - // Result must be negative - if (tmp <= (unsigned __int64)IntTraits::minInt) - { - *pRet = SignedNegation::Value(tmp); - return true; - } - } - else - { - // Result must be positive - if (tmp <= (unsigned __int64)IntTraits::maxInt) - { - *pRet = (signed __int64)tmp; - return true; - } - } - } - - return false; -#endif - } - - template - static void RegMultiplyThrow(signed __int64 a, signed __int32 b, signed __int64* pRet) SAFEINT_CPP_THROW - { -#if SAFEINT_USE_INTRINSICS - if (!IntrinsicMultiplyInt64(a, (signed __int64)b, pRet)) E::SafeIntOnOverflow(); -#else - bool aNegative = false; - bool bNegative = false; - - unsigned __int64 tmp; - - if (a < 0) - { - aNegative = true; - a = (signed __int64)AbsValueHelper::method>::Abs(a); - } - - if (b < 0) - { - bNegative = true; - b = (signed __int32)AbsValueHelper::method>::Abs(b); - } - - LargeIntRegMultiply::template RegMultiplyThrow( - (unsigned __int64)a, (unsigned __int32)b, &tmp); - - // The unsigned multiplication didn't overflow - if (aNegative ^ bNegative) - { - // Result must be negative - if (tmp <= (unsigned __int64)IntTraits::minInt) - { - *pRet = SignedNegation::Value(tmp); - return; - } - } - else - { - // Result must be positive - if (tmp <= (unsigned __int64)IntTraits::maxInt) - { - *pRet = (signed __int64)tmp; - return; - } - } - - E::SafeIntOnOverflow(); -#endif - } -}; - -template<> -class LargeIntRegMultiply -{ -public: - static bool RegMultiply(signed __int32 a, const signed __int64& b, signed __int32* pRet) SAFEINT_NOTHROW - { -#if SAFEINT_USE_INTRINSICS - __int64 tmp; - - if (IntrinsicMultiplyInt64(a, b, &tmp)) - { - if (tmp > IntTraits::maxInt || tmp < IntTraits::minInt) - { - return false; - } - - *pRet = (__int32)tmp; - return true; - } - return false; -#else - bool aNegative = false; - bool bNegative = false; - - unsigned __int32 tmp; - __int64 b1 = b; - - if (a < 0) - { - aNegative = true; - a = (signed __int32)AbsValueHelper::method>::Abs(a); - } - - if (b1 < 0) - { - bNegative = true; - b1 = (signed __int64)AbsValueHelper::method>::Abs(b1); - } - - if (LargeIntRegMultiply::RegMultiply( - (unsigned __int32)a, (unsigned __int64)b1, &tmp)) - { - // The unsigned multiplication didn't overflow - if (aNegative ^ bNegative) - { - // Result must be negative - if (tmp <= (unsigned __int32)IntTraits::minInt) - { - *pRet = SignedNegation::Value(tmp); - return true; - } - } - else - { - // Result must be positive - if (tmp <= (unsigned __int32)IntTraits::maxInt) - { - *pRet = (signed __int32)tmp; - return true; - } - } - } - - return false; -#endif - } - - template - static void RegMultiplyThrow(signed __int32 a, const signed __int64& b, signed __int32* pRet) SAFEINT_CPP_THROW - { -#if SAFEINT_USE_INTRINSICS - __int64 tmp; - - if (IntrinsicMultiplyInt64(a, b, &tmp)) - { - if (tmp > IntTraits::maxInt || tmp < IntTraits::minInt) - { - E::SafeIntOnOverflow(); - } - - *pRet = (__int32)tmp; - return; - } - E::SafeIntOnOverflow(); -#else - bool aNegative = false; - bool bNegative = false; - - unsigned __int32 tmp; - signed __int64 b2 = b; - - if (a < 0) - { - aNegative = true; - a = (signed __int32)AbsValueHelper::method>::Abs(a); - } - - if (b < 0) - { - bNegative = true; - b2 = (signed __int64)AbsValueHelper::method>::Abs(b2); - } - - LargeIntRegMultiply::template RegMultiplyThrow( - (unsigned __int32)a, (unsigned __int64)b2, &tmp); - - // The unsigned multiplication didn't overflow - if (aNegative ^ bNegative) - { - // Result must be negative - if (tmp <= (unsigned __int32)IntTraits::minInt) - { - *pRet = SignedNegation::Value(tmp); - return; - } - } - else - { - // Result must be positive - if (tmp <= (unsigned __int32)IntTraits::maxInt) - { - *pRet = (signed __int32)tmp; - return; - } - } - - E::SafeIntOnOverflow(); -#endif - } -}; - -template<> -class LargeIntRegMultiply -{ -public: - // Leave this one as-is - will call unsigned intrinsic internally - static bool RegMultiply(const signed __int64& a, const unsigned __int64& b, signed __int64* pRet) SAFEINT_NOTHROW - { - bool aNegative = false; - - unsigned __int64 tmp; - __int64 a1 = a; - - if (a1 < 0) - { - aNegative = true; - a1 = (signed __int64)AbsValueHelper::method>::Abs(a1); - } - - if (LargeIntRegMultiply::RegMultiply( - (unsigned __int64)a1, (unsigned __int64)b, &tmp)) - { - // The unsigned multiplication didn't overflow - if (aNegative) - { - // Result must be negative - if (tmp <= (unsigned __int64)IntTraits::minInt) - { - *pRet = SignedNegation::Value(tmp); - return true; - } - } - else - { - // Result must be positive - if (tmp <= (unsigned __int64)IntTraits::maxInt) - { - *pRet = (signed __int64)tmp; - return true; - } - } - } - - return false; - } - - template - static void RegMultiplyThrow(const signed __int64& a, - const unsigned __int64& b, - signed __int64* pRet) SAFEINT_CPP_THROW - { - bool aNegative = false; - unsigned __int64 tmp; - __int64 a1 = a; - - if (a1 < 0) - { - aNegative = true; - a1 = (signed __int64)AbsValueHelper::method>::Abs(a1); - } - - if (LargeIntRegMultiply::RegMultiply( - (unsigned __int64)a1, (unsigned __int64)b, &tmp)) - { - // The unsigned multiplication didn't overflow - if (aNegative) - { - // Result must be negative - if (tmp <= (unsigned __int64)IntTraits::minInt) - { - *pRet = SignedNegation::Value(tmp); - return; - } - } - else - { - // Result must be positive - if (tmp <= (unsigned __int64)IntTraits::maxInt) - { - *pRet = (signed __int64)tmp; - return; - } - } - } - - E::SafeIntOnOverflow(); - } -}; - -// In all of the following functions where LargeIntRegMultiply methods are called, -// we need to properly transition types. The methods need __int64, __int32, etc. -// but the variables being passed to us could be long long, long int, or long, depending on -// the compiler. Microsoft compiler knows that long long is the same type as __int64, but gcc doesn't - -template -class MultiplicationHelper -{ -public: - // T, U are unsigned __int64 - static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW - { - C_ASSERT(IntTraits::isUint64 && IntTraits::isUint64); - unsigned __int64 t1 = t; - unsigned __int64 u1 = u; - return LargeIntRegMultiply::RegMultiply( - t1, u1, reinterpret_cast(&ret)); - } - - template - static void MultiplyThrow(const unsigned __int64& t, const unsigned __int64& u, T& ret) SAFEINT_CPP_THROW - { - C_ASSERT(IntTraits::isUint64 && IntTraits::isUint64); - unsigned __int64 t1 = t; - unsigned __int64 u1 = u; - LargeIntRegMultiply::template RegMultiplyThrow( - t1, u1, reinterpret_cast(&ret)); - } -}; - -template -class MultiplicationHelper -{ -public: - // T is unsigned __int64 - // U is any unsigned int 32-bit or less - static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW - { - C_ASSERT(IntTraits::isUint64); - unsigned __int64 t1 = t; - return LargeIntRegMultiply::RegMultiply( - t1, (unsigned __int32)u, reinterpret_cast(&ret)); - } - - template - static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW - { - C_ASSERT(IntTraits::isUint64); - unsigned __int64 t1 = t; - LargeIntRegMultiply::template RegMultiplyThrow( - t1, (unsigned __int32)u, reinterpret_cast(&ret)); - } -}; - -// converse of the previous function -template -class MultiplicationHelper -{ -public: - // T is any unsigned int up to 32-bit - // U is unsigned __int64 - static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW - { - C_ASSERT(IntTraits::isUint64); - unsigned __int64 u1 = u; - unsigned __int32 tmp; - - if (LargeIntRegMultiply::RegMultiply(t, u1, &tmp) && - SafeCastHelper::method>::Cast(tmp, ret)) - { - return true; - } - - return false; - } - - template - static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW - { - C_ASSERT(IntTraits::isUint64); - unsigned __int64 u1 = u; - unsigned __int32 tmp; - - LargeIntRegMultiply::template RegMultiplyThrow(t, u1, &tmp); - SafeCastHelper::method>::template CastThrow(tmp, - ret); - } -}; - -template -class MultiplicationHelper -{ -public: - // T is unsigned __int64 - // U is any signed int, up to 64-bit - static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW - { - C_ASSERT(IntTraits::isUint64); - unsigned __int64 t1 = t; - return LargeIntRegMultiply::RegMultiply( - t1, (signed __int32)u, reinterpret_cast(&ret)); - } - - template - static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW - { - C_ASSERT(IntTraits::isUint64); - unsigned __int64 t1 = t; - LargeIntRegMultiply::template RegMultiplyThrow( - t1, (signed __int32)u, reinterpret_cast(&ret)); - } -}; - -template -class MultiplicationHelper -{ -public: - // T is unsigned __int64 - // U is __int64 - static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW - { - C_ASSERT(IntTraits::isUint64 && IntTraits::isInt64); - unsigned __int64 t1 = t; - __int64 u1 = u; - return LargeIntRegMultiply::RegMultiply( - t1, u1, reinterpret_cast(&ret)); - } - - template - static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW - { - C_ASSERT(IntTraits::isUint64 && IntTraits::isInt64); - unsigned __int64 t1 = t; - __int64 u1 = u; - LargeIntRegMultiply::template RegMultiplyThrow( - t1, u1, reinterpret_cast(&ret)); - } -}; - -template -class MultiplicationHelper -{ -public: - // T is unsigned up to 32-bit - // U is __int64 - static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW - { - C_ASSERT(IntTraits::isInt64); - __int64 u1 = u; - unsigned __int32 tmp; - - if (LargeIntRegMultiply::RegMultiply((unsigned __int32)t, u1, &tmp) && - SafeCastHelper::method>::Cast(tmp, ret)) - { - return true; - } - - return false; - } - - template - static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW - { - C_ASSERT(IntTraits::isInt64); - __int64 u1 = u; - unsigned __int32 tmp; - - LargeIntRegMultiply::template RegMultiplyThrow((unsigned __int32)t, u1, &tmp); - SafeCastHelper::method>::template CastThrow(tmp, - ret); - } -}; - -template -class MultiplicationHelper -{ -public: - // T is __int64 - // U is unsigned up to 32-bit - static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW - { - C_ASSERT(IntTraits::isInt64); - __int64 t1 = t; - return LargeIntRegMultiply<__int64, unsigned __int32>::RegMultiply( - t1, (unsigned __int32)u, reinterpret_cast<__int64*>(&ret)); - } - - template - static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW - { - C_ASSERT(IntTraits::isInt64); - __int64 t1 = t; - LargeIntRegMultiply<__int64, unsigned __int32>::template RegMultiplyThrow( - t1, (unsigned __int32)u, reinterpret_cast<__int64*>(&ret)); - } -}; - -template -class MultiplicationHelper -{ -public: - // T, U are __int64 - static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW - { - C_ASSERT(IntTraits::isInt64 && IntTraits::isInt64); - __int64 t1 = t; - __int64 u1 = u; - return LargeIntRegMultiply<__int64, __int64>::RegMultiply(t1, u1, reinterpret_cast<__int64*>(&ret)); - } - - template - static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW - { - C_ASSERT(IntTraits::isInt64 && IntTraits::isInt64); - __int64 t1 = t; - __int64 u1 = u; - LargeIntRegMultiply<__int64, __int64>::template RegMultiplyThrow(t1, u1, reinterpret_cast<__int64*>(&ret)); - } -}; - -template -class MultiplicationHelper -{ -public: - // T is __int64 - // U is signed up to 32-bit - static bool Multiply(const T& t, U u, T& ret) SAFEINT_NOTHROW - { - C_ASSERT(IntTraits::isInt64); - __int64 t1 = t; - return LargeIntRegMultiply<__int64, __int32>::RegMultiply(t1, (__int32)u, reinterpret_cast<__int64*>(&ret)); - } - - template - static void MultiplyThrow(const __int64& t, U u, T& ret) SAFEINT_CPP_THROW - { - C_ASSERT(IntTraits::isInt64); - __int64 t1 = t; - LargeIntRegMultiply<__int64, __int32>::template RegMultiplyThrow( - t1, (__int32)u, reinterpret_cast<__int64*>(&ret)); - } -}; - -template -class MultiplicationHelper -{ -public: - // T is signed up to 32-bit - // U is unsigned __int64 - static bool Multiply(T t, const U& u, T& ret) SAFEINT_NOTHROW - { - C_ASSERT(IntTraits::isUint64); - unsigned __int64 u1 = u; - __int32 tmp; - - if (LargeIntRegMultiply<__int32, unsigned __int64>::RegMultiply((__int32)t, u1, &tmp) && - SafeCastHelper::method>::Cast(tmp, ret)) - { - return true; - } - - return false; - } - - template - static void MultiplyThrow(T t, const unsigned __int64& u, T& ret) SAFEINT_CPP_THROW - { - C_ASSERT(IntTraits::isUint64); - unsigned __int64 u1 = u; - __int32 tmp; - - LargeIntRegMultiply<__int32, unsigned __int64>::template RegMultiplyThrow((__int32)t, u1, &tmp); - SafeCastHelper::method>::template CastThrow(tmp, ret); - } -}; - -template -class MultiplicationHelper -{ -public: - // T is __int64 - // U is unsigned __int64 - static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW - { - C_ASSERT(IntTraits::isInt64 && IntTraits::isUint64); - __int64 t1 = t; - unsigned __int64 u1 = u; - return LargeIntRegMultiply<__int64, unsigned __int64>::RegMultiply(t1, u1, reinterpret_cast<__int64*>(&ret)); - } - - template - static void MultiplyThrow(const __int64& t, const unsigned __int64& u, T& ret) SAFEINT_CPP_THROW - { - C_ASSERT(IntTraits::isInt64 && IntTraits::isUint64); - __int64 t1 = t; - unsigned __int64 u1 = u; - LargeIntRegMultiply<__int64, unsigned __int64>::template RegMultiplyThrow( - t1, u1, reinterpret_cast<__int64*>(&ret)); - } -}; - -template -class MultiplicationHelper -{ -public: - // T is signed, up to 32-bit - // U is __int64 - static bool Multiply(T t, const U& u, T& ret) SAFEINT_NOTHROW - { - C_ASSERT(IntTraits::isInt64); - __int64 u1 = u; - __int32 tmp; - - if (LargeIntRegMultiply<__int32, __int64>::RegMultiply((__int32)t, u1, &tmp) && - SafeCastHelper::method>::Cast(tmp, ret)) - { - return true; - } - - return false; - } - - template - static void MultiplyThrow(T t, const U& u, T& ret) SAFEINT_CPP_THROW - { - C_ASSERT(IntTraits::isInt64); - __int64 u1 = u; - __int32 tmp; - - LargeIntRegMultiply<__int32, __int64>::template RegMultiplyThrow((__int32)t, u1, &tmp); - SafeCastHelper::method>::template CastThrow(tmp, ret); - } -}; - -enum DivisionState -{ - DivisionState_OK, - DivisionState_UnsignedSigned, - DivisionState_SignedUnsigned32, - DivisionState_SignedUnsigned64, - DivisionState_SignedUnsigned, - DivisionState_SignedSigned -}; - -template -class DivisionMethod -{ -public: - enum - { - method = - (SafeIntCompare::isBothUnsigned - ? DivisionState_OK - : (!IntTraits::isSigned && IntTraits::isSigned) - ? DivisionState_UnsignedSigned - : (IntTraits::isSigned && IntTraits::isUint32 && IntTraits::isLT64Bit) - ? DivisionState_SignedUnsigned32 - : (IntTraits::isSigned && IntTraits::isUint64) - ? DivisionState_SignedUnsigned64 - : (IntTraits::isSigned && !IntTraits::isSigned) ? DivisionState_SignedUnsigned - : DivisionState_SignedSigned) - }; -}; - -template -class DivisionHelper; - -template -class DivisionHelper -{ -public: - static SafeIntError Divide(const T& t, const U& u, T& result) SAFEINT_NOTHROW - { - if (u == 0) return SafeIntDivideByZero; - - if (t == 0) - { - result = 0; - return SafeIntNoError; - } - - result = (T)(t / u); - return SafeIntNoError; - } - - template - static void DivideThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW - { - if (u == 0) E::SafeIntOnDivZero(); - - if (t == 0) - { - result = 0; - return; - } - - result = (T)(t / u); - } -}; - -template -class DivisionHelper -{ -public: - static SafeIntError Divide(const T& t, const U& u, T& result) SAFEINT_NOTHROW - { - if (u == 0) return SafeIntDivideByZero; - - if (t == 0) - { - result = 0; - return SafeIntNoError; - } - - if (u > 0) - { - result = (T)(t / u); - return SafeIntNoError; - } - - // it is always an error to try and divide an unsigned number by a negative signed number - // unless u is bigger than t - if (AbsValueHelper::method>::Abs(u) > t) - { - result = 0; - return SafeIntNoError; - } - - return SafeIntArithmeticOverflow; - } - - template - static void DivideThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW - { - if (u == 0) E::SafeIntOnDivZero(); - - if (t == 0) - { - result = 0; - return; - } - - if (u > 0) - { - result = (T)(t / u); - return; - } - - // it is always an error to try and divide an unsigned number by a negative signed number - // unless u is bigger than t - if (AbsValueHelper::method>::Abs(u) > t) - { - result = 0; - return; - } - - E::SafeIntOnOverflow(); - } -}; - -template -class DivisionHelper -{ -public: - static SafeIntError Divide(const T& t, const U& u, T& result) SAFEINT_NOTHROW - { - if (u == 0) return SafeIntDivideByZero; - - if (t == 0) - { - result = 0; - return SafeIntNoError; - } - - // Test for t > 0 - // If t < 0, must explicitly upcast, or implicit upcast to ulong will cause errors - // As it turns out, 32-bit division is about twice as fast, which justifies the extra conditional - - if (t > 0) - result = (T)(t / u); - else - result = (T)((__int64)t / (__int64)u); - - return SafeIntNoError; - } - - template - static void DivideThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW - { - if (u == 0) - { - E::SafeIntOnDivZero(); - } - - if (t == 0) - { - result = 0; - return; - } - - // Test for t > 0 - // If t < 0, must explicitly upcast, or implicit upcast to ulong will cause errors - // As it turns out, 32-bit division is about twice as fast, which justifies the extra conditional - - if (t > 0) - result = (T)(t / u); - else - result = (T)((__int64)t / (__int64)u); - } -}; - -template -class DivisionHelper -{ -public: - static SafeIntError Divide(const T& t, const unsigned __int64& u, T& result) SAFEINT_NOTHROW - { - C_ASSERT(IntTraits::isUint64); - - if (u == 0) - { - return SafeIntDivideByZero; - } - - if (t == 0) - { - result = 0; - return SafeIntNoError; - } - - if (u <= (unsigned __int64)IntTraits::maxInt) - { - // Else u can safely be cast to T - if (CompileConst::Value()) - result = (T)((int)t / (int)u); - else - result = (T)((__int64)t / (__int64)u); - } - else // Corner case - if (t == IntTraits::minInt && u == (unsigned __int64)IntTraits::minInt) - { - // Min int divided by it's own magnitude is -1 - result = -1; - } - else - { - result = 0; - } - return SafeIntNoError; - } - - template - static void DivideThrow(const T& t, const unsigned __int64& u, T& result) SAFEINT_CPP_THROW - { - C_ASSERT(IntTraits::isUint64); - - if (u == 0) - { - E::SafeIntOnDivZero(); - } - - if (t == 0) - { - result = 0; - return; - } - - if (u <= (unsigned __int64)IntTraits::maxInt) - { - // Else u can safely be cast to T - if (CompileConst::Value()) - result = (T)((int)t / (int)u); - else - result = (T)((__int64)t / (__int64)u); - } - else // Corner case - if (t == IntTraits::minInt && u == (unsigned __int64)IntTraits::minInt) - { - // Min int divided by it's own magnitude is -1 - result = -1; - } - else - { - result = 0; - } - } -}; - -template -class DivisionHelper -{ -public: - // T is any signed, U is unsigned and smaller than 32-bit - // In this case, standard operator casting is correct - static SafeIntError Divide(const T& t, const U& u, T& result) SAFEINT_NOTHROW - { - if (u == 0) - { - return SafeIntDivideByZero; - } - - if (t == 0) - { - result = 0; - return SafeIntNoError; - } - - result = (T)(t / u); - return SafeIntNoError; - } - - template - static void DivideThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW - { - if (u == 0) - { - E::SafeIntOnDivZero(); - } - - if (t == 0) - { - result = 0; - return; - } - - result = (T)(t / u); - } -}; - -template -class DivisionHelper -{ -public: - static SafeIntError Divide(const T& t, const U& u, T& result) SAFEINT_NOTHROW - { - if (u == 0) - { - return SafeIntDivideByZero; - } - - if (t == 0) - { - result = 0; - return SafeIntNoError; - } - - // Must test for corner case - if (t == IntTraits::minInt && u == (U)-1) return SafeIntArithmeticOverflow; - - result = (T)(t / u); - return SafeIntNoError; - } - - template - static void DivideThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW - { - if (u == 0) - { - E::SafeIntOnDivZero(); - } - - if (t == 0) - { - result = 0; - return; - } - - // Must test for corner case - if (t == IntTraits::minInt && u == (U)-1) E::SafeIntOnOverflow(); - - result = (T)(t / u); - } -}; - -enum AdditionState -{ - AdditionState_CastIntCheckMax, - AdditionState_CastUintCheckOverflow, - AdditionState_CastUintCheckOverflowMax, - AdditionState_CastUint64CheckOverflow, - AdditionState_CastUint64CheckOverflowMax, - AdditionState_CastIntCheckSafeIntMinMax, - AdditionState_CastInt64CheckSafeIntMinMax, - AdditionState_CastInt64CheckMax, - AdditionState_CastUint64CheckSafeIntMinMax, - AdditionState_CastUint64CheckSafeIntMinMax2, - AdditionState_CastInt64CheckOverflow, - AdditionState_CastInt64CheckOverflowSafeIntMinMax, - AdditionState_CastInt64CheckOverflowMax, - AdditionState_ManualCheckInt64Uint64, - AdditionState_ManualCheck, - AdditionState_Error -}; - -template -class AdditionMethod -{ -public: - enum - { - // unsigned-unsigned - method = - (IntRegion::IntZone_UintLT32_UintLT32 - ? AdditionState_CastIntCheckMax - : (IntRegion::IntZone_Uint32_UintLT64) - ? AdditionState_CastUintCheckOverflow - : (IntRegion::IntZone_UintLT32_Uint32) - ? AdditionState_CastUintCheckOverflowMax - : (IntRegion::IntZone_Uint64_Uint) - ? AdditionState_CastUint64CheckOverflow - : (IntRegion::IntZone_UintLT64_Uint64) - ? AdditionState_CastUint64CheckOverflowMax - : - // unsigned-signed - (IntRegion::IntZone_UintLT32_IntLT32) - ? AdditionState_CastIntCheckSafeIntMinMax - : (IntRegion::IntZone_Uint32_IntLT64 || - IntRegion::IntZone_UintLT32_Int32) - ? AdditionState_CastInt64CheckSafeIntMinMax - : (IntRegion::IntZone_Uint64_Int || - IntRegion::IntZone_Uint64_Int64) - ? AdditionState_CastUint64CheckSafeIntMinMax - : (IntRegion::IntZone_UintLT64_Int64) - ? AdditionState_CastUint64CheckSafeIntMinMax2 - : - // signed-signed - (IntRegion::IntZone_IntLT32_IntLT32) - ? AdditionState_CastIntCheckSafeIntMinMax - : (IntRegion::IntZone_Int32_IntLT64 || - IntRegion::IntZone_IntLT32_Int32) - ? AdditionState_CastInt64CheckSafeIntMinMax - : (IntRegion::IntZone_Int64_Int || - IntRegion::IntZone_Int64_Int64) - ? AdditionState_CastInt64CheckOverflow - : (IntRegion::IntZone_IntLT64_Int64) - ? AdditionState_CastInt64CheckOverflowSafeIntMinMax - : - // signed-unsigned - (IntRegion:: - IntZone_IntLT32_UintLT32) - ? AdditionState_CastIntCheckMax - : (IntRegion:: - IntZone_Int32_UintLT32 || - IntRegion:: - IntZone_IntLT64_Uint32) - ? AdditionState_CastInt64CheckMax - : (IntRegion:: - IntZone_Int64_UintLT64) - ? AdditionState_CastInt64CheckOverflowMax - : (IntRegion:: - IntZone_Int64_Uint64) - ? AdditionState_ManualCheckInt64Uint64 - : (IntRegion< - T, - U>:: - IntZone_Int_Uint64) - ? AdditionState_ManualCheck - : AdditionState_Error) - }; -}; - -template -class AdditionHelper; - -template -class AdditionHelper -{ -public: - static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // 16-bit or less unsigned addition - __int32 tmp = lhs + rhs; - - if (tmp <= (__int32)IntTraits::maxInt) - { - result = (T)tmp; - return true; - } - - return false; - } - - template - static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // 16-bit or less unsigned addition - __int32 tmp = lhs + rhs; - - if (tmp <= (__int32)IntTraits::maxInt) - { - result = (T)tmp; - return; - } - - E::SafeIntOnOverflow(); - } -}; - -template -class AdditionHelper -{ -public: - static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // 32-bit or less - both are unsigned - unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs; - - // we added didn't get smaller - if (tmp >= lhs) - { - result = (T)tmp; - return true; - } - return false; - } - - template - static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // 32-bit or less - both are unsigned - unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs; - - // we added didn't get smaller - if (tmp >= lhs) - { - result = (T)tmp; - return; - } - E::SafeIntOnOverflow(); - } -}; - -template -class AdditionHelper -{ -public: - static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // 32-bit or less - both are unsigned - unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs; - - // We added and it didn't get smaller or exceed maxInt - if (tmp >= lhs && tmp <= IntTraits::maxInt) - { - result = (T)tmp; - return true; - } - return false; - } - - template - static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // 32-bit or less - both are unsigned - unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs; - - // We added and it didn't get smaller or exceed maxInt - if (tmp >= lhs && tmp <= IntTraits::maxInt) - { - result = (T)tmp; - return; - } - E::SafeIntOnOverflow(); - } -}; - -template -class AdditionHelper -{ -public: - static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // lhs unsigned __int64, rhs unsigned - unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; - - // We added and it didn't get smaller - if (tmp >= lhs) - { - result = (T)tmp; - return true; - } - - return false; - } - - template - static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // lhs unsigned __int64, rhs unsigned - unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; - - // We added and it didn't get smaller - if (tmp >= lhs) - { - result = (T)tmp; - return; - } - - E::SafeIntOnOverflow(); - } -}; - -template -class AdditionHelper -{ -public: - static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // lhs unsigned __int64, rhs unsigned - unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; - - // We added and it didn't get smaller - if (tmp >= lhs && tmp <= IntTraits::maxInt) - { - result = (T)tmp; - return true; - } - - return false; - } - - template - static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // lhs unsigned __int64, rhs unsigned - unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; - - // We added and it didn't get smaller - if (tmp >= lhs && tmp <= IntTraits::maxInt) - { - result = (T)tmp; - return; - } - - E::SafeIntOnOverflow(); - } -}; - -template -class AdditionHelper -{ -public: - static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // 16-bit or less - one or both are signed - __int32 tmp = lhs + rhs; - - if (tmp <= (__int32)IntTraits::maxInt && tmp >= (__int32)IntTraits::minInt) - { - result = (T)tmp; - return true; - } - - return false; - } - - template - static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // 16-bit or less - one or both are signed - __int32 tmp = lhs + rhs; - - if (tmp <= (__int32)IntTraits::maxInt && tmp >= (__int32)IntTraits::minInt) - { - result = (T)tmp; - return; - } - - E::SafeIntOnOverflow(); - } -}; - -template -class AdditionHelper -{ -public: - static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // 32-bit or less - one or both are signed - __int64 tmp = (__int64)lhs + (__int64)rhs; - - if (tmp <= (__int64)IntTraits::maxInt && tmp >= (__int64)IntTraits::minInt) - { - result = (T)tmp; - return true; - } - - return false; - } - - template - static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // 32-bit or less - one or both are signed - __int64 tmp = (__int64)lhs + (__int64)rhs; - - if (tmp <= (__int64)IntTraits::maxInt && tmp >= (__int64)IntTraits::minInt) - { - result = (T)tmp; - return; - } - - E::SafeIntOnOverflow(); - } -}; - -template -class AdditionHelper -{ -public: - static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // 32-bit or less - lhs signed, rhs unsigned - __int64 tmp = (__int64)lhs + (__int64)rhs; - - if (tmp <= IntTraits::maxInt) - { - result = (T)tmp; - return true; - } - - return false; - } - - template - static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // 32-bit or less - lhs signed, rhs unsigned - __int64 tmp = (__int64)lhs + (__int64)rhs; - - if (tmp <= IntTraits::maxInt) - { - result = (T)tmp; - return; - } - - E::SafeIntOnOverflow(); - } -}; - -template -class AdditionHelper -{ -public: - static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // lhs is unsigned __int64, rhs signed - unsigned __int64 tmp; - - if (rhs < 0) - { - // So we're effectively subtracting - tmp = AbsValueHelper::method>::Abs(rhs); - - if (tmp <= lhs) - { - result = lhs - tmp; - return true; - } - } - else - { - // now we know that rhs can be safely cast into an unsigned __int64 - tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; - - // We added and it did not become smaller - if (tmp >= lhs) - { - result = (T)tmp; - return true; - } - } - - return false; - } - - template - static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // lhs is unsigned __int64, rhs signed - unsigned __int64 tmp; - - if (rhs < 0) - { - // So we're effectively subtracting - tmp = AbsValueHelper::method>::Abs(rhs); - - if (tmp <= lhs) - { - result = lhs - tmp; - return; - } - } - else - { - // now we know that rhs can be safely cast into an unsigned __int64 - tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; - - // We added and it did not become smaller - if (tmp >= lhs) - { - result = (T)tmp; - return; - } - } - - E::SafeIntOnOverflow(); - } -}; - -template -class AdditionHelper -{ -public: - static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // lhs is unsigned and < 64-bit, rhs signed __int64 - if (rhs < 0) - { - if (lhs >= ~(unsigned __int64)(rhs) + 1) // negation is safe, since rhs is 64-bit - { - result = (T)(lhs + rhs); - return true; - } - } - else - { - // now we know that rhs can be safely cast into an unsigned __int64 - unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; - - // special case - rhs cannot be larger than 0x7fffffffffffffff, lhs cannot be larger than 0xffffffff - // it is not possible for the operation above to overflow, so just check max - if (tmp <= IntTraits::maxInt) - { - result = (T)tmp; - return true; - } - } - return false; - } - - template - static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // lhs is unsigned and < 64-bit, rhs signed __int64 - if (rhs < 0) - { - if (lhs >= ~(unsigned __int64)(rhs) + 1) // negation is safe, since rhs is 64-bit - { - result = (T)(lhs + rhs); - return; - } - } - else - { - // now we know that rhs can be safely cast into an unsigned __int64 - unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; - - // special case - rhs cannot be larger than 0x7fffffffffffffff, lhs cannot be larger than 0xffffffff - // it is not possible for the operation above to overflow, so just check max - if (tmp <= IntTraits::maxInt) - { - result = (T)tmp; - return; - } - } - E::SafeIntOnOverflow(); - } -}; - -template -class AdditionHelper -{ -public: - static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // lhs is signed __int64, rhs signed - __int64 tmp = (__int64)((unsigned __int64)lhs + (unsigned __int64)rhs); - - if (lhs >= 0) - { - // mixed sign cannot overflow - if (rhs >= 0 && tmp < lhs) return false; - } - else - { - // lhs negative - if (rhs < 0 && tmp > lhs) return false; - } - - result = (T)tmp; - return true; - } - - template - static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // lhs is signed __int64, rhs signed - __int64 tmp = (__int64)((unsigned __int64)lhs + (unsigned __int64)rhs); - - if (lhs >= 0) - { - // mixed sign cannot overflow - if (rhs >= 0 && tmp < lhs) E::SafeIntOnOverflow(); - } - else - { - // lhs negative - if (rhs < 0 && tmp > lhs) E::SafeIntOnOverflow(); - } - - result = (T)tmp; - } -}; - -template -class AdditionHelper -{ -public: - static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // rhs is signed __int64, lhs signed - __int64 tmp; - - if (AdditionHelper<__int64, __int64, AdditionState_CastInt64CheckOverflow>::Addition( - (__int64)lhs, (__int64)rhs, tmp) && - tmp <= IntTraits::maxInt && tmp >= IntTraits::minInt) - { - result = (T)tmp; - return true; - } - - return false; - } - - template - static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // rhs is signed __int64, lhs signed - __int64 tmp; - - AdditionHelper<__int64, __int64, AdditionState_CastInt64CheckOverflow>::AdditionThrow( - (__int64)lhs, (__int64)rhs, tmp); - - if (tmp <= IntTraits::maxInt && tmp >= IntTraits::minInt) - { - result = (T)tmp; - return; - } - - E::SafeIntOnOverflow(); - } -}; - -template -class AdditionHelper -{ -public: - static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // lhs is signed __int64, rhs unsigned < 64-bit - unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; - - if ((__int64)tmp >= lhs) - { - result = (T)(__int64)tmp; - return true; - } - - return false; - } - - template - static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // lhs is signed __int64, rhs unsigned < 64-bit - // Some compilers get optimization-happy, let's thwart them - - unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; - - if ((__int64)tmp >= lhs) - { - result = (T)(__int64)tmp; - return; - } - - E::SafeIntOnOverflow(); - } -}; - -template -class AdditionHelper -{ -public: - static bool Addition(const __int64& lhs, const unsigned __int64& rhs, __int64& result) SAFEINT_NOTHROW - { - C_ASSERT(IntTraits::isInt64 && IntTraits::isUint64); - // rhs is unsigned __int64, lhs __int64 - // cast everything to unsigned, perform addition, then - // cast back for check - this is done to stop optimizers from removing the code - unsigned __int64 tmp = (unsigned __int64)lhs + rhs; - - if ((__int64)tmp >= lhs) - { - result = (__int64)tmp; - return true; - } - - return false; - } - - template - static void AdditionThrow(const __int64& lhs, const unsigned __int64& rhs, T& result) SAFEINT_CPP_THROW - { - C_ASSERT(IntTraits::isInt64 && IntTraits::isUint64); - // rhs is unsigned __int64, lhs __int64 - unsigned __int64 tmp = (unsigned __int64)lhs + rhs; - - if ((__int64)tmp >= lhs) - { - result = (__int64)tmp; - return; - } - - E::SafeIntOnOverflow(); - } -}; - -template -class AdditionHelper -{ -public: - static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // rhs is unsigned __int64, lhs signed, 32-bit or less - if ((unsigned __int32)(rhs >> 32) == 0) - { - // Now it just happens to work out that the standard behavior does what we want - // Adding explicit casts to show exactly what's happening here - // Note - this is tweaked to keep optimizers from tossing out the code. - unsigned __int32 tmp = (unsigned __int32)rhs + (unsigned __int32)lhs; - - if ((__int32)tmp >= lhs && - SafeCastHelper::method>::Cast((__int32)tmp, result)) - return true; - } - - return false; - } - - template - static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // rhs is unsigned __int64, lhs signed, 32-bit or less - - if ((unsigned __int32)(rhs >> 32) == 0) - { - // Now it just happens to work out that the standard behavior does what we want - // Adding explicit casts to show exactly what's happening here - unsigned __int32 tmp = (unsigned __int32)rhs + (unsigned __int32)lhs; - - if ((__int32)tmp >= lhs) - { - SafeCastHelper::method>::template CastThrow((__int32)tmp, - result); - return; - } - } - E::SafeIntOnOverflow(); - } -}; - -enum SubtractionState -{ - SubtractionState_BothUnsigned, - SubtractionState_CastIntCheckSafeIntMinMax, - SubtractionState_CastIntCheckMin, - SubtractionState_CastInt64CheckSafeIntMinMax, - SubtractionState_CastInt64CheckMin, - SubtractionState_Uint64Int, - SubtractionState_UintInt64, - SubtractionState_Int64Int, - SubtractionState_IntInt64, - SubtractionState_Int64Uint, - SubtractionState_IntUint64, - SubtractionState_Int64Uint64, - // states for SubtractionMethod2 - SubtractionState_BothUnsigned2, - SubtractionState_CastIntCheckSafeIntMinMax2, - SubtractionState_CastInt64CheckSafeIntMinMax2, - SubtractionState_Uint64Int2, - SubtractionState_UintInt642, - SubtractionState_Int64Int2, - SubtractionState_IntInt642, - SubtractionState_Int64Uint2, - SubtractionState_IntUint642, - SubtractionState_Int64Uint642, - SubtractionState_Error -}; - -template -class SubtractionMethod -{ -public: - enum - { - // unsigned-unsigned - method = - ((IntRegion::IntZone_UintLT32_UintLT32 || (IntRegion::IntZone_Uint32_UintLT64) || - (IntRegion::IntZone_UintLT32_Uint32) || (IntRegion::IntZone_Uint64_Uint) || - (IntRegion::IntZone_UintLT64_Uint64)) - ? SubtractionState_BothUnsigned - : - // unsigned-signed - (IntRegion::IntZone_UintLT32_IntLT32) - ? SubtractionState_CastIntCheckSafeIntMinMax - : (IntRegion::IntZone_Uint32_IntLT64 || IntRegion::IntZone_UintLT32_Int32) - ? SubtractionState_CastInt64CheckSafeIntMinMax - : (IntRegion::IntZone_Uint64_Int || IntRegion::IntZone_Uint64_Int64) - ? SubtractionState_Uint64Int - : (IntRegion::IntZone_UintLT64_Int64) ? SubtractionState_UintInt64 : - // signed-signed - (IntRegion::IntZone_IntLT32_IntLT32) - ? SubtractionState_CastIntCheckSafeIntMinMax - : (IntRegion::IntZone_Int32_IntLT64 || - IntRegion::IntZone_IntLT32_Int32) - ? SubtractionState_CastInt64CheckSafeIntMinMax - : (IntRegion::IntZone_Int64_Int || - IntRegion::IntZone_Int64_Int64) - ? SubtractionState_Int64Int - : (IntRegion::IntZone_IntLT64_Int64) - ? SubtractionState_IntInt64 - : - // signed-unsigned - (IntRegion::IntZone_IntLT32_UintLT32) - ? SubtractionState_CastIntCheckMin - : (IntRegion::IntZone_Int32_UintLT32 || - IntRegion::IntZone_IntLT64_Uint32) - ? SubtractionState_CastInt64CheckMin - : (IntRegion::IntZone_Int64_UintLT64) - ? SubtractionState_Int64Uint - : (IntRegion::IntZone_Int_Uint64) - ? SubtractionState_IntUint64 - : (IntRegion:: - IntZone_Int64_Uint64) - ? SubtractionState_Int64Uint64 - : SubtractionState_Error) - }; -}; - -// this is for the case of U - SafeInt< T, E > -template -class SubtractionMethod2 -{ -public: - enum - { - // unsigned-unsigned - method = - ((IntRegion::IntZone_UintLT32_UintLT32 || (IntRegion::IntZone_Uint32_UintLT64) || - (IntRegion::IntZone_UintLT32_Uint32) || (IntRegion::IntZone_Uint64_Uint) || - (IntRegion::IntZone_UintLT64_Uint64)) - ? SubtractionState_BothUnsigned2 - : - // unsigned-signed - (IntRegion::IntZone_UintLT32_IntLT32) - ? SubtractionState_CastIntCheckSafeIntMinMax2 - : (IntRegion::IntZone_Uint32_IntLT64 || IntRegion::IntZone_UintLT32_Int32) - ? SubtractionState_CastInt64CheckSafeIntMinMax2 - : (IntRegion::IntZone_Uint64_Int || IntRegion::IntZone_Uint64_Int64) - ? SubtractionState_Uint64Int2 - : (IntRegion::IntZone_UintLT64_Int64) ? SubtractionState_UintInt642 : - // signed-signed - (IntRegion::IntZone_IntLT32_IntLT32) - ? SubtractionState_CastIntCheckSafeIntMinMax2 - : (IntRegion::IntZone_Int32_IntLT64 || - IntRegion::IntZone_IntLT32_Int32) - ? SubtractionState_CastInt64CheckSafeIntMinMax2 - : (IntRegion::IntZone_Int64_Int || - IntRegion::IntZone_Int64_Int64) - ? SubtractionState_Int64Int2 - : (IntRegion::IntZone_IntLT64_Int64) - ? SubtractionState_IntInt642 - : - // signed-unsigned - (IntRegion::IntZone_IntLT32_UintLT32) - ? SubtractionState_CastIntCheckSafeIntMinMax2 - : (IntRegion::IntZone_Int32_UintLT32 || - IntRegion::IntZone_IntLT64_Uint32) - ? SubtractionState_CastInt64CheckSafeIntMinMax2 - : (IntRegion::IntZone_Int64_UintLT64) - ? SubtractionState_Int64Uint2 - : (IntRegion::IntZone_Int_Uint64) - ? SubtractionState_IntUint642 - : (IntRegion:: - IntZone_Int64_Uint64) - ? SubtractionState_Int64Uint642 - : SubtractionState_Error) - }; -}; - -template -class SubtractionHelper; - -template -class SubtractionHelper -{ -public: - static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // both are unsigned - easy case - if (rhs <= lhs) - { - result = (T)(lhs - rhs); - return true; - } - - return false; - } - - template - static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // both are unsigned - easy case - if (rhs <= lhs) - { - result = (T)(lhs - rhs); - return; - } - - E::SafeIntOnOverflow(); - } -}; - -template -class SubtractionHelper -{ -public: - static bool Subtract(const T& lhs, const U& rhs, U& result) SAFEINT_NOTHROW - { - // both are unsigned - easy case - // Except we do have to check for overflow - lhs could be larger than result can hold - if (rhs <= lhs) - { - T tmp = (T)(lhs - rhs); - return SafeCastHelper::method>::Cast(tmp, result); - } - - return false; - } - - template - static void SubtractThrow(const T& lhs, const U& rhs, U& result) SAFEINT_CPP_THROW - { - // both are unsigned - easy case - if (rhs <= lhs) - { - T tmp = (T)(lhs - rhs); - SafeCastHelper::method>::template CastThrow(tmp, result); - return; - } - - E::SafeIntOnOverflow(); - } -}; - -template -class SubtractionHelper -{ -public: - static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // both values are 16-bit or less - // rhs is signed, so could end up increasing or decreasing - __int32 tmp = lhs - rhs; - - if (SafeCastHelper::method>::Cast(tmp, result)) - { - result = (T)tmp; - return true; - } - - return false; - } - - template - static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // both values are 16-bit or less - // rhs is signed, so could end up increasing or decreasing - __int32 tmp = lhs - rhs; - - SafeCastHelper::method>::template CastThrow(tmp, result); - } -}; - -template -class SubtractionHelper -{ -public: - static bool Subtract(const U& lhs, const T& rhs, T& result) SAFEINT_NOTHROW - { - // both values are 16-bit or less - // rhs is signed, so could end up increasing or decreasing - __int32 tmp = lhs - rhs; - - return SafeCastHelper::method>::Cast(tmp, result); - } - - template - static void SubtractThrow(const U& lhs, const T& rhs, T& result) SAFEINT_CPP_THROW - { - // both values are 16-bit or less - // rhs is signed, so could end up increasing or decreasing - __int32 tmp = lhs - rhs; - - SafeCastHelper::method>::template CastThrow(tmp, result); - } -}; - -template -class SubtractionHelper -{ -public: - static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // both values are 16-bit or less - // rhs is unsigned - check only minimum - __int32 tmp = lhs - rhs; - - if (tmp >= (__int32)IntTraits::minInt) - { - result = (T)tmp; - return true; - } - - return false; - } - - template - static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // both values are 16-bit or less - // rhs is unsigned - check only minimum - __int32 tmp = lhs - rhs; - - if (tmp >= (__int32)IntTraits::minInt) - { - result = (T)tmp; - return; - } - - E::SafeIntOnOverflow(); - } -}; - -template -class SubtractionHelper -{ -public: - static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // both values are 32-bit or less - // rhs is signed, so could end up increasing or decreasing - __int64 tmp = (__int64)lhs - (__int64)rhs; - - return SafeCastHelper::method>::Cast(tmp, result); - } - - template - static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // both values are 32-bit or less - // rhs is signed, so could end up increasing or decreasing - __int64 tmp = (__int64)lhs - (__int64)rhs; - - SafeCastHelper::method>::template CastThrow(tmp, result); - } -}; - -template -class SubtractionHelper -{ -public: - static bool Subtract(const U& lhs, const T& rhs, T& result) SAFEINT_NOTHROW - { - // both values are 32-bit or less - // rhs is signed, so could end up increasing or decreasing - __int64 tmp = (__int64)lhs - (__int64)rhs; - - return SafeCastHelper::method>::Cast(tmp, result); - } - - template - static void SubtractThrow(const U& lhs, const T& rhs, T& result) SAFEINT_CPP_THROW - { - // both values are 32-bit or less - // rhs is signed, so could end up increasing or decreasing - __int64 tmp = (__int64)lhs - (__int64)rhs; - - SafeCastHelper::method>::template CastThrow(tmp, result); - } -}; - -template -class SubtractionHelper -{ -public: - static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // both values are 32-bit or less - // rhs is unsigned - check only minimum - __int64 tmp = (__int64)lhs - (__int64)rhs; - - if (tmp >= (__int64)IntTraits::minInt) - { - result = (T)tmp; - return true; - } - - return false; - } - - template - static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // both values are 32-bit or less - // rhs is unsigned - check only minimum - __int64 tmp = (__int64)lhs - (__int64)rhs; - - if (tmp >= (__int64)IntTraits::minInt) - { - result = (T)tmp; - return; - } - - E::SafeIntOnOverflow(); - } -}; - -template -class SubtractionHelper -{ -public: - static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // lhs is an unsigned __int64, rhs signed - // must first see if rhs is positive or negative - if (rhs >= 0) - { - if ((unsigned __int64)rhs <= lhs) - { - result = (T)(lhs - (unsigned __int64)rhs); - return true; - } - } - else - { - T tmp = lhs; - // we're now effectively adding - result = lhs + AbsValueHelper::method>::Abs(rhs); - - if (result >= tmp) return true; - } - - return false; - } - - template - static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // lhs is an unsigned __int64, rhs signed - // must first see if rhs is positive or negative - if (rhs >= 0) - { - if ((unsigned __int64)rhs <= lhs) - { - result = (T)(lhs - (unsigned __int64)rhs); - return; - } - } - else - { - T tmp = lhs; - // we're now effectively adding - result = lhs + AbsValueHelper::method>::Abs(rhs); - - if (result >= tmp) return; - } - - E::SafeIntOnOverflow(); - } -}; - -template -class SubtractionHelper -{ -public: - static bool Subtract(const U& lhs, const T& rhs, T& result) SAFEINT_NOTHROW - { - // U is unsigned __int64, T is signed - if (rhs < 0) - { - // treat this as addition - unsigned __int64 tmp; - - tmp = lhs + (unsigned __int64)AbsValueHelper::method>::Abs(rhs); - - // must check for addition overflow and max - if (tmp >= lhs && tmp <= IntTraits::maxInt) - { - result = (T)tmp; - return true; - } - } - else if ((unsigned __int64)rhs > lhs) // now both are positive, so comparison always works - { - // result is negative - // implies that lhs must fit into T, and result cannot overflow - // Also allows us to drop to 32-bit math, which is faster on a 32-bit system - result = (T)lhs - (T)rhs; - return true; - } - else - { - // result is positive - unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; - - if (tmp <= IntTraits::maxInt) - { - result = (T)tmp; - return true; - } - } - - return false; - } - - template - static void SubtractThrow(const U& lhs, const T& rhs, T& result) SAFEINT_CPP_THROW - { - // U is unsigned __int64, T is signed - if (rhs < 0) - { - // treat this as addition - unsigned __int64 tmp; - - tmp = lhs + (unsigned __int64)AbsValueHelper::method>::Abs(rhs); - - // must check for addition overflow and max - if (tmp >= lhs && tmp <= IntTraits::maxInt) - { - result = (T)tmp; - return; - } - } - else if ((unsigned __int64)rhs > lhs) // now both are positive, so comparison always works - { - // result is negative - // implies that lhs must fit into T, and result cannot overflow - // Also allows us to drop to 32-bit math, which is faster on a 32-bit system - result = (T)lhs - (T)rhs; - return; - } - else - { - // result is positive - unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; - - if (tmp <= IntTraits::maxInt) - { - result = (T)tmp; - return; - } - } - - E::SafeIntOnOverflow(); - } -}; - -template -class SubtractionHelper -{ -public: - static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // lhs is an unsigned int32 or smaller, rhs signed __int64 - // must first see if rhs is positive or negative - if (rhs >= 0) - { - if ((unsigned __int64)rhs <= lhs) - { - result = (T)(lhs - (T)rhs); - return true; - } - } - else - { - // we're now effectively adding - // since lhs is 32-bit, and rhs cannot exceed 2^63 - // this addition cannot overflow - unsigned __int64 tmp = lhs + ~(unsigned __int64)(rhs) + 1; // negation safe - - // but we could exceed MaxInt - if (tmp <= IntTraits::maxInt) - { - result = (T)tmp; - return true; - } - } - - return false; - } - - template - static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // lhs is an unsigned int32 or smaller, rhs signed __int64 - // must first see if rhs is positive or negative - if (rhs >= 0) - { - if ((unsigned __int64)rhs <= lhs) - { - result = (T)(lhs - (T)rhs); - return; - } - } - else - { - // we're now effectively adding - // since lhs is 32-bit, and rhs cannot exceed 2^63 - // this addition cannot overflow - unsigned __int64 tmp = lhs + ~(unsigned __int64)(rhs) + 1; // negation safe - - // but we could exceed MaxInt - if (tmp <= IntTraits::maxInt) - { - result = (T)tmp; - return; - } - } - - E::SafeIntOnOverflow(); - } -}; - -template -class SubtractionHelper -{ -public: - static bool Subtract(const U& lhs, const T& rhs, T& result) SAFEINT_NOTHROW - { - // U unsigned 32-bit or less, T __int64 - if (rhs >= 0) - { - // overflow not possible - result = (T)((__int64)lhs - rhs); - return true; - } - else - { - // we effectively have an addition - // which cannot overflow internally - unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)(-rhs); - - if (tmp <= (unsigned __int64)IntTraits::maxInt) - { - result = (T)tmp; - return true; - } - } - - return false; - } - - template - static void SubtractThrow(const U& lhs, const T& rhs, T& result) SAFEINT_CPP_THROW - { - // U unsigned 32-bit or less, T __int64 - if (rhs >= 0) - { - // overflow not possible - result = (T)((__int64)lhs - rhs); - return; - } - else - { - // we effectively have an addition - // which cannot overflow internally - unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)(-rhs); - - if (tmp <= (unsigned __int64)IntTraits::maxInt) - { - result = (T)tmp; - return; - } - } - - E::SafeIntOnOverflow(); - } -}; - -template -class SubtractionHelper -{ -public: - static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // lhs is an __int64, rhs signed (up to 64-bit) - // we have essentially 4 cases: - // - // 1) lhs positive, rhs positive - overflow not possible - // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error - // 3) lhs negative, rhs positive - check result <= lhs - // 4) lhs negative, rhs negative - overflow not possible - - __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs); - - // Note - ideally, we can order these so that true conditionals - // lead to success, which enables better pipelining - // It isn't practical here - if ((lhs >= 0 && rhs < 0 && tmp < lhs) || // condition 2 - (rhs >= 0 && tmp > lhs)) // condition 3 - { - return false; - } - - result = (T)tmp; - return true; - } - - template - static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // lhs is an __int64, rhs signed (up to 64-bit) - // we have essentially 4 cases: - // - // 1) lhs positive, rhs positive - overflow not possible - // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error - // 3) lhs negative, rhs positive - check result <= lhs - // 4) lhs negative, rhs negative - overflow not possible - - __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs); - - // Note - ideally, we can order these so that true conditionals - // lead to success, which enables better pipelining - // It isn't practical here - if ((lhs >= 0 && rhs < 0 && tmp < lhs) || // condition 2 - (rhs >= 0 && tmp > lhs)) // condition 3 - { - E::SafeIntOnOverflow(); - } - - result = (T)tmp; - } -}; - -template -class SubtractionHelper -{ -public: - static bool Subtract(const U& lhs, const T& rhs, T& result) SAFEINT_NOTHROW - { - // lhs __int64, rhs any signed int (including __int64) - __int64 tmp = lhs - rhs; - - // we have essentially 4 cases: - // - // 1) lhs positive, rhs positive - overflow not possible in tmp - // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error - // 3) lhs negative, rhs positive - check result <= lhs - // 4) lhs negative, rhs negative - overflow not possible in tmp - - if (lhs >= 0) - { - // if both positive, overflow to negative not possible - // which is why we'll explicitly check maxInt, and not call SafeCast - if ((IntTraits::isLT64Bit && tmp > IntTraits::maxInt) || (rhs < 0 && tmp < lhs)) - { - return false; - } - } - else - { - // lhs negative - if ((IntTraits::isLT64Bit && tmp < IntTraits::minInt) || (rhs >= 0 && tmp > lhs)) - { - return false; - } - } - - result = (T)tmp; - return true; - } - - template - static void SubtractThrow(const U& lhs, const T& rhs, T& result) SAFEINT_CPP_THROW - { - // lhs __int64, rhs any signed int (including __int64) - __int64 tmp = lhs - rhs; - - // we have essentially 4 cases: - // - // 1) lhs positive, rhs positive - overflow not possible in tmp - // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error - // 3) lhs negative, rhs positive - check result <= lhs - // 4) lhs negative, rhs negative - overflow not possible in tmp - - if (lhs >= 0) - { - // if both positive, overflow to negative not possible - // which is why we'll explicitly check maxInt, and not call SafeCast - if ((CompileConst::isLT64Bit>::Value() && tmp > IntTraits::maxInt) || - (rhs < 0 && tmp < lhs)) - { - E::SafeIntOnOverflow(); - } - } - else - { - // lhs negative - if ((CompileConst::isLT64Bit>::Value() && tmp < IntTraits::minInt) || - (rhs >= 0 && tmp > lhs)) - { - E::SafeIntOnOverflow(); - } - } - - result = (T)tmp; - } -}; - -template -class SubtractionHelper -{ -public: - static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // lhs is a 32-bit int or less, rhs __int64 - // we have essentially 4 cases: - // - // lhs positive, rhs positive - rhs could be larger than lhs can represent - // lhs positive, rhs negative - additive case - check tmp >= lhs and tmp > max int - // lhs negative, rhs positive - check tmp <= lhs and tmp < min int - // lhs negative, rhs negative - addition cannot internally overflow, check against max - - __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs); - - if (lhs >= 0) - { - // first case - if (rhs >= 0) - { - if (tmp >= IntTraits::minInt) - { - result = (T)tmp; - return true; - } - } - else - { - // second case - if (tmp >= lhs && tmp <= IntTraits::maxInt) - { - result = (T)tmp; - return true; - } - } - } - else - { - // lhs < 0 - // third case - if (rhs >= 0) - { - if (tmp <= lhs && tmp >= IntTraits::minInt) - { - result = (T)tmp; - return true; - } - } - else - { - // fourth case - if (tmp <= IntTraits::maxInt) - { - result = (T)tmp; - return true; - } - } - } - - return false; - } - - template - static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // lhs is a 32-bit int or less, rhs __int64 - // we have essentially 4 cases: - // - // lhs positive, rhs positive - rhs could be larger than lhs can represent - // lhs positive, rhs negative - additive case - check tmp >= lhs and tmp > max int - // lhs negative, rhs positive - check tmp <= lhs and tmp < min int - // lhs negative, rhs negative - addition cannot internally overflow, check against max - - __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs); - - if (lhs >= 0) - { - // first case - if (rhs >= 0) - { - if (tmp >= IntTraits::minInt) - { - result = (T)tmp; - return; - } - } - else - { - // second case - if (tmp >= lhs && tmp <= IntTraits::maxInt) - { - result = (T)tmp; - return; - } - } - } - else - { - // lhs < 0 - // third case - if (rhs >= 0) - { - if (tmp <= lhs && tmp >= IntTraits::minInt) - { - result = (T)tmp; - return; - } - } - else - { - // fourth case - if (tmp <= IntTraits::maxInt) - { - result = (T)tmp; - return; - } - } - } - - E::SafeIntOnOverflow(); - } -}; - -template -class SubtractionHelper -{ -public: - static bool Subtract(const U& lhs, const T& rhs, T& result) SAFEINT_NOTHROW - { - // lhs is any signed int32 or smaller, rhs is int64 - __int64 tmp = (__int64)lhs - rhs; - - if ((lhs >= 0 && rhs < 0 && tmp < lhs) || (rhs > 0 && tmp > lhs)) - { - return false; - // else OK - } - - result = (T)tmp; - return true; - } - - template - static void SubtractThrow(const U& lhs, const T& rhs, T& result) SAFEINT_CPP_THROW - { - // lhs is any signed int32 or smaller, rhs is int64 - __int64 tmp = (__int64)lhs - rhs; - - if ((lhs >= 0 && rhs < 0 && tmp < lhs) || (rhs > 0 && tmp > lhs)) - { - E::SafeIntOnOverflow(); - // else OK - } - - result = (T)tmp; - } -}; - -template -class SubtractionHelper -{ -public: - static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // lhs is a 64-bit int, rhs unsigned int32 or smaller - // perform test as unsigned to prevent unwanted optimizations - unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; - - if ((__int64)tmp <= lhs) - { - result = (T)(__int64)tmp; - return true; - } - - return false; - } - - template - static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // lhs is a 64-bit int, rhs unsigned int32 or smaller - // perform test as unsigned to prevent unwanted optimizations - unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; - - if ((__int64)tmp <= lhs) - { - result = (T)tmp; - return; - } - - E::SafeIntOnOverflow(); - } -}; - -template -class SubtractionHelper -{ -public: - // lhs is __int64, rhs is unsigned 32-bit or smaller - static bool Subtract(const U& lhs, const T& rhs, T& result) SAFEINT_NOTHROW - { - // Do this as unsigned to prevent unwanted optimizations - unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; - - if ((__int64)tmp <= IntTraits::maxInt && (__int64)tmp >= IntTraits::minInt) - { - result = (T)(__int64)tmp; - return true; - } - - return false; - } - - template - static void SubtractThrow(const U& lhs, const T& rhs, T& result) SAFEINT_CPP_THROW - { - // Do this as unsigned to prevent unwanted optimizations - unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; - - if ((__int64)tmp <= IntTraits::maxInt && (__int64)tmp >= IntTraits::minInt) - { - result = (T)(__int64)tmp; - return; - } - - E::SafeIntOnOverflow(); - } -}; - -template -class SubtractionHelper -{ -public: - static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW - { - // lhs is any signed int, rhs unsigned int64 - // check against available range - - // We need the absolute value of IntTraits< T >::minInt - // This will give it to us without extraneous compiler warnings - const unsigned __int64 AbsMinIntT = (unsigned __int64)IntTraits::maxInt + 1; - - if (lhs < 0) - { - if (rhs <= AbsMinIntT - AbsValueHelper::method>::Abs(lhs)) - { - result = (T)(lhs - rhs); - return true; - } - } - else - { - if (rhs <= AbsMinIntT + (unsigned __int64)lhs) - { - result = (T)(lhs - rhs); - return true; - } - } - - return false; - } - - template - static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW - { - // lhs is any signed int, rhs unsigned int64 - // check against available range - - // We need the absolute value of IntTraits< T >::minInt - // This will give it to us without extraneous compiler warnings - const unsigned __int64 AbsMinIntT = (unsigned __int64)IntTraits::maxInt + 1; - - if (lhs < 0) - { - if (rhs <= AbsMinIntT - AbsValueHelper::method>::Abs(lhs)) - { - result = (T)(lhs - rhs); - return; - } - } - else - { - if (rhs <= AbsMinIntT + (unsigned __int64)lhs) - { - result = (T)(lhs - rhs); - return; - } - } - - E::SafeIntOnOverflow(); - } -}; - -template -class SubtractionHelper -{ -public: - static bool Subtract(const U& lhs, const T& rhs, T& result) SAFEINT_NOTHROW - { - // We run into upcasting problems on comparison - needs 2 checks - if (lhs >= 0 && (T)lhs >= rhs) - { - result = (T)((U)lhs - (U)rhs); - return true; - } - - return false; - } - - template - static void SubtractThrow(const U& lhs, const T& rhs, T& result) SAFEINT_CPP_THROW - { - // We run into upcasting problems on comparison - needs 2 checks - if (lhs >= 0 && (T)lhs >= rhs) - { - result = (T)((U)lhs - (U)rhs); - return; - } - - E::SafeIntOnOverflow(); - } -}; - -template -class SubtractionHelper -{ -public: - static bool Subtract(const __int64& lhs, const unsigned __int64& rhs, __int64& result) SAFEINT_NOTHROW - { - C_ASSERT(IntTraits::isInt64 && IntTraits::isUint64); - // if we subtract, and it gets larger, there's a problem - // Perform test as unsigned to prevent unwanted optimizations - unsigned __int64 tmp = (unsigned __int64)lhs - rhs; - - if ((__int64)tmp <= lhs) - { - result = (__int64)tmp; - return true; - } - return false; - } - - template - static void SubtractThrow(const __int64& lhs, const unsigned __int64& rhs, T& result) SAFEINT_CPP_THROW - { - C_ASSERT(IntTraits::isInt64 && IntTraits::isUint64); - // if we subtract, and it gets larger, there's a problem - // Perform test as unsigned to prevent unwanted optimizations - unsigned __int64 tmp = (unsigned __int64)lhs - rhs; - - if ((__int64)tmp <= lhs) - { - result = (__int64)tmp; - return; - } - - E::SafeIntOnOverflow(); - } -}; - -template -class SubtractionHelper -{ -public: - // If lhs is negative, immediate problem - return must be positive, and subtracting only makes it - // get smaller. If rhs > lhs, then it would also go negative, which is the other case - static bool Subtract(const __int64& lhs, const unsigned __int64& rhs, T& result) SAFEINT_NOTHROW - { - C_ASSERT(IntTraits::isUint64 && IntTraits::isInt64); - if (lhs >= 0 && (unsigned __int64)lhs >= rhs) - { - result = (unsigned __int64)lhs - rhs; - return true; - } - - return false; - } - - template - static void SubtractThrow(const __int64& lhs, const unsigned __int64& rhs, T& result) SAFEINT_CPP_THROW - { - C_ASSERT(IntTraits::isUint64 && IntTraits::isInt64); - if (lhs >= 0 && (unsigned __int64)lhs >= rhs) - { - result = (unsigned __int64)lhs - rhs; - return; - } - - E::SafeIntOnOverflow(); - } -}; - -enum BinaryState -{ - BinaryState_OK, - BinaryState_Int8, - BinaryState_Int16, - BinaryState_Int32 -}; - -template -class BinaryMethod -{ -public: - enum - { - // If both operands are unsigned OR - // return type is smaller than rhs OR - // return type is larger and rhs is unsigned - // Then binary operations won't produce unexpected results - method = (sizeof(T) <= sizeof(U) || SafeIntCompare::isBothUnsigned || !IntTraits::isSigned) - ? BinaryState_OK - : IntTraits::isInt8 ? BinaryState_Int8 - : IntTraits::isInt16 ? BinaryState_Int16 : BinaryState_Int32 - }; -}; - -#ifdef SAFEINT_DISABLE_BINARY_ASSERT -#define BinaryAssert(x) -#else -#define BinaryAssert(x) SAFEINT_ASSERT(x) -#endif - -template -class BinaryAndHelper; - -template -class BinaryAndHelper -{ -public: - static T And(T lhs, U rhs) SAFEINT_NOTHROW { return (T)(lhs & rhs); } -}; - -template -class BinaryAndHelper -{ -public: - static T And(T lhs, U rhs) SAFEINT_NOTHROW - { - // cast forces sign extension to be zeros - BinaryAssert((lhs & rhs) == (lhs & (unsigned __int8)rhs)); - return (T)(lhs & (unsigned __int8)rhs); - } -}; - -template -class BinaryAndHelper -{ -public: - static T And(T lhs, U rhs) SAFEINT_NOTHROW - { - // cast forces sign extension to be zeros - BinaryAssert((lhs & rhs) == (lhs & (unsigned __int16)rhs)); - return (T)(lhs & (unsigned __int16)rhs); - } -}; - -template -class BinaryAndHelper -{ -public: - static T And(T lhs, U rhs) SAFEINT_NOTHROW - { - // cast forces sign extension to be zeros - BinaryAssert((lhs & rhs) == (lhs & (unsigned __int32)rhs)); - return (T)(lhs & (unsigned __int32)rhs); - } -}; - -template -class BinaryOrHelper; - -template -class BinaryOrHelper -{ -public: - static T Or(T lhs, U rhs) SAFEINT_NOTHROW { return (T)(lhs | rhs); } -}; - -template -class BinaryOrHelper -{ -public: - static T Or(T lhs, U rhs) SAFEINT_NOTHROW - { - // cast forces sign extension to be zeros - BinaryAssert((lhs | rhs) == (lhs | (unsigned __int8)rhs)); - return (T)(lhs | (unsigned __int8)rhs); - } -}; - -template -class BinaryOrHelper -{ -public: - static T Or(T lhs, U rhs) SAFEINT_NOTHROW - { - // cast forces sign extension to be zeros - BinaryAssert((lhs | rhs) == (lhs | (unsigned __int16)rhs)); - return (T)(lhs | (unsigned __int16)rhs); - } -}; - -template -class BinaryOrHelper -{ -public: - static T Or(T lhs, U rhs) SAFEINT_NOTHROW - { - // cast forces sign extension to be zeros - BinaryAssert((lhs | rhs) == (lhs | (unsigned __int32)rhs)); - return (T)(lhs | (unsigned __int32)rhs); - } -}; - -template -class BinaryXorHelper; - -template -class BinaryXorHelper -{ -public: - static T Xor(T lhs, U rhs) SAFEINT_NOTHROW { return (T)(lhs ^ rhs); } -}; - -template -class BinaryXorHelper -{ -public: - static T Xor(T lhs, U rhs) SAFEINT_NOTHROW - { - // cast forces sign extension to be zeros - BinaryAssert((lhs ^ rhs) == (lhs ^ (unsigned __int8)rhs)); - return (T)(lhs ^ (unsigned __int8)rhs); - } -}; - -template -class BinaryXorHelper -{ -public: - static T Xor(T lhs, U rhs) SAFEINT_NOTHROW - { - // cast forces sign extension to be zeros - BinaryAssert((lhs ^ rhs) == (lhs ^ (unsigned __int16)rhs)); - return (T)(lhs ^ (unsigned __int16)rhs); - } -}; - -template -class BinaryXorHelper -{ -public: - static T Xor(T lhs, U rhs) SAFEINT_NOTHROW - { - // cast forces sign extension to be zeros - BinaryAssert((lhs ^ rhs) == (lhs ^ (unsigned __int32)rhs)); - return (T)(lhs ^ (unsigned __int32)rhs); - } -}; - -/***************** External functions ****************************************/ - -// External functions that can be used where you only need to check one operation -// non-class helper function so that you can check for a cast's validity -// and handle errors how you like -template -inline bool SafeCast(const T From, U& To) SAFEINT_NOTHROW -{ - return SafeCastHelper::method>::Cast(From, To); -} - -template -inline bool SafeEquals(const T t, const U u) SAFEINT_NOTHROW -{ - return EqualityTest::method>::IsEquals(t, u); -} - -template -inline bool SafeNotEquals(const T t, const U u) SAFEINT_NOTHROW -{ - return !EqualityTest::method>::IsEquals(t, u); -} - -template -inline bool SafeGreaterThan(const T t, const U u) SAFEINT_NOTHROW -{ - return GreaterThanTest::method>::GreaterThan(t, u); -} - -template -inline bool SafeGreaterThanEquals(const T t, const U u) SAFEINT_NOTHROW -{ - return !GreaterThanTest::method>::GreaterThan(u, t); -} - -template -inline bool SafeLessThan(const T t, const U u) SAFEINT_NOTHROW -{ - return GreaterThanTest::method>::GreaterThan(u, t); -} - -template -inline bool SafeLessThanEquals(const T t, const U u) SAFEINT_NOTHROW -{ - return !GreaterThanTest::method>::GreaterThan(t, u); -} - -template -inline bool SafeModulus(const T& t, const U& u, T& result) SAFEINT_NOTHROW -{ - return (ModulusHelper::method>::Modulus(t, u, result) == SafeIntNoError); -} - -template -inline bool SafeMultiply(T t, U u, T& result) SAFEINT_NOTHROW -{ - return MultiplicationHelper::method>::Multiply(t, u, result); -} - -template -inline bool SafeDivide(T t, U u, T& result) SAFEINT_NOTHROW -{ - return (DivisionHelper::method>::Divide(t, u, result) == SafeIntNoError); -} - -template -inline bool SafeAdd(T t, U u, T& result) SAFEINT_NOTHROW -{ - return AdditionHelper::method>::Addition(t, u, result); -} - -template -inline bool SafeSubtract(T t, U u, T& result) SAFEINT_NOTHROW -{ - return SubtractionHelper::method>::Subtract(t, u, result); -} - -/***************** end external functions ************************************/ - -// Main SafeInt class -// Assumes exceptions can be thrown -template -class SafeInt -{ -public: - SafeInt() SAFEINT_NOTHROW - { - C_ASSERT(NumericType::isInt); - m_int = 0; - } - - // Having a constructor for every type of int - // avoids having the compiler evade our checks when doing implicit casts - - // e.g., SafeInt s = 0x7fffffff; - SafeInt(const T& i) SAFEINT_NOTHROW - { - C_ASSERT(NumericType::isInt); - // always safe - m_int = i; - } - - // provide explicit boolean converter - SafeInt(bool b) SAFEINT_NOTHROW - { - C_ASSERT(NumericType::isInt); - m_int = (T)(b ? 1 : 0); - } - - template - SafeInt(const SafeInt& u) SAFEINT_CPP_THROW - { - C_ASSERT(NumericType::isInt); - *this = SafeInt((U)u); - } - - template - SafeInt(const U& i) SAFEINT_CPP_THROW - { - C_ASSERT(NumericType::isInt); - // SafeCast will throw exceptions if i won't fit in type T - SafeCastHelper::method>::template CastThrow(i, m_int); - } - - // The destructor is intentionally commented out - no destructor - // vs. a do-nothing destructor makes a huge difference in - // inlining characteristics. It wasn't doing anything anyway. - // ~SafeInt(){}; - - // now start overloading operators - // assignment operator - // constructors exist for all int types and will ensure safety - - template - SafeInt& operator=(const U& rhs) SAFEINT_CPP_THROW - { - // use constructor to test size - // constructor is optimized to do minimal checking based - // on whether T can contain U - // note - do not change this - *this = SafeInt(rhs); - return *this; - } - - SafeInt& operator=(const T& rhs) SAFEINT_NOTHROW - { - m_int = rhs; - return *this; - } - - template - SafeInt& operator=(const SafeInt& rhs) SAFEINT_CPP_THROW - { - SafeCastHelper::method>::template CastThrow(rhs.Ref(), m_int); - return *this; - } - - SafeInt& operator=(const SafeInt& rhs) SAFEINT_NOTHROW - { - m_int = rhs.m_int; - return *this; - } - - // Casting operators - - operator bool() const SAFEINT_NOTHROW { return !!m_int; } - - operator char() const SAFEINT_CPP_THROW - { - char val; - SafeCastHelper::method>::template CastThrow(m_int, val); - return val; - } - - operator signed char() const SAFEINT_CPP_THROW - { - signed char val; - SafeCastHelper::method>::template CastThrow(m_int, val); - return val; - } - - operator unsigned char() const SAFEINT_CPP_THROW - { - unsigned char val; - SafeCastHelper::method>::template CastThrow(m_int, val); - return val; - } - - operator __int16() const SAFEINT_CPP_THROW - { - __int16 val; - SafeCastHelper<__int16, T, GetCastMethod<__int16, T>::method>::template CastThrow(m_int, val); - return val; - } - - operator unsigned __int16() const SAFEINT_CPP_THROW - { - unsigned __int16 val; - SafeCastHelper::method>::template CastThrow(m_int, - val); - return val; - } - - operator __int32() const SAFEINT_CPP_THROW - { - __int32 val; - SafeCastHelper<__int32, T, GetCastMethod<__int32, T>::method>::template CastThrow(m_int, val); - return val; - } - - operator unsigned __int32() const SAFEINT_CPP_THROW - { - unsigned __int32 val; - SafeCastHelper::method>::template CastThrow(m_int, - val); - return val; - } - - // The compiler knows that int == __int32 - // but not that long == __int32 - operator long() const SAFEINT_CPP_THROW - { - long val; - SafeCastHelper::method>::template CastThrow(m_int, val); - return val; - } - - operator unsigned long() const SAFEINT_CPP_THROW - { - unsigned long val; - SafeCastHelper::method>::template CastThrow(m_int, val); - return val; - } - - operator __int64() const SAFEINT_CPP_THROW - { - __int64 val; - SafeCastHelper<__int64, T, GetCastMethod<__int64, T>::method>::template CastThrow(m_int, val); - return val; - } - - operator unsigned __int64() const SAFEINT_CPP_THROW - { - unsigned __int64 val; - SafeCastHelper::method>::template CastThrow(m_int, - val); - return val; - } - -#if defined SAFEINT_USE_WCHAR_T || defined _NATIVE_WCHAR_T_DEFINED - operator wchar_t() const SAFEINT_CPP_THROW - { - wchar_t val; - SafeCastHelper::method>::template CastThrow(m_int, val); - return val; - } -#endif - -#ifdef SIZE_T_CAST_NEEDED - // We also need an explicit cast to size_t, or the compiler will complain - // Apparently, only SOME compilers complain, and cl 14.00.50727.42 isn't one of them - // Leave here in case we decide to backport this to an earlier compiler - operator size_t() const SAFEINT_CPP_THROW - { - size_t val; - SafeCastHelper::method>::template CastThrow(m_int, val); - return val; - } -#endif - - // Also provide a cast operator for floating point types - operator float() const SAFEINT_CPP_THROW - { - float val; - SafeCastHelper::method>::template CastThrow(m_int, val); - return val; - } - - operator double() const SAFEINT_CPP_THROW - { - double val; - SafeCastHelper::method>::template CastThrow(m_int, val); - return val; - } - operator long double() const SAFEINT_CPP_THROW - { - long double val; - SafeCastHelper::method>::template CastThrow(m_int, val); - return val; - } - - // If you need a pointer to the data - // this could be dangerous, but allows you to correctly pass - // instances of this class to APIs that take a pointer to an integer - // also see overloaded address-of operator below - T* Ptr() SAFEINT_NOTHROW { return &m_int; } - const T* Ptr() const SAFEINT_NOTHROW { return &m_int; } - const T& Ref() const SAFEINT_NOTHROW { return m_int; } - - // Or if SafeInt< T, E >::Ptr() is inconvenient, use the overload - // operator & - // This allows you to do unsafe things! - // It is meant to allow you to more easily - // pass a SafeInt into things like ReadFile - T* operator&() SAFEINT_NOTHROW { return &m_int; } - const T* operator&() const SAFEINT_NOTHROW { return &m_int; } - - // Unary operators - bool operator!() const SAFEINT_NOTHROW { return (!m_int) ? true : false; } - - // operator + (unary) - // note - normally, the '+' and '-' operators will upcast to a signed int - // for T < 32 bits. This class changes behavior to preserve type - const SafeInt& operator+() const SAFEINT_NOTHROW { return *this; } - - // unary - - - SafeInt operator-() const SAFEINT_CPP_THROW - { - // Note - unsigned still performs the bitwise manipulation - // will warn at level 2 or higher if the value is 32-bit or larger - return SafeInt(NegationHelper::isSigned>::template NegativeThrow(m_int)); - } - - // prefix increment operator - SafeInt& operator++() SAFEINT_CPP_THROW - { - if (m_int != IntTraits::maxInt) - { - ++m_int; - return *this; - } - E::SafeIntOnOverflow(); - } - - // prefix decrement operator - SafeInt& operator--() SAFEINT_CPP_THROW - { - if (m_int != IntTraits::minInt) - { - --m_int; - return *this; - } - E::SafeIntOnOverflow(); - } - - // note that postfix operators have inherently worse perf - // characteristics - - // postfix increment operator - SafeInt operator++(int) SAFEINT_CPP_THROW // dummy arg to comply with spec - { - if (m_int != IntTraits::maxInt) - { - SafeInt tmp(m_int); - - m_int++; - return tmp; - } - E::SafeIntOnOverflow(); - } - - // postfix decrement operator - SafeInt operator--(int) SAFEINT_CPP_THROW // dummy arg to comply with spec - { - if (m_int != IntTraits::minInt) - { - SafeInt tmp(m_int); - m_int--; - return tmp; - } - E::SafeIntOnOverflow(); - } - - // One's complement - // Note - this operator will normally change size to an int - // cast in return improves perf and maintains type - SafeInt operator~() const SAFEINT_NOTHROW { return SafeInt((T)~m_int); } - - // Binary operators - // - // arithmetic binary operators - // % modulus - // * multiplication - // / division - // + addition - // - subtraction - // - // For each of the arithmetic operators, you will need to - // use them as follows: - // - // SafeInt c = 2; - // SafeInt i = 3; - // - // SafeInt i2 = i op (char)c; - // OR - // SafeInt i2 = (int)i op c; - // - // The base problem is that if the lhs and rhs inputs are different SafeInt types - // it is not possible in this implementation to determine what type of SafeInt - // should be returned. You have to let the class know which of the two inputs - // need to be the return type by forcing the other value to the base integer type. - // - // Note - as per feedback from Scott Meyers, I'm exploring how to get around this. - // 3.0 update - I'm still thinking about this. It can be done with template metaprogramming, - // but it is tricky, and there's a perf vs. correctness tradeoff where the right answer - // is situational. - // - // The case of: - // - // SafeInt< T, E > i, j, k; - // i = j op k; - // - // works just fine and no unboxing is needed because the return type is not ambiguous. - - // Modulus - // Modulus has some convenient properties - - // first, the magnitude of the return can never be - // larger than the lhs operand, and it must be the same sign - // as well. It does, however, suffer from the same promotion - // problems as comparisons, division and other operations - template - SafeInt operator%(U rhs) const SAFEINT_CPP_THROW - { - T result; - ModulusHelper::method>::template ModulusThrow(m_int, rhs, result); - return SafeInt(result); - } - - SafeInt operator%(SafeInt rhs) const SAFEINT_CPP_THROW - { - T result; - ModulusHelper::method>::template ModulusThrow(m_int, rhs, result); - return SafeInt(result); - } - - // Modulus assignment - template - SafeInt& operator%=(U rhs) SAFEINT_CPP_THROW - { - ModulusHelper::method>::template ModulusThrow(m_int, rhs, m_int); - return *this; - } - - template - SafeInt& operator%=(SafeInt rhs) SAFEINT_CPP_THROW - { - ModulusHelper::method>::template ModulusThrow(m_int, (U)rhs, m_int); - return *this; - } - - // Multiplication - template - SafeInt operator*(U rhs) const SAFEINT_CPP_THROW - { - T ret(0); - MultiplicationHelper::method>::template MultiplyThrow(m_int, rhs, ret); - return SafeInt(ret); - } - - SafeInt operator*(SafeInt rhs) const SAFEINT_CPP_THROW - { - T ret(0); - MultiplicationHelper::method>::template MultiplyThrow(m_int, (T)rhs, ret); - return SafeInt(ret); - } - - // Multiplication assignment - SafeInt& operator*=(SafeInt rhs) SAFEINT_CPP_THROW - { - MultiplicationHelper::method>::template MultiplyThrow(m_int, (T)rhs, m_int); - return *this; - } - - template - SafeInt& operator*=(U rhs) SAFEINT_CPP_THROW - { - MultiplicationHelper::method>::template MultiplyThrow(m_int, rhs, m_int); - return *this; - } - - template - SafeInt& operator*=(SafeInt rhs) SAFEINT_CPP_THROW - { - MultiplicationHelper::method>::template MultiplyThrow( - m_int, rhs.Ref(), m_int); - return *this; - } - - // Division - template - SafeInt operator/(U rhs) const SAFEINT_CPP_THROW - { - T ret(0); - DivisionHelper::method>::template DivideThrow(m_int, rhs, ret); - return SafeInt(ret); - } - - SafeInt operator/(SafeInt rhs) const SAFEINT_CPP_THROW - { - T ret(0); - DivisionHelper::method>::template DivideThrow(m_int, (T)rhs, ret); - return SafeInt(ret); - } - - // Division assignment - SafeInt& operator/=(SafeInt i) SAFEINT_CPP_THROW - { - DivisionHelper::method>::template DivideThrow(m_int, (T)i, m_int); - return *this; - } - - template - SafeInt& operator/=(U i) SAFEINT_CPP_THROW - { - DivisionHelper::method>::template DivideThrow(m_int, i, m_int); - return *this; - } - - template - SafeInt& operator/=(SafeInt i) - { - DivisionHelper::method>::template DivideThrow(m_int, (U)i, m_int); - return *this; - } - - // For addition and subtraction - - // Addition - SafeInt operator+(SafeInt rhs) const SAFEINT_CPP_THROW - { - T ret(0); - AdditionHelper::method>::template AdditionThrow(m_int, (T)rhs, ret); - return SafeInt(ret); - } - - template - SafeInt operator+(U rhs) const SAFEINT_CPP_THROW - { - T ret(0); - AdditionHelper::method>::template AdditionThrow(m_int, rhs, ret); - return SafeInt(ret); - } - - // addition assignment - SafeInt& operator+=(SafeInt rhs) SAFEINT_CPP_THROW - { - AdditionHelper::method>::template AdditionThrow(m_int, (T)rhs, m_int); - return *this; - } - - template - SafeInt& operator+=(U rhs) SAFEINT_CPP_THROW - { - AdditionHelper::method>::template AdditionThrow(m_int, rhs, m_int); - return *this; - } - - template - SafeInt& operator+=(SafeInt rhs) SAFEINT_CPP_THROW - { - AdditionHelper::method>::template AdditionThrow(m_int, (U)rhs, m_int); - return *this; - } - - // Subtraction - template - SafeInt operator-(U rhs) const SAFEINT_CPP_THROW - { - T ret(0); - SubtractionHelper::method>::template SubtractThrow(m_int, rhs, ret); - return SafeInt(ret); - } - - SafeInt operator-(SafeInt rhs) const SAFEINT_CPP_THROW - { - T ret(0); - SubtractionHelper::method>::template SubtractThrow(m_int, (T)rhs, ret); - return SafeInt(ret); - } - - // Subtraction assignment - SafeInt& operator-=(SafeInt rhs) SAFEINT_CPP_THROW - { - SubtractionHelper::method>::template SubtractThrow(m_int, (T)rhs, m_int); - return *this; - } - - template - SafeInt& operator-=(U rhs) SAFEINT_CPP_THROW - { - SubtractionHelper::method>::template SubtractThrow(m_int, rhs, m_int); - return *this; - } - - template - SafeInt& operator-=(SafeInt rhs) SAFEINT_CPP_THROW - { - SubtractionHelper::method>::template SubtractThrow(m_int, (U)rhs, m_int); - return *this; - } - - // Shift operators - // Note - shift operators ALWAYS return the same type as the lhs - // specific version for SafeInt< T, E > not needed - - // code path is exactly the same as for SafeInt< U, E > as rhs - - // Left shift - // Also, shifting > bitcount is undefined - trap in debug -#ifdef SAFEINT_DISABLE_SHIFT_ASSERT -#define ShiftAssert(x) -#else -#define ShiftAssert(x) SAFEINT_ASSERT(x) -#endif - - template - SafeInt operator<<(U bits) const SAFEINT_NOTHROW - { - ShiftAssert(!IntTraits::isSigned || bits >= 0); - ShiftAssert(bits < (int)IntTraits::bitCount); - - return SafeInt((T)(m_int << bits)); - } - - template - SafeInt operator<<(SafeInt bits) const SAFEINT_NOTHROW - { - ShiftAssert(!IntTraits::isSigned || (U)bits >= 0); - ShiftAssert((U)bits < (int)IntTraits::bitCount); - - return SafeInt((T)(m_int << (U)bits)); - } - - // Left shift assignment - - template - SafeInt& operator<<=(U bits) SAFEINT_NOTHROW - { - ShiftAssert(!IntTraits::isSigned || bits >= 0); - ShiftAssert(bits < (int)IntTraits::bitCount); - - m_int <<= bits; - return *this; - } - - template - SafeInt& operator<<=(SafeInt bits) SAFEINT_NOTHROW - { - ShiftAssert(!IntTraits::isSigned || (U)bits >= 0); - ShiftAssert((U)bits < (int)IntTraits::bitCount); - - m_int <<= (U)bits; - return *this; - } - - // Right shift - template - SafeInt operator>>(U bits) const SAFEINT_NOTHROW - { - ShiftAssert(!IntTraits::isSigned || bits >= 0); - ShiftAssert(bits < (int)IntTraits::bitCount); - - return SafeInt((T)(m_int >> bits)); - } - - template - SafeInt operator>>(SafeInt bits) const SAFEINT_NOTHROW - { - ShiftAssert(!IntTraits::isSigned || (U)bits >= 0); - ShiftAssert(bits < (int)IntTraits::bitCount); - - return SafeInt((T)(m_int >> (U)bits)); - } - - // Right shift assignment - template - SafeInt& operator>>=(U bits) SAFEINT_NOTHROW - { - ShiftAssert(!IntTraits::isSigned || bits >= 0); - ShiftAssert(bits < (int)IntTraits::bitCount); - - m_int >>= bits; - return *this; - } - - template - SafeInt& operator>>=(SafeInt bits) SAFEINT_NOTHROW - { - ShiftAssert(!IntTraits::isSigned || (U)bits >= 0); - ShiftAssert((U)bits < (int)IntTraits::bitCount); - - m_int >>= (U)bits; - return *this; - } - - // Bitwise operators - // This only makes sense if we're dealing with the same type and size - // demand a type T, or something that fits into a type T - - // Bitwise & - SafeInt operator&(SafeInt rhs) const SAFEINT_NOTHROW { return SafeInt(m_int & (T)rhs); } - - template - SafeInt operator&(U rhs) const SAFEINT_NOTHROW - { - // we want to avoid setting bits by surprise - // consider the case of lhs = int, value = 0xffffffff - // rhs = char, value = 0xff - // - // programmer intent is to get only the lower 8 bits - // normal behavior is to upcast both sides to an int - // which then sign extends rhs, setting all the bits - - // If you land in the assert, this is because the bitwise operator - // was causing unexpected behavior. Fix is to properly cast your inputs - // so that it works like you meant, not unexpectedly - - return SafeInt(BinaryAndHelper::method>::And(m_int, rhs)); - } - - // Bitwise & assignment - SafeInt& operator&=(SafeInt rhs) SAFEINT_NOTHROW - { - m_int &= (T)rhs; - return *this; - } - - template - SafeInt& operator&=(U rhs) SAFEINT_NOTHROW - { - m_int = BinaryAndHelper::method>::And(m_int, rhs); - return *this; - } - - template - SafeInt& operator&=(SafeInt rhs) SAFEINT_NOTHROW - { - m_int = BinaryAndHelper::method>::And(m_int, (U)rhs); - return *this; - } - - // XOR - SafeInt operator^(SafeInt rhs) const SAFEINT_NOTHROW { return SafeInt((T)(m_int ^ (T)rhs)); } - - template - SafeInt operator^(U rhs) const SAFEINT_NOTHROW - { - // If you land in the assert, this is because the bitwise operator - // was causing unexpected behavior. Fix is to properly cast your inputs - // so that it works like you meant, not unexpectedly - - return SafeInt(BinaryXorHelper::method>::Xor(m_int, rhs)); - } - - // XOR assignment - SafeInt& operator^=(SafeInt rhs) SAFEINT_NOTHROW - { - m_int ^= (T)rhs; - return *this; - } - - template - SafeInt& operator^=(U rhs) SAFEINT_NOTHROW - { - m_int = BinaryXorHelper::method>::Xor(m_int, rhs); - return *this; - } - - template - SafeInt& operator^=(SafeInt rhs) SAFEINT_NOTHROW - { - m_int = BinaryXorHelper::method>::Xor(m_int, (U)rhs); - return *this; - } - - // bitwise OR - SafeInt operator|(SafeInt rhs) const SAFEINT_NOTHROW { return SafeInt((T)(m_int | (T)rhs)); } - - template - SafeInt operator|(U rhs) const SAFEINT_NOTHROW - { - return SafeInt(BinaryOrHelper::method>::Or(m_int, rhs)); - } - - // bitwise OR assignment - SafeInt& operator|=(SafeInt rhs) SAFEINT_NOTHROW - { - m_int |= (T)rhs; - return *this; - } - - template - SafeInt& operator|=(U rhs) SAFEINT_NOTHROW - { - m_int = BinaryOrHelper::method>::Or(m_int, rhs); - return *this; - } - - template - SafeInt& operator|=(SafeInt rhs) SAFEINT_NOTHROW - { - m_int = BinaryOrHelper::method>::Or(m_int, (U)rhs); - return *this; - } - - // Miscellaneous helper functions - SafeInt Min(SafeInt test, const T floor = IntTraits::minInt) const SAFEINT_NOTHROW - { - T tmp = test < m_int ? (T)test : m_int; - return tmp < floor ? floor : tmp; - } - - SafeInt Max(SafeInt test, const T upper = IntTraits::maxInt) const SAFEINT_NOTHROW - { - T tmp = test > m_int ? (T)test : m_int; - return tmp > upper ? upper : tmp; - } - - void Swap(SafeInt& with) SAFEINT_NOTHROW - { - T temp(m_int); - m_int = with.m_int; - with.m_int = temp; - } - - static SafeInt SafeAtoI(const char* input) SAFEINT_CPP_THROW { return SafeTtoI(input); } - - static SafeInt SafeWtoI(const wchar_t* input) { return SafeTtoI(input); } - - enum alignBits - { - align2 = 1, - align4 = 2, - align8 = 3, - align16 = 4, - align32 = 5, - align64 = 6, - align128 = 7, - align256 = 8 - }; - - template - const SafeInt& Align() SAFEINT_CPP_THROW - { - // Zero is always aligned - if (m_int == 0) return *this; - - // We don't support aligning negative numbers at this time - // Can't align unsigned numbers on bitCount (e.g., 8 bits = 256, unsigned char max = 255) - // or signed numbers on bitCount-1 (e.g., 7 bits = 128, signed char max = 127). - // Also makes no sense to try to align on negative or no bits. - - ShiftAssert(((IntTraits::isSigned && bits < (int)IntTraits::bitCount - 1) || - (!IntTraits::isSigned && bits < (int)IntTraits::bitCount)) && - bits >= 0 && (!IntTraits::isSigned || m_int > 0)); - - const T AlignValue = ((T)1 << bits) - 1; - - m_int = (T)((m_int + AlignValue) & ~AlignValue); - - if (m_int <= 0) E::SafeIntOnOverflow(); - - return *this; - } - - // Commonly needed alignments: - const SafeInt& Align2() { return Align(); } - const SafeInt& Align4() { return Align(); } - const SafeInt& Align8() { return Align(); } - const SafeInt& Align16() { return Align(); } - const SafeInt& Align32() { return Align(); } - const SafeInt& Align64() { return Align(); } - -private: - // This is almost certainly not the best optimized version of atoi, - // but it does not display a typical bug where it isn't possible to set MinInt - // and it won't allow you to overflow your integer. - // This is here because it is useful, and it is an example of what - // can be done easily with SafeInt. - template - static SafeInt SafeTtoI(U* input) SAFEINT_CPP_THROW - { - U* tmp = input; - SafeInt s; - bool negative = false; - - // Bad input, or empty string - if (input == nullptr || input[0] == 0) E::SafeIntOnOverflow(); - - switch (*tmp) - { - case '-': - tmp++; - negative = true; - break; - case '+': tmp++; break; - } - - while (*tmp != 0) - { - if (*tmp < '0' || *tmp > '9') break; - - if ((T)s != 0) s *= (T)10; - - if (!negative) - s += (T)(*tmp - '0'); - else - s -= (T)(*tmp - '0'); - - tmp++; - } - - return s; - } - - T m_int; -}; - -// Helper function used to subtract pointers. -// Used to squelch warnings -template -SafeInt SafePtrDiff(const P* p1, const P* p2) SAFEINT_CPP_THROW -{ - return SafeInt(p1 - p2); -} - -// Comparison operators - -// Less than -template -bool operator<(U lhs, SafeInt rhs) SAFEINT_NOTHROW -{ - return GreaterThanTest::method>::GreaterThan((T)rhs, lhs); -} - -template -bool operator<(SafeInt lhs, U rhs) SAFEINT_NOTHROW -{ - return GreaterThanTest::method>::GreaterThan(rhs, (T)lhs); -} - -template -bool operator<(SafeInt lhs, SafeInt rhs) SAFEINT_NOTHROW -{ - return GreaterThanTest::method>::GreaterThan((T)rhs, (U)lhs); -} - -// Greater than -template -bool operator>(U lhs, SafeInt rhs) SAFEINT_NOTHROW -{ - return GreaterThanTest::method>::GreaterThan(lhs, (T)rhs); -} - -template -bool operator>(SafeInt lhs, U rhs) SAFEINT_NOTHROW -{ - return GreaterThanTest::method>::GreaterThan((T)lhs, rhs); -} - -template -bool operator>(SafeInt lhs, SafeInt rhs) SAFEINT_NOTHROW -{ - return GreaterThanTest::method>::GreaterThan((T)lhs, (U)rhs); -} - -// Greater than or equal -template -bool operator>=(U lhs, SafeInt rhs) SAFEINT_NOTHROW -{ - return !GreaterThanTest::method>::GreaterThan((T)rhs, lhs); -} - -template -bool operator>=(SafeInt lhs, U rhs) SAFEINT_NOTHROW -{ - return !GreaterThanTest::method>::GreaterThan(rhs, (T)lhs); -} - -template -bool operator>=(SafeInt lhs, SafeInt rhs) SAFEINT_NOTHROW -{ - return !GreaterThanTest::method>::GreaterThan((U)rhs, (T)lhs); -} - -// Less than or equal -template -bool operator<=(U lhs, SafeInt rhs) SAFEINT_NOTHROW -{ - return !GreaterThanTest::method>::GreaterThan(lhs, (T)rhs); -} - -template -bool operator<=(SafeInt lhs, U rhs) SAFEINT_NOTHROW -{ - return !GreaterThanTest::method>::GreaterThan((T)lhs, rhs); -} - -template -bool operator<=(SafeInt lhs, SafeInt rhs) SAFEINT_NOTHROW -{ - return !GreaterThanTest::method>::GreaterThan((T)lhs, (U)rhs); -} - -// equality -// explicit overload for bool -template -bool operator==(bool lhs, SafeInt rhs) SAFEINT_NOTHROW -{ - return lhs == ((T)rhs == 0 ? false : true); -} - -template -bool operator==(SafeInt lhs, bool rhs) SAFEINT_NOTHROW -{ - return rhs == ((T)lhs == 0 ? false : true); -} - -template -bool operator==(U lhs, SafeInt rhs) SAFEINT_NOTHROW -{ - return EqualityTest::method>::IsEquals((T)rhs, lhs); -} - -template -bool operator==(SafeInt lhs, U rhs) SAFEINT_NOTHROW -{ - return EqualityTest::method>::IsEquals((T)lhs, rhs); -} - -template -bool operator==(SafeInt lhs, SafeInt rhs) SAFEINT_NOTHROW -{ - return EqualityTest::method>::IsEquals((T)lhs, (U)rhs); -} - -// not equals -template -bool operator!=(U lhs, SafeInt rhs) SAFEINT_NOTHROW -{ - return !EqualityTest::method>::IsEquals((T)rhs, lhs); -} - -template -bool operator!=(SafeInt lhs, U rhs) SAFEINT_NOTHROW -{ - return !EqualityTest::method>::IsEquals((T)lhs, rhs); -} - -template -bool operator!=(SafeInt lhs, SafeInt rhs) SAFEINT_NOTHROW -{ - return !EqualityTest::method>::IsEquals(lhs, rhs); -} - -template -bool operator!=(bool lhs, SafeInt rhs) SAFEINT_NOTHROW -{ - return ((T)rhs == 0 ? false : true) != lhs; -} - -template -bool operator!=(SafeInt lhs, bool rhs) SAFEINT_NOTHROW -{ - return ((T)lhs == 0 ? false : true) != rhs; -} - -template -class ModulusSimpleCaseHelper; - -template -class ModulusSignedCaseHelper; - -template -class ModulusSignedCaseHelper -{ -public: - static bool SignedCase(SafeInt rhs, SafeInt& result) SAFEINT_NOTHROW - { - if ((T)rhs == (T)-1) - { - result = 0; - return true; - } - return false; - } -}; - -template -class ModulusSignedCaseHelper -{ -public: - static bool SignedCase(SafeInt /*rhs*/, SafeInt& /*result*/) SAFEINT_NOTHROW { return false; } -}; - -template -class ModulusSimpleCaseHelper -{ -public: - static bool ModulusSimpleCase(U lhs, SafeInt rhs, SafeInt& result) SAFEINT_CPP_THROW - { - if (rhs != 0) - { - if (ModulusSignedCaseHelper::isSigned>::SignedCase(rhs, result)) return true; - - result = SafeInt((T)(lhs % (T)rhs)); - return true; - } - - E::SafeIntOnDivZero(); - } -}; - -template -class ModulusSimpleCaseHelper -{ -public: - static bool ModulusSimpleCase(U /*lhs*/, SafeInt /*rhs*/, SafeInt& /*result*/) SAFEINT_NOTHROW - { - return false; - } -}; - -// Modulus -template -SafeInt operator%(U lhs, SafeInt rhs) SAFEINT_CPP_THROW -{ - // Value of return depends on sign of lhs - // This one may not be safe - bounds check in constructor - // if lhs is negative and rhs is unsigned, this will throw an exception. - - // Fast-track the simple case - // same size and same sign - SafeInt result; - - if (ModulusSimpleCaseHelper < T, - U, - E, - sizeof(T) == sizeof(U) && - (bool)IntTraits::isSigned == (bool)IntTraits::isSigned > ::ModulusSimpleCase(lhs, rhs, result)) - return result; - - return SafeInt((SafeInt(lhs) % (T)rhs)); -} - -// Multiplication -template -SafeInt operator*(U lhs, SafeInt rhs)SAFEINT_CPP_THROW -{ - T ret(0); - MultiplicationHelper::method>::template MultiplyThrow((T)rhs, lhs, ret); - return SafeInt(ret); -} - -template -class DivisionNegativeCornerCaseHelper; - -template -class DivisionNegativeCornerCaseHelper -{ -public: - static bool NegativeCornerCase(U lhs, SafeInt rhs, SafeInt& result) SAFEINT_CPP_THROW - { - // Problem case - normal casting behavior changes meaning - // flip rhs to positive - // any operator casts now do the right thing - U tmp; - - if (CompileConst::Value()) - tmp = lhs / (U)(~(unsigned __int32)(T)rhs + 1); - else - tmp = lhs / (U)(~(unsigned __int64)(T)rhs + 1); - - if (tmp <= (U)IntTraits::maxInt) - { - result = SafeInt((T)(~(unsigned __int64)tmp + 1)); - return true; - } - - // Corner case - T maxT = IntTraits::maxInt; - if (tmp == (U)maxT + 1) - { - T minT = IntTraits::minInt; - result = SafeInt(minT); - return true; - } - - E::SafeIntOnOverflow(); - } -}; - -template -class DivisionNegativeCornerCaseHelper -{ -public: - static bool NegativeCornerCase(U /*lhs*/, SafeInt /*rhs*/, SafeInt& /*result*/) SAFEINT_NOTHROW - { - return false; - } -}; - -template -class DivisionCornerCaseHelper; - -template -class DivisionCornerCaseHelper -{ -public: - static bool DivisionCornerCase1(U lhs, SafeInt rhs, SafeInt& result) SAFEINT_CPP_THROW - { - if ((T)rhs > 0) - { - result = SafeInt(lhs / (T)rhs); - return true; - } - - // Now rhs is either negative, or zero - if ((T)rhs != 0) - { - if (DivisionNegativeCornerCaseHelper < T, - U, - E, - sizeof(U) >= 4 && sizeof(T) <= sizeof(U) > ::NegativeCornerCase(lhs, rhs, result)) - return true; - - result = SafeInt(lhs / (T)rhs); - return true; - } - - E::SafeIntOnDivZero(); - } -}; - -template -class DivisionCornerCaseHelper -{ -public: - static bool DivisionCornerCase1(U /*lhs*/, SafeInt /*rhs*/, SafeInt& /*result*/) SAFEINT_NOTHROW - { - return false; - } -}; - -template -class DivisionCornerCaseHelper2; - -template -class DivisionCornerCaseHelper2 -{ -public: - static bool DivisionCornerCase2(U lhs, SafeInt rhs, SafeInt& result) SAFEINT_CPP_THROW - { - if (lhs == IntTraits::minInt && (T)rhs == -1) - { - // corner case of a corner case - lhs = min int, rhs = -1, - // but rhs is the return type, so in essence, we can return -lhs - // if rhs is a larger type than lhs - // If types are wrong, throws - -#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER -#pragma warning(push) -// cast truncates constant value -#pragma warning(disable : 4310) -#endif - - if (CompileConst::Value()) - result = SafeInt((T)(-(T)IntTraits::minInt)); - else - E::SafeIntOnOverflow(); - -#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER -#pragma warning(pop) -#endif - - return true; - } - - return false; - } -}; - -template -class DivisionCornerCaseHelper2 -{ -public: - static bool DivisionCornerCase2(U /*lhs*/, SafeInt /*rhs*/, SafeInt& /*result*/) SAFEINT_NOTHROW - { - return false; - } -}; - -// Division -template -SafeInt operator/(U lhs, SafeInt rhs) SAFEINT_CPP_THROW -{ - // Corner case - has to be handled seperately - SafeInt result; - if (DivisionCornerCaseHelper::method == (int)DivisionState_UnsignedSigned>:: - DivisionCornerCase1(lhs, rhs, result)) - return result; - - if (DivisionCornerCaseHelper2::isBothSigned>::DivisionCornerCase2(lhs, rhs, result)) - return result; - - // Otherwise normal logic works with addition of bounds check when casting from U->T - U ret; - DivisionHelper::method>::template DivideThrow(lhs, (T)rhs, ret); - return SafeInt(ret); -} - -// Addition -template -SafeInt operator+(U lhs, SafeInt rhs) SAFEINT_CPP_THROW -{ - T ret(0); - AdditionHelper::method>::template AdditionThrow((T)rhs, lhs, ret); - return SafeInt(ret); -} - -// Subtraction -template -SafeInt operator-(U lhs, SafeInt rhs) SAFEINT_CPP_THROW -{ - T ret(0); - SubtractionHelper::method>::template SubtractThrow(lhs, rhs.Ref(), ret); - - return SafeInt(ret); -} - -// Overrides designed to deal with cases where a SafeInt is assigned out -// to a normal int - this at least makes the last operation safe -// += -template -T& operator+=(T& lhs, SafeInt rhs) SAFEINT_CPP_THROW -{ - T ret(0); - AdditionHelper::method>::template AdditionThrow(lhs, (U)rhs, ret); - lhs = ret; - return lhs; -} - -template -T& operator-=(T& lhs, SafeInt rhs) SAFEINT_CPP_THROW -{ - T ret(0); - SubtractionHelper::method>::template SubtractThrow(lhs, (U)rhs, ret); - lhs = ret; - return lhs; -} - -template -T& operator*=(T& lhs, SafeInt rhs) SAFEINT_CPP_THROW -{ - T ret(0); - MultiplicationHelper::method>::template MultiplyThrow(lhs, (U)rhs, ret); - lhs = ret; - return lhs; -} - -template -T& operator/=(T& lhs, SafeInt rhs) SAFEINT_CPP_THROW -{ - T ret(0); - DivisionHelper::method>::template DivideThrow(lhs, (U)rhs, ret); - lhs = ret; - return lhs; -} - -template -T& operator%=(T& lhs, SafeInt rhs) SAFEINT_CPP_THROW -{ - T ret(0); - ModulusHelper::method>::template ModulusThrow(lhs, (U)rhs, ret); - lhs = ret; - return lhs; -} - -template -T& operator&=(T& lhs, SafeInt rhs) SAFEINT_NOTHROW -{ - lhs = BinaryAndHelper::method>::And(lhs, (U)rhs); - return lhs; -} - -template -T& operator^=(T& lhs, SafeInt rhs) SAFEINT_NOTHROW -{ - lhs = BinaryXorHelper::method>::Xor(lhs, (U)rhs); - return lhs; -} - -template -T& operator|=(T& lhs, SafeInt rhs) SAFEINT_NOTHROW -{ - lhs = BinaryOrHelper::method>::Or(lhs, (U)rhs); - return lhs; -} - -template -T& operator<<=(T& lhs, SafeInt rhs) SAFEINT_NOTHROW -{ - lhs = (T)(SafeInt(lhs) << (U)rhs); - return lhs; -} - -template -T& operator>>=(T& lhs, SafeInt rhs) SAFEINT_NOTHROW -{ - lhs = (T)(SafeInt(lhs) >> (U)rhs); - return lhs; -} - -// Specific pointer overrides -// Note - this function makes no attempt to ensure -// that the resulting pointer is still in the buffer, only -// that no int overflows happened on the way to getting the new pointer -template -T*& operator+=(T*& lhs, SafeInt rhs) SAFEINT_CPP_THROW -{ - // Cast the pointer to a number so we can do arithmetic - SafeInt ptr_val = reinterpret_cast(lhs); - // Check first that rhs is valid for the type of ptrdiff_t - // and that multiplying by sizeof( T ) doesn't overflow a ptrdiff_t - // Next, we need to add 2 SafeInts of different types, so unbox the ptr_diff - // Finally, cast the number back to a pointer of the correct type - lhs = reinterpret_cast((size_t)(ptr_val + (ptrdiff_t)(SafeInt(rhs) * sizeof(T)))); - return lhs; -} - -template -T*& operator-=(T*& lhs, SafeInt rhs) SAFEINT_CPP_THROW -{ - // Cast the pointer to a number so we can do arithmetic - SafeInt ptr_val = reinterpret_cast(lhs); - // See above for comments - lhs = reinterpret_cast((size_t)(ptr_val - (ptrdiff_t)(SafeInt(rhs) * sizeof(T)))); - return lhs; -} - -template -T*& operator*=(T*& lhs, SafeInt) SAFEINT_NOTHROW -{ - // This operator explicitly not supported - C_ASSERT(sizeof(T) == 0); - return (lhs = NULL); -} - -template -T*& operator/=(T*& lhs, SafeInt) SAFEINT_NOTHROW -{ - // This operator explicitly not supported - C_ASSERT(sizeof(T) == 0); - return (lhs = NULL); -} - -template -T*& operator%=(T*& lhs, SafeInt) SAFEINT_NOTHROW -{ - // This operator explicitly not supported - C_ASSERT(sizeof(T) == 0); - return (lhs = NULL); -} - -template -T*& operator&=(T*& lhs, SafeInt) SAFEINT_NOTHROW -{ - // This operator explicitly not supported - C_ASSERT(sizeof(T) == 0); - return (lhs = NULL); -} - -template -T*& operator^=(T*& lhs, SafeInt) SAFEINT_NOTHROW -{ - // This operator explicitly not supported - C_ASSERT(sizeof(T) == 0); - return (lhs = NULL); -} - -template -T*& operator|=(T*& lhs, SafeInt) SAFEINT_NOTHROW -{ - // This operator explicitly not supported - C_ASSERT(sizeof(T) == 0); - return (lhs = NULL); -} - -template -T*& operator<<=(T*& lhs, SafeInt) SAFEINT_NOTHROW -{ - // This operator explicitly not supported - C_ASSERT(sizeof(T) == 0); - return (lhs = NULL); -} - -template -T*& operator>>=(T*& lhs, SafeInt) SAFEINT_NOTHROW -{ - // This operator explicitly not supported - C_ASSERT(sizeof(T) == 0); - return (lhs = NULL); -} - -// Shift operators -// NOTE - shift operators always return the type of the lhs argument - -// Left shift -template -SafeInt operator<<(U lhs, SafeInt bits) SAFEINT_NOTHROW -{ - ShiftAssert(!IntTraits::isSigned || (T)bits >= 0); - ShiftAssert((T)bits < (int)IntTraits::bitCount); - - return SafeInt((U)(lhs << (T)bits)); -} - -// Right shift -template -SafeInt operator>>(U lhs, SafeInt bits) SAFEINT_NOTHROW -{ - ShiftAssert(!IntTraits::isSigned || (T)bits >= 0); - ShiftAssert((T)bits < (int)IntTraits::bitCount); - - return SafeInt((U)(lhs >> (T)bits)); -} - -// Bitwise operators -// This only makes sense if we're dealing with the same type and size -// demand a type T, or something that fits into a type T. - -// Bitwise & -template -SafeInt operator&(U lhs, SafeInt rhs)SAFEINT_NOTHROW -{ - return SafeInt(BinaryAndHelper::method>::And((T)rhs, lhs)); -} - -// Bitwise XOR -template -SafeInt operator^(U lhs, SafeInt rhs) SAFEINT_NOTHROW -{ - return SafeInt(BinaryXorHelper::method>::Xor((T)rhs, lhs)); -} - -// Bitwise OR -template -SafeInt operator|(U lhs, SafeInt rhs) SAFEINT_NOTHROW -{ - return SafeInt(BinaryOrHelper::method>::Or((T)rhs, lhs)); -} - -#if SAFEINT_COMPILER == GCC_COMPILER -#pragma GCC diagnostic pop -#endif - -#if SAFEINT_COMPILER == CLANG_COMPILER -#pragma clang diagnostic pop -#endif - -#ifdef C_ASSERT_DEFINED_SAFEINT -#undef C_ASSERT -#undef C_ASSERT_DEFINED_SAFEINT -#endif // C_ASSERT_DEFINED_SAFEINT - -} // namespace safeint3 -} // namespace msl diff --git a/deps/cpprestsdk/include/cpprest/details/basic_types.h b/deps/cpprestsdk/include/cpprest/details/basic_types.h deleted file mode 100644 index d2ceb87189..0000000000 --- a/deps/cpprestsdk/include/cpprest/details/basic_types.h +++ /dev/null @@ -1,131 +0,0 @@ -/*** - * Copyright (C) Microsoft. All rights reserved. - * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. - * - * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - * - * Platform-dependent type definitions - * - * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk - * - * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - ****/ - -#pragma once - -#include "cpprest/details/cpprest_compat.h" -#include -#include -#include -#include - -#ifndef _WIN32 -#ifndef __STDC_LIMIT_MACROS -#define __STDC_LIMIT_MACROS -#endif -#include -#else -#include -#endif - -#include "cpprest/details/SafeInt3.hpp" - -namespace utility -{ -#ifdef _WIN32 -#define _UTF16_STRINGS -#endif - -// We should be using a 64-bit size type for most situations that do -// not involve specifying the size of a memory allocation or buffer. -typedef uint64_t size64_t; - -#ifndef _WIN32 -typedef uint32_t HRESULT; // Needed for PPLX -#endif - -#ifdef _UTF16_STRINGS -// -// On Windows, all strings are wide -// -typedef wchar_t char_t; -typedef std::wstring string_t; -#define _XPLATSTR(x) L##x -typedef std::wostringstream ostringstream_t; -typedef std::wofstream ofstream_t; -typedef std::wostream ostream_t; -typedef std::wistream istream_t; -typedef std::wifstream ifstream_t; -typedef std::wistringstream istringstream_t; -typedef std::wstringstream stringstream_t; -#define ucout std::wcout -#define ucin std::wcin -#define ucerr std::wcerr -#else -// -// On POSIX platforms, all strings are narrow -// -typedef char char_t; -typedef std::string string_t; -#define _XPLATSTR(x) x -typedef std::ostringstream ostringstream_t; -typedef std::ofstream ofstream_t; -typedef std::ostream ostream_t; -typedef std::istream istream_t; -typedef std::ifstream ifstream_t; -typedef std::istringstream istringstream_t; -typedef std::stringstream stringstream_t; -#define ucout std::cout -#define ucin std::cin -#define ucerr std::cerr -#endif // endif _UTF16_STRINGS - -#ifndef _TURN_OFF_PLATFORM_STRING -// The 'U' macro can be used to create a string or character literal of the platform type, i.e. utility::char_t. -// If you are using a library causing conflicts with 'U' macro, it can be turned off by defining the macro -// '_TURN_OFF_PLATFORM_STRING' before including the C++ REST SDK header files, and e.g. use '_XPLATSTR' instead. -#define U(x) _XPLATSTR(x) -#endif // !_TURN_OFF_PLATFORM_STRING - -} // namespace utility - -typedef char utf8char; -typedef std::string utf8string; -typedef std::stringstream utf8stringstream; -typedef std::ostringstream utf8ostringstream; -typedef std::ostream utf8ostream; -typedef std::istream utf8istream; -typedef std::istringstream utf8istringstream; - -#ifdef _UTF16_STRINGS -typedef wchar_t utf16char; -typedef std::wstring utf16string; -typedef std::wstringstream utf16stringstream; -typedef std::wostringstream utf16ostringstream; -typedef std::wostream utf16ostream; -typedef std::wistream utf16istream; -typedef std::wistringstream utf16istringstream; -#else -typedef char16_t utf16char; -typedef std::u16string utf16string; -typedef std::basic_stringstream utf16stringstream; -typedef std::basic_ostringstream utf16ostringstream; -typedef std::basic_ostream utf16ostream; -typedef std::basic_istream utf16istream; -typedef std::basic_istringstream utf16istringstream; -#endif - -#if defined(_WIN32) -// Include on everything except Windows Desktop ARM, unless explicitly excluded. -#if !defined(CPPREST_EXCLUDE_WEBSOCKETS) -#if defined(WINAPI_FAMILY) -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && defined(_M_ARM) -#define CPPREST_EXCLUDE_WEBSOCKETS -#endif -#else -#if defined(_M_ARM) -#define CPPREST_EXCLUDE_WEBSOCKETS -#endif -#endif -#endif -#endif diff --git a/deps/cpprestsdk/include/cpprest/details/cpprest_compat.h b/deps/cpprestsdk/include/cpprest/details/cpprest_compat.h deleted file mode 100644 index c0c55bebc3..0000000000 --- a/deps/cpprestsdk/include/cpprest/details/cpprest_compat.h +++ /dev/null @@ -1,91 +0,0 @@ -/*** - * Copyright (C) Microsoft. All rights reserved. - * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. - * - * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - * - * Standard macros and definitions. - * This header has minimal dependency on windows headers and is safe for use in the public API - * - * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk - * - * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - ****/ - -#pragma once - -#if defined(_WIN32) - -#if _MSC_VER >= 1900 -#define CPPREST_NOEXCEPT noexcept -#define CPPREST_CONSTEXPR constexpr -#else -#define CPPREST_NOEXCEPT -#define CPPREST_CONSTEXPR const -#endif // _MSC_VER >= 1900 - -#define CASABLANCA_UNREFERENCED_PARAMETER(x) (x) - -#include - -#else // ^^^ _WIN32 ^^^ // vvv !_WIN32 vvv - -#define __declspec(x) __attribute__((x)) -#define dllimport -#define novtable /* no novtable equivalent */ -#define __assume(x) \ - do \ - { \ - if (!(x)) __builtin_unreachable(); \ - } while (false) -#define CASABLANCA_UNREFERENCED_PARAMETER(x) (void)x -#define CPPREST_NOEXCEPT noexcept -#define CPPREST_CONSTEXPR constexpr - -#include -#define _ASSERTE(x) assert(x) - -// No SAL on non Windows platforms -#include "cpprest/details/nosal.h" - -#if !defined(__cdecl) -#if defined(cdecl) -#define __cdecl __attribute__((cdecl)) -#else // ^^^ defined cdecl ^^^ // vvv !defined cdecl vvv -#define __cdecl -#endif // defined cdecl -#endif // not defined __cdecl - -#if defined(__ANDROID__) -// This is needed to disable the use of __thread inside the boost library. -// Android does not support thread local storage -- if boost is included -// without this macro defined, it will create references to __tls_get_addr -// which (while able to link) will not be available at runtime and prevent -// the .so from loading. -#if not defined BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION -#define BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION -#endif // not defined BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION -#endif // defined(__ANDROID__) - -#ifdef __clang__ -#include -#endif // __clang__ -#endif // _WIN32 - -#define _NO_ASYNCRTIMP - -#ifdef _NO_ASYNCRTIMP -#define _ASYNCRTIMP -#else // ^^^ _NO_ASYNCRTIMP ^^^ // vvv !_NO_ASYNCRTIMP vvv -#ifdef _ASYNCRT_EXPORT -#define _ASYNCRTIMP __declspec(dllexport) -#else // ^^^ _ASYNCRT_EXPORT ^^^ // vvv !_ASYNCRT_EXPORT vvv -#define _ASYNCRTIMP __declspec(dllimport) -#endif // _ASYNCRT_EXPORT -#endif // _NO_ASYNCRTIMP - -#ifdef CASABLANCA_DEPRECATION_NO_WARNINGS -#define CASABLANCA_DEPRECATED(x) -#else -#define CASABLANCA_DEPRECATED(x) __declspec(deprecated(x)) -#endif // CASABLANCA_DEPRECATION_NO_WARNINGS diff --git a/deps/cpprestsdk/include/cpprest/details/web_utilities.h b/deps/cpprestsdk/include/cpprest/details/web_utilities.h deleted file mode 100644 index 8b99d94aa2..0000000000 --- a/deps/cpprestsdk/include/cpprest/details/web_utilities.h +++ /dev/null @@ -1,223 +0,0 @@ -/*** - * Copyright (C) Microsoft. All rights reserved. - * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. - * - * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - * - * utility classes used by the different web:: clients - * - * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - ****/ -#pragma once - -#include "cpprest/asyncrt_utils.h" -#include "cpprest/uri.h" - -namespace web -{ -namespace details -{ -class zero_memory_deleter -{ -public: - _ASYNCRTIMP void operator()(::utility::string_t* data) const; -}; -typedef std::unique_ptr<::utility::string_t, zero_memory_deleter> plaintext_string; - -#if defined(_WIN32) && !defined(CPPREST_TARGET_XP) -#if defined(__cplusplus_winrt) -class winrt_encryption -{ -public: - winrt_encryption() {} - _ASYNCRTIMP winrt_encryption(const std::wstring& data); - _ASYNCRTIMP plaintext_string decrypt() const; - -private: - ::pplx::task m_buffer; -}; -#else -class win32_encryption -{ -public: - win32_encryption() {} - _ASYNCRTIMP win32_encryption(const std::wstring& data); - _ASYNCRTIMP ~win32_encryption(); - _ASYNCRTIMP plaintext_string decrypt() const; - -private: - std::vector m_buffer; - size_t m_numCharacters; -}; -#endif -#endif -} // namespace details - -/// -/// Represents a set of user credentials (user name and password) to be used -/// for authentication. -/// -class credentials -{ -public: - /// - /// Constructs an empty set of credentials without a user name or password. - /// - credentials() {} - - /// - /// Constructs credentials from given user name and password. - /// - /// User name as a string. - /// Password as a string. - credentials(utility::string_t username, const utility::string_t& password) - : m_username(std::move(username)), m_password(password) - { - } - - /// - /// The user name associated with the credentials. - /// - /// A string containing the user name. - const utility::string_t& username() const { return m_username; } - - /// - /// The password for the user name associated with the credentials. - /// - /// A string containing the password. - CASABLANCA_DEPRECATED( - "This API is deprecated for security reasons to avoid unnecessary password copies stored in plaintext.") - utility::string_t password() const - { -#if defined(_WIN32) && !defined(CPPREST_TARGET_XP) - return utility::string_t(*m_password.decrypt()); -#else - return m_password; -#endif - } - - /// - /// Checks if credentials have been set - /// - /// true if user name and password is set, false otherwise. - bool is_set() const { return !m_username.empty(); } - - details::plaintext_string _internal_decrypt() const - { - // Encryption APIs not supported on XP -#if defined(_WIN32) && !defined(CPPREST_TARGET_XP) - return m_password.decrypt(); -#else - return details::plaintext_string(new ::utility::string_t(m_password)); -#endif - } - -private: - ::utility::string_t m_username; - -#if defined(_WIN32) && !defined(CPPREST_TARGET_XP) -#if defined(__cplusplus_winrt) - details::winrt_encryption m_password; -#else - details::win32_encryption m_password; -#endif -#else - ::utility::string_t m_password; -#endif -}; - -/// -/// web_proxy represents the concept of the web proxy, which can be auto-discovered, -/// disabled, or specified explicitly by the user. -/// -class web_proxy -{ - enum web_proxy_mode_internal - { - use_default_, - use_auto_discovery_, - disabled_, - user_provided_ - }; - -public: - enum web_proxy_mode - { - use_default = use_default_, - use_auto_discovery = use_auto_discovery_, - disabled = disabled_ - }; - - /// - /// Constructs a proxy with the default settings. - /// - web_proxy() : m_address(_XPLATSTR("")), m_mode(use_default_) {} - - /// - /// Creates a proxy with specified mode. - /// - /// Mode to use. - web_proxy(web_proxy_mode mode) : m_address(_XPLATSTR("")), m_mode(static_cast(mode)) {} - - /// - /// Creates a proxy explicitly with provided address. - /// - /// Proxy URI to use. - web_proxy(uri address) : m_address(address), m_mode(user_provided_) {} - - /// - /// Gets this proxy's URI address. Returns an empty URI if not explicitly set by user. - /// - /// A reference to this proxy's URI. - const uri& address() const { return m_address; } - - /// - /// Gets the credentials used for authentication with this proxy. - /// - /// Credentials to for this proxy. - const web::credentials& credentials() const { return m_credentials; } - - /// - /// Sets the credentials to use for authentication with this proxy. - /// - /// Credentials to use for this proxy. - void set_credentials(web::credentials cred) - { - if (m_mode == disabled_) - { - throw std::invalid_argument("Cannot attach credentials to a disabled proxy"); - } - m_credentials = std::move(cred); - } - - /// - /// Checks if this proxy was constructed with default settings. - /// - /// True if default, false otherwise. - bool is_default() const { return m_mode == use_default_; } - - /// - /// Checks if using a proxy is disabled. - /// - /// True if disabled, false otherwise. - bool is_disabled() const { return m_mode == disabled_; } - - /// - /// Checks if the auto discovery protocol, WPAD, is to be used. - /// - /// True if auto discovery enabled, false otherwise. - bool is_auto_discovery() const { return m_mode == use_auto_discovery_; } - - /// - /// Checks if a proxy address is explicitly specified by the user. - /// - /// True if a proxy address was explicitly specified, false otherwise. - bool is_specified() const { return m_mode == user_provided_; } - -private: - web::uri m_address; - web_proxy_mode_internal m_mode; - web::credentials m_credentials; -}; - -} // namespace web diff --git a/deps/cpprestsdk/include/cpprest/json.h b/deps/cpprestsdk/include/cpprest/json.h deleted file mode 100644 index 4095be50ea..0000000000 --- a/deps/cpprestsdk/include/cpprest/json.h +++ /dev/null @@ -1,1786 +0,0 @@ -/*** - * Copyright (C) Microsoft. All rights reserved. - * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. - * - * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - * - * HTTP Library: JSON parser and writer - * - * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk - * - * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - ****/ -#pragma once - -#ifndef CASA_JSON_H -#define CASA_JSON_H - -#include "cpprest/asyncrt_utils.h" -#include "cpprest/details/basic_types.h" -#include -#include -#include -#include -#include -#include - -namespace web -{ -/// Library for parsing and serializing JSON values to and from C++ types. -namespace json -{ -// Various forward declarations. -namespace details -{ -class _Value; -class _Number; -class _Null; -class _Boolean; -class _String; -class _Object; -class _Array; -template -class JSON_Parser; -} // namespace details - -namespace details -{ -extern bool g_keep_json_object_unsorted; -} - -/// -/// Preserve the order of the name/value pairs when parsing a JSON object. -/// The default is false, which can yield better performance. -/// -/// true if ordering should be preserved when parsing, false otherwise. -/// Note this is a global setting and affects all JSON parsing done. -void _ASYNCRTIMP __cdecl keep_object_element_order(bool keep_order); - -#ifdef _WIN32 -#ifdef _DEBUG -#define ENABLE_JSON_VALUE_VISUALIZER -#endif -#endif - -class number; -class array; -class object; - -/// -/// A JSON value represented as a C++ class. -/// -class value -{ -public: - /// - /// This enumeration represents the various kinds of JSON values. - /// - enum value_type - { - /// Number value - Number, - /// Boolean value - Boolean, - /// String value - String, - /// Object value - Object, - /// Array value - Array, - /// Null value - Null - }; - - /// - /// Constructor creating a null value - /// - _ASYNCRTIMP value(); - - /// - /// Constructor creating a JSON number value - /// - /// The C++ value to create a JSON value from - _ASYNCRTIMP value(int32_t value); - - /// - /// Constructor creating a JSON number value - /// - /// The C++ value to create a JSON value from - _ASYNCRTIMP value(uint32_t value); - - /// - /// Constructor creating a JSON number value - /// - /// The C++ value to create a JSON value from - _ASYNCRTIMP value(int64_t value); - - /// - /// Constructor creating a JSON number value - /// - /// The C++ value to create a JSON value from - _ASYNCRTIMP value(uint64_t value); - - /// - /// Constructor creating a JSON number value - /// - /// The C++ value to create a JSON value from - _ASYNCRTIMP value(double value); - - /// - /// Constructor creating a JSON Boolean value - /// - /// The C++ value to create a JSON value from - _ASYNCRTIMP explicit value(bool value); - - /// - /// Constructor creating a JSON string value - /// - /// The C++ value to create a JSON value from, a C++ STL string of the platform-native character - /// width This constructor has O(n) performance because it tries to determine if specified string - /// has characters that should be properly escaped in JSON. - _ASYNCRTIMP explicit value(utility::string_t value); - - /// - /// Constructor creating a JSON string value specifying if the string contains characters to escape - /// - /// The C++ value to create a JSON value from, a C++ STL string of the platform-native character - /// width Whether contains characters that should - /// be escaped in JSON value This constructor has O(1) performance. - /// - _ASYNCRTIMP explicit value(utility::string_t value, bool has_escape_chars); - - /// - /// Constructor creating a JSON string value - /// - /// The C++ value to create a JSON value from, a C++ STL string of the platform-native character - /// width This constructor has O(n) performance because it tries to determine if specified - /// string has characters that should be properly escaped in JSON. - /// - /// - /// This constructor exists in order to avoid string literals matching another constructor, - /// as is very likely. For example, conversion to bool does not require a user-defined conversion, - /// and will therefore match first, which means that the JSON value turns up as a boolean. - /// - /// - _ASYNCRTIMP explicit value(const utility::char_t* value); - - /// - /// Constructor creating a JSON string value - /// - /// The C++ value to create a JSON value from, a C++ STL string of the platform-native character - /// width Whether contains characters - /// - /// This overload has O(1) performance. - /// - /// - /// This constructor exists in order to avoid string literals matching another constructor, - /// as is very likely. For example, conversion to bool does not require a user-defined conversion, - /// and will therefore match first, which means that the JSON value turns up as a boolean. - /// - /// - _ASYNCRTIMP explicit value(const utility::char_t* value, bool has_escape_chars); - - /// - /// Copy constructor - /// - _ASYNCRTIMP value(const value&); - - /// - /// Move constructor - /// - _ASYNCRTIMP value(value&&) CPPREST_NOEXCEPT; - - /// - /// Assignment operator. - /// - /// The JSON value object that contains the result of the assignment. - _ASYNCRTIMP value& operator=(const value&); - - /// - /// Move assignment operator. - /// - /// The JSON value object that contains the result of the assignment. - _ASYNCRTIMP value& operator=(value&&) CPPREST_NOEXCEPT; - - // Static factories - - /// - /// Creates a null value - /// - /// A JSON null value - static _ASYNCRTIMP value __cdecl null(); - - /// - /// Creates a number value - /// - /// The C++ value to create a JSON value from - /// A JSON number value - static _ASYNCRTIMP value __cdecl number(double value); - - /// - /// Creates a number value - /// - /// The C++ value to create a JSON value from - /// A JSON number value - static _ASYNCRTIMP value __cdecl number(int32_t value); - - /// - /// Creates a number value - /// - /// The C++ value to create a JSON value from - /// A JSON number value - static _ASYNCRTIMP value __cdecl number(uint32_t value); - - /// - /// Creates a number value - /// - /// The C++ value to create a JSON value from - /// A JSON number value - static _ASYNCRTIMP value __cdecl number(int64_t value); - - /// - /// Creates a number value - /// - /// The C++ value to create a JSON value from - /// A JSON number value - static _ASYNCRTIMP value __cdecl number(uint64_t value); - - /// - /// Creates a Boolean value - /// - /// The C++ value to create a JSON value from - /// A JSON Boolean value - static _ASYNCRTIMP value __cdecl boolean(bool value); - - /// - /// Creates a string value - /// - /// The C++ value to create a JSON value from - /// A JSON string value - /// - /// This overload has O(n) performance because it tries to determine if - /// specified string has characters that should be properly escaped in JSON. - /// - static _ASYNCRTIMP value __cdecl string(utility::string_t value); - - /// - /// Creates a string value specifying if the string contains characters to escape - /// - /// The C++ value to create a JSON value from - /// Whether contains characters - /// that should be escaped in JSON value - /// A JSON string value - /// - /// This overload has O(1) performance. - /// - static _ASYNCRTIMP value __cdecl string(utility::string_t value, bool has_escape_chars); - -#ifdef _WIN32 -private: - // Only used internally by JSON parser. - static _ASYNCRTIMP value __cdecl string(const std::string& value); - -public: -#endif - - /// - /// Creates an object value - /// - /// Whether to preserve the original order of the fields - /// An empty JSON object value - static _ASYNCRTIMP json::value __cdecl object(bool keep_order = false); - - /// - /// Creates an object value from a collection of field/values - /// - /// Field names associated with JSON values - /// Whether to preserve the original order of the fields - /// A non-empty JSON object value - static _ASYNCRTIMP json::value __cdecl object(std::vector> fields, - bool keep_order = false); - - /// - /// Creates an empty JSON array - /// - /// An empty JSON array value - static _ASYNCRTIMP json::value __cdecl array(); - - /// - /// Creates a JSON array - /// - /// The initial number of elements of the JSON value - /// A JSON array value - static _ASYNCRTIMP json::value __cdecl array(size_t size); - - /// - /// Creates a JSON array - /// - /// A vector of JSON values - /// A JSON array value - static _ASYNCRTIMP json::value __cdecl array(std::vector elements); - - /// - /// Accesses the type of JSON value the current value instance is - /// - /// The value's type - _ASYNCRTIMP json::value::value_type type() const; - - /// - /// Is the current value a null value? - /// - /// true if the value is a null value, false otherwise - bool is_null() const { return type() == Null; }; - - /// - /// Is the current value a number value? - /// - /// true if the value is a number value, false otherwise - bool is_number() const { return type() == Number; } - - /// - /// Is the current value represented as an integer number value? - /// - /// - /// Note that if a json value is a number but represented as a double it can still - /// be retrieved as a integer using as_integer(), however the value will be truncated. - /// - /// true if the value is an integer value, false otherwise. - _ASYNCRTIMP bool is_integer() const; - - /// - /// Is the current value represented as an double number value? - /// - /// - /// Note that if a json value is a number but represented as a int it can still - /// be retrieved as a double using as_double(). - /// - /// true if the value is an double value, false otherwise. - _ASYNCRTIMP bool is_double() const; - - /// - /// Is the current value a Boolean value? - /// - /// true if the value is a Boolean value, false otherwise - bool is_boolean() const { return type() == Boolean; } - - /// - /// Is the current value a string value? - /// - /// true if the value is a string value, false otherwise - bool is_string() const { return type() == String; } - - /// - /// Is the current value an array? - /// - /// true if the value is an array, false otherwise - bool is_array() const { return type() == Array; } - - /// - /// Is the current value an object? - /// - /// true if the value is an object, false otherwise - bool is_object() const { return type() == Object; } - - /// - /// Gets the number of children of the value. - /// - /// The number of children. 0 for all non-composites. - size_t size() const; - - /// - /// Parses a string and construct a JSON value. - /// - /// The C++ value to create a JSON value from, a C++ STL double-byte string - _ASYNCRTIMP static value __cdecl parse(const utility::string_t& value); - - /// - /// Attempts to parse a string and construct a JSON value. - /// - /// The C++ value to create a JSON value from, a C++ STL double-byte string - /// If parsing fails, the error code is greater than 0 - /// The parsed object. Returns web::json::value::null if failed - _ASYNCRTIMP static value __cdecl parse(const utility::string_t& value, std::error_code& errorCode); - - /// - /// Serializes the current JSON value to a C++ string. - /// - /// A string representation of the value - _ASYNCRTIMP utility::string_t serialize() const; - - /// - /// Serializes the current JSON value to a C++ string. - /// - /// A string representation of the value - CASABLANCA_DEPRECATED("This API is deprecated and has been renamed to avoid confusion with as_string(), use " - "::web::json::value::serialize() instead.") - _ASYNCRTIMP utility::string_t to_string() const; - - /// - /// Parses a JSON value from the contents of an input stream using the native platform character width. - /// - /// The stream to read the JSON value from - /// The JSON value object created from the input stream. - _ASYNCRTIMP static value __cdecl parse(utility::istream_t& input); - - /// - /// Parses a JSON value from the contents of an input stream using the native platform character width. - /// - /// The stream to read the JSON value from - /// If parsing fails, the error code is greater than 0 - /// The parsed object. Returns web::json::value::null if failed - _ASYNCRTIMP static value __cdecl parse(utility::istream_t& input, std::error_code& errorCode); - - /// - /// Writes the current JSON value to a stream with the native platform character width. - /// - /// The stream that the JSON string representation should be written to. - _ASYNCRTIMP void serialize(utility::ostream_t& stream) const; - -#ifdef _WIN32 - /// - /// Parses a JSON value from the contents of a single-byte (UTF8) stream. - /// - /// The stream to read the JSON value from - _ASYNCRTIMP static value __cdecl parse(std::istream& stream); - - /// - /// Parses a JSON value from the contents of a single-byte (UTF8) stream. - /// - /// The stream to read the JSON value from - /// If parsing fails, the error code is greater than 0 - /// The parsed object. Returns web::json::value::null if failed - _ASYNCRTIMP static value __cdecl parse(std::istream& stream, std::error_code& error); - - /// - /// Serializes the content of the value into a single-byte (UTF8) stream. - /// - /// The stream that the JSON string representation should be written to. - _ASYNCRTIMP void serialize(std::ostream& stream) const; -#endif - - /// - /// Converts the JSON value to a C++ double, if and only if it is a number value. - /// Throws if the value is not a number - /// - /// A double representation of the value - _ASYNCRTIMP double as_double() const; - - /// - /// Converts the JSON value to a C++ integer, if and only if it is a number value. - /// Throws if the value is not a number - /// - /// An integer representation of the value - _ASYNCRTIMP int as_integer() const; - - /// - /// Converts the JSON value to a number class, if and only if it is a number value. - /// Throws if the value is not a number - /// - /// An instance of number class - _ASYNCRTIMP const json::number& as_number() const; - - /// - /// Converts the JSON value to a C++ bool, if and only if it is a Boolean value. - /// - /// A C++ bool representation of the value - _ASYNCRTIMP bool as_bool() const; - - /// - /// Converts the JSON value to a json array, if and only if it is an array value. - /// - /// The returned json::array should have the same or shorter lifetime as this - /// An array representation of the value - _ASYNCRTIMP json::array& as_array(); - - /// - /// Converts the JSON value to a json array, if and only if it is an array value. - /// - /// The returned json::array should have the same or shorter lifetime as this - /// An array representation of the value - _ASYNCRTIMP const json::array& as_array() const; - - /// - /// Converts the JSON value to a json object, if and only if it is an object value. - /// - /// An object representation of the value - _ASYNCRTIMP json::object& as_object(); - - /// - /// Converts the JSON value to a json object, if and only if it is an object value. - /// - /// An object representation of the value - _ASYNCRTIMP const json::object& as_object() const; - - /// - /// Converts the JSON value to a C++ STL string, if and only if it is a string value. - /// - /// A C++ STL string representation of the value - _ASYNCRTIMP const utility::string_t& as_string() const; - - /// - /// Compares two JSON values for equality. - /// - /// The JSON value to compare with. - /// True if the values are equal. - _ASYNCRTIMP bool operator==(const value& other) const; - - /// - /// Compares two JSON values for inequality. - /// - /// The JSON value to compare with. - /// True if the values are unequal. - bool operator!=(const value& other) const { return !((*this) == other); } - - /// - /// Tests for the presence of a field. - /// - /// The name of the field - /// True if the field exists, false otherwise. - bool has_field(const utility::string_t& key) const; - - /// - /// Tests for the presence of a number field - /// - /// The name of the field - /// True if the field exists, false otherwise. - _ASYNCRTIMP bool has_number_field(const utility::string_t& key) const; - - /// - /// Tests for the presence of an integer field - /// - /// The name of the field - /// True if the field exists, false otherwise. - _ASYNCRTIMP bool has_integer_field(const utility::string_t& key) const; - - /// - /// Tests for the presence of a double field - /// - /// The name of the field - /// True if the field exists, false otherwise. - _ASYNCRTIMP bool has_double_field(const utility::string_t& key) const; - - /// - /// Tests for the presence of a boolean field - /// - /// The name of the field - /// True if the field exists, false otherwise. - _ASYNCRTIMP bool has_boolean_field(const utility::string_t& key) const; - - /// - /// Tests for the presence of a string field - /// - /// The name of the field - /// True if the field exists, false otherwise. - _ASYNCRTIMP bool has_string_field(const utility::string_t& key) const; - - /// - /// Tests for the presence of an array field - /// - /// The name of the field - /// True if the field exists, false otherwise. - _ASYNCRTIMP bool has_array_field(const utility::string_t& key) const; - - /// - /// Tests for the presence of an object field - /// - /// The name of the field - /// True if the field exists, false otherwise. - _ASYNCRTIMP bool has_object_field(const utility::string_t& key) const; - - /// - /// Accesses a field of a JSON object. - /// - /// The name of the field - /// The value kept in the field; null if the field does not exist - CASABLANCA_DEPRECATED( - "This API is deprecated and will be removed in a future release, use json::value::at() instead.") - value get(const utility::string_t& key) const; - - /// - /// Erases an element of a JSON array. Throws if index is out of bounds. - /// - /// The index of the element to erase in the JSON array. - _ASYNCRTIMP void erase(size_t index); - - /// - /// Erases an element of a JSON object. Throws if the key doesn't exist. - /// - /// The key of the element to erase in the JSON object. - _ASYNCRTIMP void erase(const utility::string_t& key); - - /// - /// Accesses an element of a JSON array. Throws when index out of bounds. - /// - /// The index of an element in the JSON array. - /// A reference to the value. - _ASYNCRTIMP json::value& at(size_t index); - - /// - /// Accesses an element of a JSON array. Throws when index out of bounds. - /// - /// The index of an element in the JSON array. - /// A reference to the value. - _ASYNCRTIMP const json::value& at(size_t index) const; - - /// - /// Accesses an element of a JSON object. If the key doesn't exist, this method throws. - /// - /// The key of an element in the JSON object. - /// If the key exists, a reference to the value. - _ASYNCRTIMP json::value& at(const utility::string_t& key); - - /// - /// Accesses an element of a JSON object. If the key doesn't exist, this method throws. - /// - /// The key of an element in the JSON object. - /// If the key exists, a reference to the value. - _ASYNCRTIMP const json::value& at(const utility::string_t& key) const; - - /// - /// Accesses a field of a JSON object. - /// - /// The name of the field - /// A reference to the value kept in the field. - _ASYNCRTIMP value& operator[](const utility::string_t& key); - -#ifdef _WIN32 -private: - // Only used internally by JSON parser - _ASYNCRTIMP value& operator[](const std::string& key) - { - // JSON object stores its field map as a unordered_map of string_t, so this conversion is hard to avoid - return operator[](utility::conversions::to_string_t(key)); - } - -public: -#endif - - /// - /// Accesses an element of a JSON array. - /// - /// The index of an element in the JSON array - /// The value kept at the array index; null if outside the boundaries of the array - CASABLANCA_DEPRECATED( - "This API is deprecated and will be removed in a future release, use json::value::at() instead.") - value get(size_t index) const; - - /// - /// Accesses an element of a JSON array. - /// - /// The index of an element in the JSON array. - /// A reference to the value kept in the field. - _ASYNCRTIMP value& operator[](size_t index); - -private: - friend class web::json::details::_Object; - friend class web::json::details::_Array; - template - friend class web::json::details::JSON_Parser; - -#ifdef _WIN32 - /// - /// Writes the current JSON value as a double-byte string to a string instance. - /// - /// The string that the JSON representation should be written to. - _ASYNCRTIMP void format(std::basic_string& string) const; -#endif - /// - /// Serializes the content of the value into a string instance in UTF8 format - /// - /// The string that the JSON representation should be written to - _ASYNCRTIMP void format(std::basic_string& string) const; - -#ifdef ENABLE_JSON_VALUE_VISUALIZER - explicit value(std::unique_ptr v, value_type kind) : m_value(std::move(v)), m_kind(kind) -#else - explicit value(std::unique_ptr v) : m_value(std::move(v)) -#endif - { - } - - std::unique_ptr m_value; -#ifdef ENABLE_JSON_VALUE_VISUALIZER - value_type m_kind; -#endif -}; - -/// -/// A single exception type to represent errors in parsing, converting, and accessing -/// elements of JSON values. -/// -class json_exception : public std::exception -{ -private: - std::string _message; - -public: - json_exception(const char* const message) : _message(message) {} -#ifdef _UTF16_STRINGS - json_exception(const wchar_t* const message) : _message(utility::conversions::utf16_to_utf8(message)) {} -#endif // _UTF16_STRINGS - json_exception(std::string&& message) : _message(std::move(message)) {} - - // Must be narrow string because it derives from std::exception - const char* what() const CPPREST_NOEXCEPT { return _message.c_str(); } -}; - -namespace details -{ -enum json_error -{ - left_over_character_in_stream = 1, - malformed_array_literal, - malformed_comment, - malformed_literal, - malformed_object_literal, - malformed_numeric_literal, - malformed_string_literal, - malformed_token, - mismatched_brances, - nesting, - unexpected_token -}; - -class json_error_category_impl : public std::error_category -{ -public: - virtual const char* name() const CPPREST_NOEXCEPT override { return "json"; } - - virtual std::string message(int ev) const override - { - switch (ev) - { - case json_error::left_over_character_in_stream: - return "Left-over characters in stream after parsing a JSON value"; - case json_error::malformed_array_literal: return "Malformed array literal"; - case json_error::malformed_comment: return "Malformed comment"; - case json_error::malformed_literal: return "Malformed literal"; - case json_error::malformed_object_literal: return "Malformed object literal"; - case json_error::malformed_numeric_literal: return "Malformed numeric literal"; - case json_error::malformed_string_literal: return "Malformed string literal"; - case json_error::malformed_token: return "Malformed token"; - case json_error::mismatched_brances: return "Mismatched braces"; - case json_error::nesting: return "Nesting too deep"; - case json_error::unexpected_token: return "Unexpected token"; - default: return "Unknown json error"; - } - } -}; - -const json_error_category_impl& json_error_category(); -} // namespace details - -/// -/// A JSON array represented as a C++ class. -/// -class array -{ - typedef std::vector storage_type; - -public: - typedef storage_type::iterator iterator; - typedef storage_type::const_iterator const_iterator; - typedef storage_type::reverse_iterator reverse_iterator; - typedef storage_type::const_reverse_iterator const_reverse_iterator; - typedef storage_type::size_type size_type; - -private: - array() : m_elements() {} - array(size_type size) : m_elements(size) {} - array(storage_type elements) : m_elements(std::move(elements)) {} - -public: - /// - /// Gets the beginning iterator element of the array - /// - /// An iterator to the beginning of the JSON array. - iterator begin() { return m_elements.begin(); } - - /// - /// Gets the beginning const iterator element of the array. - /// - /// A const_iterator to the beginning of the JSON array. - const_iterator begin() const { return m_elements.cbegin(); } - - /// - /// Gets the end iterator element of the array - /// - /// An iterator to the end of the JSON array. - iterator end() { return m_elements.end(); } - - /// - /// Gets the end const iterator element of the array. - /// - /// A const_iterator to the end of the JSON array. - const_iterator end() const { return m_elements.cend(); } - - /// - /// Gets the beginning reverse iterator element of the array - /// - /// An reverse_iterator to the beginning of the JSON array. - reverse_iterator rbegin() { return m_elements.rbegin(); } - - /// - /// Gets the beginning const reverse iterator element of the array - /// - /// An const_reverse_iterator to the beginning of the JSON array. - const_reverse_iterator rbegin() const { return m_elements.rbegin(); } - - /// - /// Gets the end reverse iterator element of the array - /// - /// An reverse_iterator to the end of the JSON array. - reverse_iterator rend() { return m_elements.rend(); } - - /// - /// Gets the end const reverse iterator element of the array - /// - /// An const_reverse_iterator to the end of the JSON array. - const_reverse_iterator rend() const { return m_elements.crend(); } - - /// - /// Gets the beginning const iterator element of the array. - /// - /// A const_iterator to the beginning of the JSON array. - const_iterator cbegin() const { return m_elements.cbegin(); } - - /// - /// Gets the end const iterator element of the array. - /// - /// A const_iterator to the end of the JSON array. - const_iterator cend() const { return m_elements.cend(); } - - /// - /// Gets the beginning const reverse iterator element of the array. - /// - /// A const_reverse_iterator to the beginning of the JSON array. - const_reverse_iterator crbegin() const { return m_elements.crbegin(); } - - /// - /// Gets the end const reverse iterator element of the array. - /// - /// A const_reverse_iterator to the end of the JSON array. - const_reverse_iterator crend() const { return m_elements.crend(); } - - /// - /// Deletes an element of the JSON array. - /// - /// A const_iterator to the element to delete. - /// Iterator to the new location of the element following the erased element. - /// GCC doesn't support erase with const_iterator on vector yet. In the future this should be - /// changed. - iterator erase(iterator position) { return m_elements.erase(position); } - - /// - /// Deletes the element at an index of the JSON array. - /// - /// The index of the element to delete. - void erase(size_type index) - { - if (index >= m_elements.size()) - { - throw json_exception("index out of bounds"); - } - m_elements.erase(m_elements.begin() + index); - } - - /// - /// Accesses an element of a JSON array. Throws when index out of bounds. - /// - /// The index of an element in the JSON array. - /// A reference to the value kept in the field. - json::value& at(size_type index) - { - if (index >= m_elements.size()) throw json_exception("index out of bounds"); - - return m_elements[index]; - } - - /// - /// Accesses an element of a JSON array. Throws when index out of bounds. - /// - /// The index of an element in the JSON array. - /// A reference to the value kept in the field. - const json::value& at(size_type index) const - { - if (index >= m_elements.size()) throw json_exception("index out of bounds"); - - return m_elements[index]; - } - - /// - /// Accesses an element of a JSON array. - /// - /// The index of an element in the JSON array. - /// A reference to the value kept in the field. - json::value& operator[](size_type index) - { - msl::safeint3::SafeInt nMinSize(index); - nMinSize += 1; - msl::safeint3::SafeInt nlastSize(m_elements.size()); - if (nlastSize < nMinSize) m_elements.resize(nMinSize); - - return m_elements[index]; - } - - /// - /// Gets the number of elements of the array. - /// - /// The number of elements. - size_type size() const { return m_elements.size(); } - -private: - storage_type m_elements; - - friend class details::_Array; - template - friend class json::details::JSON_Parser; -}; - -/// -/// A JSON object represented as a C++ class. -/// -class object -{ - typedef std::vector> storage_type; - -public: - typedef storage_type::iterator iterator; - typedef storage_type::const_iterator const_iterator; - typedef storage_type::reverse_iterator reverse_iterator; - typedef storage_type::const_reverse_iterator const_reverse_iterator; - typedef storage_type::size_type size_type; - -private: - object(bool keep_order = false) : m_elements(), m_keep_order(keep_order) {} - object(storage_type elements, bool keep_order = false) : m_elements(std::move(elements)), m_keep_order(keep_order) - { - if (!keep_order) - { - sort(m_elements.begin(), m_elements.end(), compare_pairs); - } - } - -public: - /// - /// Gets the beginning iterator element of the object - /// - /// An iterator to the beginning of the JSON object. - iterator begin() { return m_elements.begin(); } - - /// - /// Gets the beginning const iterator element of the object. - /// - /// A const_iterator to the beginning of the JSON object. - const_iterator begin() const { return m_elements.cbegin(); } - - /// - /// Gets the end iterator element of the object - /// - /// An iterator to the end of the JSON object. - iterator end() { return m_elements.end(); } - - /// - /// Gets the end const iterator element of the object. - /// - /// A const_iterator to the end of the JSON object. - const_iterator end() const { return m_elements.cend(); } - - /// - /// Gets the beginning reverse iterator element of the object - /// - /// An reverse_iterator to the beginning of the JSON object. - reverse_iterator rbegin() { return m_elements.rbegin(); } - - /// - /// Gets the beginning const reverse iterator element of the object - /// - /// An const_reverse_iterator to the beginning of the JSON object. - const_reverse_iterator rbegin() const { return m_elements.rbegin(); } - - /// - /// Gets the end reverse iterator element of the object - /// - /// An reverse_iterator to the end of the JSON object. - reverse_iterator rend() { return m_elements.rend(); } - - /// - /// Gets the end const reverse iterator element of the object - /// - /// An const_reverse_iterator to the end of the JSON object. - const_reverse_iterator rend() const { return m_elements.crend(); } - - /// - /// Gets the beginning const iterator element of the object. - /// - /// A const_iterator to the beginning of the JSON object. - const_iterator cbegin() const { return m_elements.cbegin(); } - - /// - /// Gets the end const iterator element of the object. - /// - /// A const_iterator to the end of the JSON object. - const_iterator cend() const { return m_elements.cend(); } - - /// - /// Gets the beginning const reverse iterator element of the object. - /// - /// A const_reverse_iterator to the beginning of the JSON object. - const_reverse_iterator crbegin() const { return m_elements.crbegin(); } - - /// - /// Gets the end const reverse iterator element of the object. - /// - /// A const_reverse_iterator to the end of the JSON object. - const_reverse_iterator crend() const { return m_elements.crend(); } - - /// - /// Deletes an element of the JSON object. - /// - /// A const_iterator to the element to delete. - /// Iterator to the new location of the element following the erased element. - /// GCC doesn't support erase with const_iterator on vector yet. In the future this should be - /// changed. - iterator erase(iterator position) { return m_elements.erase(position); } - - /// - /// Deletes an element of the JSON object. If the key doesn't exist, this method throws. - /// - /// The key of an element in the JSON object. - void erase(const utility::string_t& key) - { - auto iter = find_by_key(key); - if (iter == m_elements.end()) - { - throw web::json::json_exception("Key not found"); - } - - m_elements.erase(iter); - } - - /// - /// Accesses an element of a JSON object. If the key doesn't exist, this method throws. - /// - /// The key of an element in the JSON object. - /// If the key exists, a reference to the value kept in the field. - json::value& at(const utility::string_t& key) - { - auto iter = find_by_key(key); - if (iter == m_elements.end()) - { - throw web::json::json_exception("Key not found"); - } - - return iter->second; - } - - /// - /// Accesses an element of a JSON object. If the key doesn't exist, this method throws. - /// - /// The key of an element in the JSON object. - /// If the key exists, a reference to the value kept in the field. - const json::value& at(const utility::string_t& key) const - { - auto iter = find_by_key(key); - if (iter == m_elements.end()) - { - throw web::json::json_exception("Key not found"); - } - - return iter->second; - } - - /// - /// Accesses an element of a JSON object. - /// - /// The key of an element in the JSON object. - /// If the key exists, a reference to the value kept in the field, otherwise a newly created null value - /// that will be stored for the given key. - json::value& operator[](const utility::string_t& key) - { - auto iter = find_insert_location(key); - - if (iter == m_elements.end() || key != iter->first) - { - return m_elements.insert(iter, std::pair(key, value()))->second; - } - - return iter->second; - } - - /// - /// Gets an iterator to an element of a JSON object. - /// - /// The key of an element in the JSON object. - /// A const iterator to the value kept in the field. - const_iterator find(const utility::string_t& key) const { return find_by_key(key); } - - /// - /// Gets the number of elements of the object. - /// - /// The number of elements. - size_type size() const { return m_elements.size(); } - - /// - /// Checks if there are any elements in the JSON object. - /// - /// True if empty. - bool empty() const { return m_elements.empty(); } - -private: - static bool compare_pairs(const std::pair& p1, - const std::pair& p2) - { - return p1.first < p2.first; - } - static bool compare_with_key(const std::pair& p1, const utility::string_t& key) - { - return p1.first < key; - } - - storage_type::iterator find_insert_location(const utility::string_t& key) - { - if (m_keep_order) - { - return std::find_if(m_elements.begin(), - m_elements.end(), - [&key](const std::pair& p) { return p.first == key; }); - } - else - { - return std::lower_bound(m_elements.begin(), m_elements.end(), key, compare_with_key); - } - } - - storage_type::const_iterator find_by_key(const utility::string_t& key) const - { - if (m_keep_order) - { - return std::find_if(m_elements.begin(), - m_elements.end(), - [&key](const std::pair& p) { return p.first == key; }); - } - else - { - auto iter = std::lower_bound(m_elements.begin(), m_elements.end(), key, compare_with_key); - if (iter != m_elements.end() && key != iter->first) - { - return m_elements.end(); - } - return iter; - } - } - - storage_type::iterator find_by_key(const utility::string_t& key) - { - auto iter = find_insert_location(key); - if (iter != m_elements.end() && key != iter->first) - { - return m_elements.end(); - } - return iter; - } - - storage_type m_elements; - bool m_keep_order; - friend class details::_Object; - - template - friend class json::details::JSON_Parser; -}; - -/// -/// A JSON number represented as a C++ class. -/// -class number -{ - // Note that these constructors make sure that only negative integers are stored as signed int64 (while others - // convert to unsigned int64). This helps handling number objects e.g. comparing two numbers. - - number(double value) : m_value(value), m_type(double_type) {} - number(int32_t value) : m_intval(value), m_type(value < 0 ? signed_type : unsigned_type) {} - number(uint32_t value) : m_intval(value), m_type(unsigned_type) {} - number(int64_t value) : m_intval(value), m_type(value < 0 ? signed_type : unsigned_type) {} - number(uint64_t value) : m_uintval(value), m_type(unsigned_type) {} - -public: - /// - /// Does the number fit into int32? - /// - /// true if the number fits into int32, false otherwise - _ASYNCRTIMP bool is_int32() const; - - /// - /// Does the number fit into unsigned int32? - /// - /// true if the number fits into unsigned int32, false otherwise - _ASYNCRTIMP bool is_uint32() const; - - /// - /// Does the number fit into int64? - /// - /// true if the number fits into int64, false otherwise - _ASYNCRTIMP bool is_int64() const; - - /// - /// Does the number fit into unsigned int64? - /// - /// true if the number fits into unsigned int64, false otherwise - bool is_uint64() const - { - switch (m_type) - { - case signed_type: return m_intval >= 0; - case unsigned_type: return true; - case double_type: - default: return false; - } - } - - /// - /// Converts the JSON number to a C++ double. - /// - /// A double representation of the number - double to_double() const - { - switch (m_type) - { - case double_type: return m_value; - case signed_type: return static_cast(m_intval); - case unsigned_type: return static_cast(m_uintval); - default: return false; - } - } - - /// - /// Converts the JSON number to int32. - /// - /// An int32 representation of the number - int32_t to_int32() const - { - if (m_type == double_type) - return static_cast(m_value); - else - return static_cast(m_intval); - } - - /// - /// Converts the JSON number to unsigned int32. - /// - /// An unsigned int32 representation of the number - uint32_t to_uint32() const - { - if (m_type == double_type) - return static_cast(m_value); - else - return static_cast(m_intval); - } - - /// - /// Converts the JSON number to int64. - /// - /// An int64 representation of the number - int64_t to_int64() const - { - if (m_type == double_type) - return static_cast(m_value); - else - return static_cast(m_intval); - } - - /// - /// Converts the JSON number to unsigned int64. - /// - /// An unsigned int64 representation of the number - uint64_t to_uint64() const - { - if (m_type == double_type) - return static_cast(m_value); - else - return static_cast(m_intval); - } - - /// - /// Is the number represented internally as an integral type? - /// - /// true if the number is represented as an integral type, false otherwise - bool is_integral() const { return m_type != double_type; } - - /// - /// Compares two JSON numbers for equality. - /// - /// The JSON number to compare with. - /// True if the numbers are equal. - bool operator==(const number& other) const - { - if (m_type != other.m_type) return false; - - switch (m_type) - { - case json::number::type::signed_type: return m_intval == other.m_intval; - case json::number::type::unsigned_type: return m_uintval == other.m_uintval; - case json::number::type::double_type: return m_value == other.m_value; - } - __assume(0); - // Absence of this return statement provokes a warning from Intel - // compiler, but its presence results in a warning from MSVC, so - // we have to resort to conditional compilation to keep both happy. -#ifdef __INTEL_COMPILER - return false; -#endif - } - -private: - union { - int64_t m_intval; - uint64_t m_uintval; - double m_value; - }; - - enum type - { - signed_type = 0, - unsigned_type, - double_type - } m_type; - - friend class details::_Number; -}; - -namespace details -{ -class _Value -{ -public: - virtual std::unique_ptr<_Value> _copy_value() = 0; - - virtual bool has_field(const utility::string_t&) const { return false; } - virtual value get_field(const utility::string_t&) const { throw json_exception("not an object"); } - virtual value get_element(array::size_type) const { throw json_exception("not an array"); } - - virtual value& index(const utility::string_t&) { throw json_exception("not an object"); } - virtual value& index(array::size_type) { throw json_exception("not an array"); } - - virtual const value& cnst_index(const utility::string_t&) const { throw json_exception("not an object"); } - virtual const value& cnst_index(array::size_type) const { throw json_exception("not an array"); } - - // Common function used for serialization to strings and streams. - virtual void serialize_impl(std::string& str) const { format(str); } -#ifdef _WIN32 - virtual void serialize_impl(std::wstring& str) const { format(str); } -#endif - - virtual utility::string_t to_string() const - { - utility::string_t str; - serialize_impl(str); - return str; - } - - virtual json::value::value_type type() const { return json::value::Null; } - - virtual bool is_integer() const { throw json_exception("not a number"); } - virtual bool is_double() const { throw json_exception("not a number"); } - - virtual const json::number& as_number() { throw json_exception("not a number"); } - virtual double as_double() const { throw json_exception("not a number"); } - virtual int as_integer() const { throw json_exception("not a number"); } - virtual bool as_bool() const { throw json_exception("not a boolean"); } - virtual json::array& as_array() { throw json_exception("not an array"); } - virtual const json::array& as_array() const { throw json_exception("not an array"); } - virtual json::object& as_object() { throw json_exception("not an object"); } - virtual const json::object& as_object() const { throw json_exception("not an object"); } - virtual const utility::string_t& as_string() const { throw json_exception("not a string"); } - - virtual size_t size() const { return 0; } - - virtual ~_Value() {} - -protected: - _Value() {} - - virtual void format(std::basic_string& stream) const { stream.append("null"); } -#ifdef _WIN32 - virtual void format(std::basic_string& stream) const { stream.append(L"null"); } -#endif -private: - friend class web::json::value; -}; - -class _Null : public _Value -{ -public: - virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Null>(); } - virtual json::value::value_type type() const { return json::value::Null; } -}; - -class _Number : public _Value -{ -public: - _Number(double value) : m_number(value) {} - _Number(int32_t value) : m_number(value) {} - _Number(uint32_t value) : m_number(value) {} - _Number(int64_t value) : m_number(value) {} - _Number(uint64_t value) : m_number(value) {} - - virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Number>(*this); } - - virtual json::value::value_type type() const { return json::value::Number; } - - virtual bool is_integer() const { return m_number.is_integral(); } - virtual bool is_double() const { return !m_number.is_integral(); } - - virtual double as_double() const { return m_number.to_double(); } - - virtual int as_integer() const { return m_number.to_int32(); } - - virtual const number& as_number() { return m_number; } - -protected: - virtual void format(std::basic_string& stream) const; -#ifdef _WIN32 - virtual void format(std::basic_string& stream) const; -#endif -private: - template - friend class json::details::JSON_Parser; - - json::number m_number; -}; - -class _Boolean : public _Value -{ -public: - _Boolean(bool value) : m_value(value) {} - - virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Boolean>(*this); } - - virtual json::value::value_type type() const { return json::value::Boolean; } - - virtual bool as_bool() const { return m_value; } - -protected: - virtual void format(std::basic_string& stream) const { stream.append(m_value ? "true" : "false"); } - -#ifdef _WIN32 - virtual void format(std::basic_string& stream) const { stream.append(m_value ? L"true" : L"false"); } -#endif -private: - template - friend class json::details::JSON_Parser; - bool m_value; -}; - -class _String : public _Value -{ -public: - _String(utility::string_t value) : m_string(std::move(value)) { m_has_escape_char = has_escape_chars(*this); } - _String(utility::string_t value, bool escaped_chars) : m_string(std::move(value)), m_has_escape_char(escaped_chars) - { - } - -#ifdef _WIN32 - _String(std::string&& value) : m_string(utility::conversions::to_utf16string(std::move(value))) - { - m_has_escape_char = has_escape_chars(*this); - } - _String(std::string&& value, bool escape_chars) - : m_string(utility::conversions::to_utf16string(std::move(value))), m_has_escape_char(escape_chars) - { - } -#endif - - virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_String>(*this); } - - virtual json::value::value_type type() const { return json::value::String; } - - virtual const utility::string_t& as_string() const; - - virtual void serialize_impl(std::string& str) const { serialize_impl_char_type(str); } -#ifdef _WIN32 - virtual void serialize_impl(std::wstring& str) const { serialize_impl_char_type(str); } -#endif - -protected: - virtual void format(std::basic_string& str) const; -#ifdef _WIN32 - virtual void format(std::basic_string& str) const; -#endif - -private: - friend class _Object; - friend class _Array; - - size_t get_reserve_size() const { return m_string.size() + 2; } - - template - void serialize_impl_char_type(std::basic_string& str) const - { - // To avoid repeated allocations reserve some space all up front. - // size of string + 2 for quotes - str.reserve(get_reserve_size()); - format(str); - } - - std::string as_utf8_string() const; - utf16string as_utf16_string() const; - - utility::string_t m_string; - - // There are significant performance gains that can be made by knowing whether - // or not a character that requires escaping is present. - bool m_has_escape_char; - static bool has_escape_chars(const _String& str); -}; - -template -_ASYNCRTIMP void append_escape_string(std::basic_string& str, const std::basic_string& escaped); - -void format_string(const utility::string_t& key, utility::string_t& str); - -#ifdef _WIN32 -void format_string(const utility::string_t& key, std::string& str); -#endif - -class _Object : public _Value -{ -public: - _Object(bool keep_order) : m_object(keep_order) {} - _Object(object::storage_type fields, bool keep_order) : m_object(std::move(fields), keep_order) {} - - virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Object>(*this); } - - virtual json::object& as_object() { return m_object; } - - virtual const json::object& as_object() const { return m_object; } - - virtual json::value::value_type type() const { return json::value::Object; } - - virtual bool has_field(const utility::string_t&) const; - - virtual json::value& index(const utility::string_t& key); - - bool is_equal(const _Object* other) const - { - if (m_object.size() != other->m_object.size()) return false; - - return std::equal(std::begin(m_object), std::end(m_object), std::begin(other->m_object)); - } - - virtual void serialize_impl(std::string& str) const - { - // To avoid repeated allocations reserve some space all up front. - str.reserve(get_reserve_size()); - format(str); - } -#ifdef _WIN32 - virtual void serialize_impl(std::wstring& str) const - { - // To avoid repeated allocations reserve some space all up front. - str.reserve(get_reserve_size()); - format(str); - } -#endif - size_t size() const { return m_object.size(); } - -protected: - virtual void format(std::basic_string& str) const { format_impl(str); } -#ifdef _WIN32 - virtual void format(std::basic_string& str) const { format_impl(str); } -#endif - -private: - json::object m_object; - - template - friend class json::details::JSON_Parser; - - template - void format_impl(std::basic_string& str) const - { - str.push_back('{'); - if (!m_object.empty()) - { - auto lastElement = m_object.end() - 1; - for (auto iter = m_object.begin(); iter != lastElement; ++iter) - { - format_string(iter->first, str); - str.push_back(':'); - iter->second.format(str); - str.push_back(','); - } - format_string(lastElement->first, str); - str.push_back(':'); - lastElement->second.format(str); - } - str.push_back('}'); - } - - size_t get_reserve_size() const - { - // This is a heuristic we can tune more in the future: - // Basically size of string plus - // sum size of value if an object, array, or string. - size_t reserveSize = 2; // For brackets {} - for (auto iter = m_object.begin(); iter != m_object.end(); ++iter) - { - reserveSize += iter->first.length() + 2; // 2 for quotes - size_t valueSize = iter->second.size() * 20; // Multiply by each object/array element - if (valueSize == 0) - { - if (iter->second.type() == json::value::String) - { - valueSize = static_cast<_String*>(iter->second.m_value.get())->get_reserve_size(); - } - else - { - valueSize = 5; // true, false, or null - } - } - reserveSize += valueSize; - } - return reserveSize; - } -}; - -class _Array : public _Value -{ -public: - _Array() {} - _Array(array::size_type size) : m_array(size) {} - _Array(array::storage_type elements) : m_array(std::move(elements)) {} - - virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Array>(*this); } - - virtual json::value::value_type type() const { return json::value::Array; } - - virtual json::array& as_array() { return m_array; } - virtual const json::array& as_array() const { return m_array; } - - virtual json::value& index(json::array::size_type index) { return m_array[index]; } - - bool is_equal(const _Array* other) const - { - if (m_array.size() != other->m_array.size()) return false; - - auto iterT = m_array.cbegin(); - auto iterO = other->m_array.cbegin(); - auto iterTe = m_array.cend(); - auto iterOe = other->m_array.cend(); - - for (; iterT != iterTe && iterO != iterOe; ++iterT, ++iterO) - { - if (*iterT != *iterO) return false; - } - - return true; - } - - virtual void serialize_impl(std::string& str) const - { - // To avoid repeated allocations reserve some space all up front. - str.reserve(get_reserve_size()); - format(str); - } -#ifdef _WIN32 - virtual void serialize_impl(std::wstring& str) const - { - // To avoid repeated allocations reserve some space all up front. - str.reserve(get_reserve_size()); - format(str); - } -#endif - size_t size() const { return m_array.size(); } - -protected: - virtual void format(std::basic_string& str) const { format_impl(str); } -#ifdef _WIN32 - virtual void format(std::basic_string& str) const { format_impl(str); } -#endif -private: - json::array m_array; - - template - friend class json::details::JSON_Parser; - - template - void format_impl(std::basic_string& str) const - { - str.push_back('['); - if (!m_array.m_elements.empty()) - { - auto lastElement = m_array.m_elements.end() - 1; - for (auto iter = m_array.m_elements.begin(); iter != lastElement; ++iter) - { - iter->format(str); - str.push_back(','); - } - lastElement->format(str); - } - str.push_back(']'); - } - - size_t get_reserve_size() const - { - // This is a heuristic we can tune more in the future: - // Basically sum size of each value if an object, array, or string by a multiplier. - size_t reserveSize = 2; // For brackets [] - for (auto iter = m_array.cbegin(); iter != m_array.cend(); ++iter) - { - size_t valueSize = iter->size() * 20; // Per each nested array/object - - if (valueSize == 0) valueSize = 5; // true, false, or null - - reserveSize += valueSize; - } - return reserveSize; - } -}; -} // namespace details - -/// -/// Gets the number of children of the value. -/// -/// The number of children. 0 for all non-composites. -inline size_t json::value::size() const { return m_value->size(); } - -/// -/// Test for the presence of a field. -/// -/// The name of the field -/// True if the field exists, false otherwise. -inline bool json::value::has_field(const utility::string_t& key) const { return m_value->has_field(key); } - -/// -/// Access a field of a JSON object. -/// -/// The name of the field -/// The value kept in the field; null if the field does not exist -inline json::value json::value::get(const utility::string_t& key) const { return m_value->get_field(key); } - -/// -/// Access an element of a JSON array. -/// -/// The index of an element in the JSON array -/// The value kept at the array index; null if outside the boundaries of the array -inline json::value json::value::get(size_t index) const { return m_value->get_element(index); } - -/// -/// A standard std::ostream operator to facilitate writing JSON values to streams. -/// -/// The output stream to write the JSON value to. -/// The JSON value to be written to the stream. -/// The output stream object -_ASYNCRTIMP utility::ostream_t& __cdecl operator<<(utility::ostream_t& os, const json::value& val); - -/// -/// A standard std::istream operator to facilitate reading JSON values from streams. -/// -/// The input stream to read the JSON value from. -/// The JSON value object read from the stream. -/// The input stream object. -_ASYNCRTIMP utility::istream_t& __cdecl operator>>(utility::istream_t& is, json::value& val); -} // namespace json -} // namespace web - -#endif diff --git a/deps/cpprestsdk/include/cpprest/uri.h b/deps/cpprestsdk/include/cpprest/uri.h deleted file mode 100644 index 00f96e1488..0000000000 --- a/deps/cpprestsdk/include/cpprest/uri.h +++ /dev/null @@ -1,21 +0,0 @@ -/*** - * Copyright (C) Microsoft. All rights reserved. - * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. - * - * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - * - * Protocol independent support for URIs. - * - * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk - * - * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - ****/ -#pragma once - -#ifndef CASA_URI_H -#define CASA_URI_H - -#include "cpprest/base_uri.h" -#include "cpprest/uri_builder.h" - -#endif diff --git a/deps/cpprestsdk/include/cpprest/uri_builder.h b/deps/cpprestsdk/include/cpprest/uri_builder.h deleted file mode 100644 index 8cc2a92231..0000000000 --- a/deps/cpprestsdk/include/cpprest/uri_builder.h +++ /dev/null @@ -1,295 +0,0 @@ -/*** - * Copyright (C) Microsoft. All rights reserved. - * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. - * - * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - * - * Builder style class for creating URIs. - * - * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk - * - * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - ****/ - -#pragma once - -#include "cpprest/base_uri.h" -#include - -namespace web -{ -/// -/// Builder for constructing URIs incrementally. -/// -class uri_builder -{ -public: - /// - /// Creates a builder with an initially empty URI. - /// - uri_builder() = default; - - /// - /// Creates a builder with a existing URI object. - /// - /// Encoded string containing the URI. - uri_builder(const uri& uri_str) : m_uri(uri_str.m_components) {} - - /// - /// Get the scheme component of the URI as an encoded string. - /// - /// The URI scheme as a string. - const utility::string_t& scheme() const { return m_uri.m_scheme; } - - /// - /// Get the user information component of the URI as an encoded string. - /// - /// The URI user information as a string. - const utility::string_t& user_info() const { return m_uri.m_user_info; } - - /// - /// Get the host component of the URI as an encoded string. - /// - /// The URI host as a string. - const utility::string_t& host() const { return m_uri.m_host; } - - /// - /// Get the port component of the URI. Returns -1 if no port is specified. - /// - /// The URI port as an integer. - int port() const { return m_uri.m_port; } - - /// - /// Get the path component of the URI as an encoded string. - /// - /// The URI path as a string. - const utility::string_t& path() const { return m_uri.m_path; } - - /// - /// Get the query component of the URI as an encoded string. - /// - /// The URI query as a string. - const utility::string_t& query() const { return m_uri.m_query; } - - /// - /// Get the fragment component of the URI as an encoded string. - /// - /// The URI fragment as a string. - const utility::string_t& fragment() const { return m_uri.m_fragment; } - - /// - /// Set the scheme of the URI. - /// - /// Uri scheme. - /// A reference to this uri_builder to support chaining. - uri_builder& set_scheme(const utility::string_t& scheme) - { - m_uri.m_scheme = scheme; - return *this; - } - - /// - /// Set the user info component of the URI. - /// - /// User info as a decoded string. - /// Specify whether to apply URI encoding to the given string. - /// A reference to this uri_builder to support chaining. - uri_builder& set_user_info(const utility::string_t& user_info, bool do_encoding = false) - { - if (do_encoding) - { - m_uri.m_user_info = uri::encode_uri(user_info, uri::components::user_info); - } - else - { - m_uri.m_user_info = user_info; - } - - return *this; - } - - /// - /// Set the host component of the URI. - /// - /// Host as a decoded string. - /// Specify whether to apply URI encoding to the given string. - /// A reference to this uri_builder to support chaining. - uri_builder& set_host(const utility::string_t& host, bool do_encoding = false) - { - if (do_encoding) - { - m_uri.m_host = uri::encode_uri(host, uri::components::host); - } - else - { - m_uri.m_host = host; - } - - return *this; - } - - /// - /// Set the port component of the URI. - /// - /// Port as an integer. - /// A reference to this uri_builder to support chaining. - uri_builder& set_port(int port) - { - m_uri.m_port = port; - return *this; - } - - /// - /// Set the port component of the URI. - /// - /// Port as a string. - /// A reference to this uri_builder to support chaining. - /// When string can't be converted to an integer the port is left unchanged. - _ASYNCRTIMP uri_builder& set_port(const utility::string_t& port); - - /// - /// Set the path component of the URI. - /// - /// Path as a decoded string. - /// Specify whether to apply URI encoding to the given string. - /// A reference to this uri_builder to support chaining. - uri_builder& set_path(const utility::string_t& path, bool do_encoding = false) - { - if (do_encoding) - { - m_uri.m_path = uri::encode_uri(path, uri::components::path); - } - else - { - m_uri.m_path = path; - } - - return *this; - } - - /// - /// Set the query component of the URI. - /// - /// Query as a decoded string. - /// Specify whether apply URI encoding to the given string. - /// A reference to this uri_builder to support chaining. - uri_builder& set_query(const utility::string_t& query, bool do_encoding = false) - { - if (do_encoding) - { - m_uri.m_query = uri::encode_uri(query, uri::components::query); - } - else - { - m_uri.m_query = query; - } - - return *this; - } - - /// - /// Set the fragment component of the URI. - /// - /// Fragment as a decoded string. - /// Specify whether to apply URI encoding to the given string. - /// A reference to this uri_builder to support chaining. - uri_builder& set_fragment(const utility::string_t& fragment, bool do_encoding = false) - { - if (do_encoding) - { - m_uri.m_fragment = uri::encode_uri(fragment, uri::components::fragment); - } - else - { - m_uri.m_fragment = fragment; - } - - return *this; - } - - /// - /// Clears all components of the underlying URI in this uri_builder. - /// - void clear() { m_uri = details::uri_components(); } - - /// - /// Appends another path to the path of this uri_builder. - /// - /// Path to append as a already encoded string. - /// Specify whether to apply URI encoding to the given string. - /// A reference to this uri_builder to support chaining. - _ASYNCRTIMP uri_builder& append_path(const utility::string_t& path, bool do_encoding = false); - - /// - /// Appends the raw contents of the path argument to the path of this uri_builder with no separator de-duplication. - /// - /// - /// The path argument is appended after adding a '/' separator without regards to the contents of path. If an empty - /// string is provided, this function will immediately return without changes to the stored path value. For example: - /// if the current contents are "/abc" and path="/xyz", the result will be "/abc//xyz". - /// - /// Path to append as a already encoded string. - /// Specify whether to apply URI encoding to the given string. - /// A reference to this uri_builder to support chaining. - _ASYNCRTIMP uri_builder& append_path_raw(const utility::string_t& path, bool do_encoding = false); - - /// - /// Appends another query to the query of this uri_builder. - /// - /// Query to append as a decoded string. - /// Specify whether to apply URI encoding to the given string. - /// A reference to this uri_builder to support chaining. - _ASYNCRTIMP uri_builder& append_query(const utility::string_t& query, bool do_encoding = false); - - /// - /// Appends an relative uri (Path, Query and fragment) at the end of the current uri. - /// - /// The relative uri to append. - /// A reference to this uri_builder to support chaining. - _ASYNCRTIMP uri_builder& append(const uri& relative_uri); - - /// - /// Appends another query to the query of this uri_builder, encoding it first. This overload is useful when building - /// a query segment of the form "element=10", where the right hand side of the query is stored as a type other than - /// a string, for instance, an integral type. - /// - /// The name portion of the query string - /// The value portion of the query string - /// A reference to this uri_builder to support chaining. - template - uri_builder& append_query(const utility::string_t& name, const T& value, bool do_encoding = true) - { - if (do_encoding) - append_query_encode_impl(name, utility::conversions::details::print_utf8string(value)); - else - append_query_no_encode_impl(name, utility::conversions::details::print_string(value)); - return *this; - } - - /// - /// Combine and validate the URI components into a encoded string. An exception will be thrown if the URI is - /// invalid. - /// - /// The created URI as a string. - _ASYNCRTIMP utility::string_t to_string() const; - - /// - /// Combine and validate the URI components into a URI class instance. An exception will be thrown if the URI is - /// invalid. - /// - /// The create URI as a URI class instance. - _ASYNCRTIMP uri to_uri() const; - - /// - /// Validate the generated URI from all existing components of this uri_builder. - /// - /// Whether the URI is valid. - _ASYNCRTIMP bool is_valid(); - -private: - _ASYNCRTIMP void append_query_encode_impl(const utility::string_t& name, const utf8string& value); - _ASYNCRTIMP void append_query_no_encode_impl(const utility::string_t& name, const utility::string_t& value); - - details::uri_components m_uri; -}; -} // namespace web diff --git a/deps/cpprestsdk/include/cpprest/version.h b/deps/cpprestsdk/include/cpprest/version.h deleted file mode 100644 index af1a6c7c1d..0000000000 --- a/deps/cpprestsdk/include/cpprest/version.h +++ /dev/null @@ -1,10 +0,0 @@ -/*** - * Copyright (C) Microsoft. All rights reserved. - * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. - * - */ -#define CPPREST_VERSION_MINOR 10 -#define CPPREST_VERSION_MAJOR 2 -#define CPPREST_VERSION_REVISION 13 - -#define CPPREST_VERSION (CPPREST_VERSION_MAJOR * 100000 + CPPREST_VERSION_MINOR * 100 + CPPREST_VERSION_REVISION) diff --git a/deps/cpprestsdk/license.txt b/deps/cpprestsdk/license.txt deleted file mode 100644 index 9a6d567042..0000000000 --- a/deps/cpprestsdk/license.txt +++ /dev/null @@ -1,25 +0,0 @@ -C++ REST SDK - -The MIT License (MIT) - -Copyright (c) Microsoft Corporation - -All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/deps/cpprestsdk/pch.cpp b/deps/cpprestsdk/pch.cpp deleted file mode 100644 index 1d9f38c57d..0000000000 --- a/deps/cpprestsdk/pch.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "pch.h" diff --git a/deps/cpprestsdk/pch.h b/deps/cpprestsdk/pch.h deleted file mode 100644 index 4b228b348b..0000000000 --- a/deps/cpprestsdk/pch.h +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// cpprestsdk headers -#include "cpprest/details/basic_types.h" -#include "cpprest/details/cpprest_compat.h" -#include "cpprest/version.h" -// json -#include "cpprest/json.h" -// utilities -#include "cpprest/asyncrt_utils.h" -#include "cpprest/details/web_utilities.h" diff --git a/deps/cpprestsdk/src/json/json.cpp b/deps/cpprestsdk/src/json/json.cpp deleted file mode 100644 index 2d9fcd6506..0000000000 --- a/deps/cpprestsdk/src/json/json.cpp +++ /dev/null @@ -1,475 +0,0 @@ -/*** - * Copyright (C) Microsoft. All rights reserved. - * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. - * - * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - * - * HTTP Library: JSON parser and writer - * - * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk - * - * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - ****/ - -#include "pch.h" - -using namespace web; - -bool json::details::g_keep_json_object_unsorted = false; -void json::keep_object_element_order(bool keep_order) { json::details::g_keep_json_object_unsorted = keep_order; } - -utility::ostream_t& web::json::operator<<(utility::ostream_t& os, const web::json::value& val) -{ - val.serialize(os); - return os; -} - -utility::istream_t& web::json::operator>>(utility::istream_t& is, json::value& val) -{ - val = json::value::parse(is); - return is; -} - -web::json::value::value() - : m_value(utility::details::make_unique()) -#ifdef ENABLE_JSON_VALUE_VISUALIZER - , m_kind(value::Null) -#endif -{ -} - -web::json::value::value(int32_t value) - : m_value(utility::details::make_unique(value)) -#ifdef ENABLE_JSON_VALUE_VISUALIZER - , m_kind(value::Number) -#endif -{ -} - -web::json::value::value(uint32_t value) - : m_value(utility::details::make_unique(value)) -#ifdef ENABLE_JSON_VALUE_VISUALIZER - , m_kind(value::Number) -#endif -{ -} - -web::json::value::value(int64_t value) - : m_value(utility::details::make_unique(value)) -#ifdef ENABLE_JSON_VALUE_VISUALIZER - , m_kind(value::Number) -#endif -{ -} - -web::json::value::value(uint64_t value) - : m_value(utility::details::make_unique(value)) -#ifdef ENABLE_JSON_VALUE_VISUALIZER - , m_kind(value::Number) -#endif -{ -} - -web::json::value::value(double value) - : m_value(utility::details::make_unique(value)) -#ifdef ENABLE_JSON_VALUE_VISUALIZER - , m_kind(value::Number) -#endif -{ -} - -web::json::value::value(bool value) - : m_value(utility::details::make_unique(value)) -#ifdef ENABLE_JSON_VALUE_VISUALIZER - , m_kind(value::Boolean) -#endif -{ -} - -web::json::value::value(utility::string_t value) - : m_value(utility::details::make_unique(std::move(value))) -#ifdef ENABLE_JSON_VALUE_VISUALIZER - , m_kind(value::String) -#endif -{ -} - -web::json::value::value(utility::string_t value, bool has_escape_chars) - : m_value(utility::details::make_unique(std::move(value), has_escape_chars)) -#ifdef ENABLE_JSON_VALUE_VISUALIZER - , m_kind(value::String) -#endif -{ -} - -web::json::value::value(const utility::char_t* value) - : m_value(utility::details::make_unique(value)) -#ifdef ENABLE_JSON_VALUE_VISUALIZER - , m_kind(value::String) -#endif -{ -} - -web::json::value::value(const utility::char_t* value, bool has_escape_chars) - : m_value(utility::details::make_unique(utility::string_t(value), has_escape_chars)) -#ifdef ENABLE_JSON_VALUE_VISUALIZER - , m_kind(value::String) -#endif -{ -} - -web::json::value::value(const value& other) - : m_value(other.m_value->_copy_value()) -#ifdef ENABLE_JSON_VALUE_VISUALIZER - , m_kind(other.m_kind) -#endif -{ -} - -web::json::value& web::json::value::operator=(const value& other) -{ - if (this != &other) - { - m_value = std::unique_ptr(other.m_value->_copy_value()); -#ifdef ENABLE_JSON_VALUE_VISUALIZER - m_kind = other.m_kind; -#endif - } - return *this; -} - -web::json::value::value(value&& other) CPPREST_NOEXCEPT : m_value(std::move(other.m_value)) -#ifdef ENABLE_JSON_VALUE_VISUALIZER - , - m_kind(other.m_kind) -#endif -{ -} - -web::json::value& web::json::value::operator=(web::json::value&& other) CPPREST_NOEXCEPT -{ - if (this != &other) - { - m_value.swap(other.m_value); -#ifdef ENABLE_JSON_VALUE_VISUALIZER - m_kind = other.m_kind; -#endif - } - return *this; -} - -web::json::value web::json::value::null() { return web::json::value(); } - -web::json::value web::json::value::number(double value) { return web::json::value(value); } - -web::json::value web::json::value::number(int32_t value) { return web::json::value(value); } - -web::json::value web::json::value::number(uint32_t value) { return web::json::value(value); } - -web::json::value web::json::value::number(int64_t value) { return web::json::value(value); } - -web::json::value web::json::value::number(uint64_t value) { return web::json::value(value); } - -web::json::value web::json::value::boolean(bool value) { return web::json::value(value); } - -web::json::value web::json::value::string(utility::string_t value) -{ - std::unique_ptr ptr = utility::details::make_unique(std::move(value)); - return web::json::value(std::move(ptr) -#ifdef ENABLE_JSON_VALUE_VISUALIZER - , - value::String -#endif - ); -} - -web::json::value web::json::value::string(utility::string_t value, bool has_escape_chars) -{ - std::unique_ptr ptr = - utility::details::make_unique(std::move(value), has_escape_chars); - return web::json::value(std::move(ptr) -#ifdef ENABLE_JSON_VALUE_VISUALIZER - , - value::String -#endif - ); -} - -#ifdef _WIN32 -web::json::value web::json::value::string(const std::string& value) -{ - std::unique_ptr ptr = - utility::details::make_unique(utility::conversions::to_utf16string(value)); - return web::json::value(std::move(ptr) -#ifdef ENABLE_JSON_VALUE_VISUALIZER - , - value::String -#endif - ); -} -#endif - -web::json::value web::json::value::object(bool keep_order) -{ - std::unique_ptr ptr = utility::details::make_unique(keep_order); - return web::json::value(std::move(ptr) -#ifdef ENABLE_JSON_VALUE_VISUALIZER - , - value::Object -#endif - ); -} - -web::json::value web::json::value::object(std::vector> fields, bool keep_order) -{ - std::unique_ptr ptr = - utility::details::make_unique(std::move(fields), keep_order); - return web::json::value(std::move(ptr) -#ifdef ENABLE_JSON_VALUE_VISUALIZER - , - value::Object -#endif - ); -} - -web::json::value web::json::value::array() -{ - std::unique_ptr ptr = utility::details::make_unique(); - return web::json::value(std::move(ptr) -#ifdef ENABLE_JSON_VALUE_VISUALIZER - , - value::Array -#endif - ); -} - -web::json::value web::json::value::array(size_t size) -{ - std::unique_ptr ptr = utility::details::make_unique(size); - return web::json::value(std::move(ptr) -#ifdef ENABLE_JSON_VALUE_VISUALIZER - , - value::Array -#endif - ); -} - -web::json::value web::json::value::array(std::vector elements) -{ - std::unique_ptr ptr = utility::details::make_unique(std::move(elements)); - return web::json::value(std::move(ptr) -#ifdef ENABLE_JSON_VALUE_VISUALIZER - , - value::Array -#endif - ); -} - -const web::json::number& web::json::value::as_number() const { return m_value->as_number(); } - -double web::json::value::as_double() const { return m_value->as_double(); } - -int web::json::value::as_integer() const { return m_value->as_integer(); } - -bool web::json::value::as_bool() const { return m_value->as_bool(); } - -json::array& web::json::value::as_array() { return m_value->as_array(); } - -const json::array& web::json::value::as_array() const { return m_value->as_array(); } - -json::object& web::json::value::as_object() { return m_value->as_object(); } - -const json::object& web::json::value::as_object() const { return m_value->as_object(); } - -bool web::json::number::is_int32() const -{ - switch (m_type) - { - case signed_type: - return m_intval >= (std::numeric_limits::min)() && m_intval <= (std::numeric_limits::max)(); - case unsigned_type: return m_uintval <= (std::numeric_limits::max)(); - case double_type: - default: return false; - } -} - -bool web::json::number::is_uint32() const -{ - switch (m_type) - { - case signed_type: return m_intval >= 0 && m_intval <= (std::numeric_limits::max)(); - case unsigned_type: return m_uintval <= (std::numeric_limits::max)(); - case double_type: - default: return false; - } -} - -bool web::json::number::is_int64() const -{ - switch (m_type) - { - case signed_type: return true; - case unsigned_type: return m_uintval <= static_cast((std::numeric_limits::max)()); - case double_type: - default: return false; - } -} - -bool web::json::details::_String::has_escape_chars(const _String& str) -{ - return std::any_of(std::begin(str.m_string), std::end(str.m_string), [](utility::string_t::value_type const x) { - if (x <= 31) - { - return true; - } - if (x == '"') - { - return true; - } - if (x == '\\') - { - return true; - } - return false; - }); -} - -web::json::value::value_type json::value::type() const { return m_value->type(); } - -bool json::value::is_integer() const -{ - if (!is_number()) - { - return false; - } - return m_value->is_integer(); -} - -bool json::value::is_double() const -{ - if (!is_number()) - { - return false; - } - return m_value->is_double(); -} - -json::value& web::json::details::_Object::index(const utility::string_t& key) { return m_object[key]; } - -bool web::json::details::_Object::has_field(const utility::string_t& key) const -{ - return m_object.find(key) != m_object.end(); -} - -bool web::json::value::has_number_field(const utility::string_t& key) const -{ - return has_field(key) && at(key).is_number(); -} - -bool web::json::value::has_integer_field(const utility::string_t& key) const -{ - return has_field(key) && at(key).is_integer(); -} - -bool web::json::value::has_double_field(const utility::string_t& key) const -{ - return has_field(key) && at(key).is_double(); -} - -bool web::json::value::has_boolean_field(const utility::string_t& key) const -{ - return has_field(key) && at(key).is_boolean(); -} - -bool web::json::value::has_string_field(const utility::string_t& key) const -{ - return has_field(key) && at(key).is_string(); -} - -bool web::json::value::has_array_field(const utility::string_t& key) const -{ - return has_field(key) && at(key).is_array(); -} - -bool web::json::value::has_object_field(const utility::string_t& key) const -{ - return has_field(key) && at(key).is_object(); -} - -utility::string_t json::value::to_string() const -{ -#ifndef _WIN32 - utility::details::scoped_c_thread_locale locale; -#endif - return m_value->to_string(); -} - -bool json::value::operator==(const json::value& other) const -{ - if (this->m_value.get() == other.m_value.get()) return true; - if (this->type() != other.type()) return false; - - switch (this->type()) - { - case Null: return true; - case Number: return this->as_number() == other.as_number(); - case Boolean: return this->as_bool() == other.as_bool(); - case String: return this->as_string() == other.as_string(); - case Object: - return static_cast(this->m_value.get()) - ->is_equal(static_cast(other.m_value.get())); - case Array: - return static_cast(this->m_value.get()) - ->is_equal(static_cast(other.m_value.get())); - } - __assume(0); -} - -void web::json::value::erase(size_t index) { return this->as_array().erase(index); } - -void web::json::value::erase(const utility::string_t& key) { return this->as_object().erase(key); } - -// at() overloads -web::json::value& web::json::value::at(size_t index) { return this->as_array().at(index); } - -const web::json::value& web::json::value::at(size_t index) const { return this->as_array().at(index); } - -web::json::value& web::json::value::at(const utility::string_t& key) { return this->as_object().at(key); } - -const web::json::value& web::json::value::at(const utility::string_t& key) const { return this->as_object().at(key); } - -web::json::value& web::json::value::operator[](const utility::string_t& key) -{ - if (this->is_null()) - { - m_value.reset(new web::json::details::_Object(details::g_keep_json_object_unsorted)); -#ifdef ENABLE_JSON_VALUE_VISUALIZER - m_kind = value::Object; -#endif - } - return m_value->index(key); -} - -web::json::value& web::json::value::operator[](size_t index) -{ - if (this->is_null()) - { - m_value.reset(new web::json::details::_Array()); -#ifdef ENABLE_JSON_VALUE_VISUALIZER - m_kind = value::Array; -#endif - } - return m_value->index(index); -} - -// Remove once VS 2013 is no longer supported. -#if defined(_WIN32) && _MSC_VER < 1900 -static web::json::details::json_error_category_impl instance; -#endif -const web::json::details::json_error_category_impl& web::json::details::json_error_category() -{ -#if !defined(_WIN32) || _MSC_VER >= 1900 - static web::json::details::json_error_category_impl instance; -#endif - return instance; -} diff --git a/deps/cpprestsdk/src/json/json_parsing.cpp b/deps/cpprestsdk/src/json/json_parsing.cpp deleted file mode 100644 index 1e7e8d332b..0000000000 --- a/deps/cpprestsdk/src/json/json_parsing.cpp +++ /dev/null @@ -1,1279 +0,0 @@ -/*** - * Copyright (C) Microsoft. All rights reserved. - * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. - * - * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - * - * HTTP Library: JSON parser - * - * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk - * - * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - ****/ - -#include "pch.h" - -#include - -#if defined(_MSC_VER) -#pragma warning(disable : 4127) // allow expressions like while(true) pass -#endif -using namespace web; -using namespace web::json; -using namespace utility; -using namespace utility::conversions; - -std::array _hexval = { - {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, - 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}; - -namespace web -{ -namespace json -{ -namespace details -{ -// -// JSON Parsing -// - -template -#if defined(_WIN32) -__declspec(noreturn) -#else - __attribute__((noreturn)) -#endif - void CreateException(const Token& tk, const utility::string_t& message) -{ - std::string str("* Line "); - str += std::to_string(tk.start.m_line); - str += ", Column "; - str += std::to_string(tk.start.m_column); - str += " Syntax error: "; - str += utility::conversions::to_utf8string(message); - throw web::json::json_exception(std::move(str)); -} - -template -void SetErrorCode(Token& tk, json_error jsonErrorCode) -{ - tk.m_error = std::error_code(jsonErrorCode, json_error_category()); -} - -template -class JSON_Parser -{ -public: - JSON_Parser() : m_currentLine(1), m_currentColumn(1), m_currentParsingDepth(0) {} - - struct Location - { - size_t m_line; - size_t m_column; - }; - - struct Token - { - enum Kind - { - TKN_EOF, - - TKN_OpenBrace, - TKN_CloseBrace, - TKN_OpenBracket, - TKN_CloseBracket, - TKN_Comma, - TKN_Colon, - TKN_StringLiteral, - TKN_NumberLiteral, - TKN_IntegerLiteral, - TKN_BooleanLiteral, - TKN_NullLiteral, - TKN_Comment - }; - - Token() : kind(TKN_EOF) {} - - Kind kind; - std::basic_string string_val; - - typename JSON_Parser::Location start; - - union { - double double_val; - int64_t int64_val; - uint64_t uint64_val; - bool boolean_val; - bool has_unescape_symbol; - }; - - bool signed_number; - - std::error_code m_error; - }; - - void GetNextToken(Token&); - - web::json::value ParseValue(typename JSON_Parser::Token& first) - { -#ifndef _WIN32 - utility::details::scoped_c_thread_locale locale; -#endif - -#ifdef ENABLE_JSON_VALUE_VISUALIZER - auto _value = _ParseValue(first); - auto type = _value->type(); - return web::json::value(std::move(_value), type); -#else - return web::json::value(_ParseValue(first)); -#endif - } - -protected: - typedef typename std::char_traits::int_type int_type; - virtual int_type NextCharacter() = 0; - virtual int_type PeekCharacter() = 0; - - virtual bool CompleteComment(Token& token); - virtual bool CompleteStringLiteral(Token& token); - int convert_unicode_to_code_point(); - bool handle_unescape_char(Token& token); - -private: - bool CompleteNumberLiteral(CharType first, Token& token); - bool ParseInt64(CharType first, uint64_t& value); - bool CompleteKeywordTrue(Token& token); - bool CompleteKeywordFalse(Token& token); - bool CompleteKeywordNull(Token& token); - std::unique_ptr _ParseValue(typename JSON_Parser::Token& first); - std::unique_ptr _ParseObject(typename JSON_Parser::Token& tkn); - std::unique_ptr _ParseArray(typename JSON_Parser::Token& tkn); - - JSON_Parser& operator=(const JSON_Parser&); - - int_type EatWhitespace(); - - void CreateToken(typename JSON_Parser::Token& tk, typename Token::Kind kind, Location& start) - { - tk.kind = kind; - tk.start = start; - tk.string_val.clear(); - } - - void CreateToken(typename JSON_Parser::Token& tk, typename Token::Kind kind) - { - tk.kind = kind; - tk.start.m_line = m_currentLine; - tk.start.m_column = m_currentColumn; - tk.string_val.clear(); - } - -protected: - size_t m_currentLine; - size_t m_currentColumn; - size_t m_currentParsingDepth; - -// The DEBUG macro is defined in XCode but we don't in our CMakeList -// so for now we will keep the same on debug and release. In the future -// this can be increase on release if necessary. -#if defined(__APPLE__) - static const size_t maxParsingDepth = 32; -#else - static const size_t maxParsingDepth = 128; -#endif -}; - -// Replace with template alias once VS 2012 support is removed. -template -typename std::char_traits::int_type eof() -{ - return std::char_traits::eof(); -} - -template -class JSON_StreamParser : public JSON_Parser -{ -public: - JSON_StreamParser(std::basic_istream& stream) : m_streambuf(stream.rdbuf()) {} - -protected: - virtual typename JSON_Parser::int_type NextCharacter(); - virtual typename JSON_Parser::int_type PeekCharacter(); - -private: - typename std::basic_streambuf>* m_streambuf; -}; - -template -class JSON_StringParser : public JSON_Parser -{ -public: - JSON_StringParser(const std::basic_string& string) : m_position(&string[0]) - { - m_startpos = m_position; - m_endpos = m_position + string.size(); - } - -protected: - virtual typename JSON_Parser::int_type NextCharacter(); - virtual typename JSON_Parser::int_type PeekCharacter(); - - virtual bool CompleteComment(typename JSON_Parser::Token& token); - virtual bool CompleteStringLiteral(typename JSON_Parser::Token& token); - -private: - bool finish_parsing_string_with_unescape_char(typename JSON_Parser::Token& token); - const CharType* m_position; - const CharType* m_startpos; - const CharType* m_endpos; -}; - -template -typename JSON_Parser::int_type JSON_StreamParser::NextCharacter() -{ - auto ch = m_streambuf->sbumpc(); - - if (ch == '\n') - { - this->m_currentLine += 1; - this->m_currentColumn = 0; - } - else - { - this->m_currentColumn += 1; - } - - return ch; -} - -template -typename JSON_Parser::int_type JSON_StreamParser::PeekCharacter() -{ - return m_streambuf->sgetc(); -} - -template -typename JSON_Parser::int_type JSON_StringParser::NextCharacter() -{ - if (m_position == m_endpos) return eof(); - - CharType ch = *m_position; - m_position += 1; - - if (ch == '\n') - { - this->m_currentLine += 1; - this->m_currentColumn = 0; - } - else - { - this->m_currentColumn += 1; - } - - return ch; -} - -template -typename JSON_Parser::int_type JSON_StringParser::PeekCharacter() -{ - if (m_position == m_endpos) return eof(); - - return *m_position; -} - -// -// Consume whitespace characters and return the first non-space character or EOF -// -template -typename JSON_Parser::int_type JSON_Parser::EatWhitespace() -{ - auto ch = NextCharacter(); - - while (ch != eof() && iswspace(static_cast(ch))) - { - ch = NextCharacter(); - } - - return ch; -} - -template -bool JSON_Parser::CompleteKeywordTrue(Token& token) -{ - if (NextCharacter() != 'r') return false; - if (NextCharacter() != 'u') return false; - if (NextCharacter() != 'e') return false; - token.kind = Token::TKN_BooleanLiteral; - token.boolean_val = true; - return true; -} - -template -bool JSON_Parser::CompleteKeywordFalse(Token& token) -{ - if (NextCharacter() != 'a') return false; - if (NextCharacter() != 'l') return false; - if (NextCharacter() != 's') return false; - if (NextCharacter() != 'e') return false; - token.kind = Token::TKN_BooleanLiteral; - token.boolean_val = false; - return true; -} - -template -bool JSON_Parser::CompleteKeywordNull(Token& token) -{ - if (NextCharacter() != 'u') return false; - if (NextCharacter() != 'l') return false; - if (NextCharacter() != 'l') return false; - token.kind = Token::TKN_NullLiteral; - return true; -} - -// Returns false only on overflow -template -inline bool JSON_Parser::ParseInt64(CharType first, uint64_t& value) -{ - value = first - '0'; - auto ch = PeekCharacter(); - while (ch >= '0' && ch <= '9') - { - unsigned int next_digit = (unsigned int)(ch - '0'); - if (value > (ULLONG_MAX / 10) || (value == ULLONG_MAX / 10 && next_digit > ULLONG_MAX % 10)) return false; - - NextCharacter(); - - value *= 10; - value += next_digit; - ch = PeekCharacter(); - } - return true; -} - -// This namespace hides the x-plat helper functions -namespace -{ -#if defined(_WIN32) -static int print_llu(char* ptr, size_t n, uint64_t val64) -{ - return _snprintf_s_l(ptr, n, _TRUNCATE, "%I64u", utility::details::scoped_c_thread_locale::c_locale(), val64); -} - -static int print_llu(wchar_t* ptr, size_t n, uint64_t val64) -{ - return _snwprintf_s_l(ptr, n, _TRUNCATE, L"%I64u", utility::details::scoped_c_thread_locale::c_locale(), val64); -} -static double anystod(const char* str) -{ - return _strtod_l(str, nullptr, utility::details::scoped_c_thread_locale::c_locale()); -} -static double anystod(const wchar_t* str) -{ - return _wcstod_l(str, nullptr, utility::details::scoped_c_thread_locale::c_locale()); -} -#else -static int __attribute__((__unused__)) print_llu(char* ptr, size_t n, unsigned long long val64) -{ - return snprintf(ptr, n, "%llu", val64); -} -static int __attribute__((__unused__)) print_llu(char* ptr, size_t n, unsigned long val64) -{ - return snprintf(ptr, n, "%lu", val64); -} -static double __attribute__((__unused__)) anystod(const char* str) { return strtod(str, nullptr); } -static double __attribute__((__unused__)) anystod(const wchar_t* str) { return wcstod(str, nullptr); } -#endif -} // namespace - -template -bool JSON_Parser::CompleteNumberLiteral(CharType first, Token& token) -{ - bool minus_sign; - - if (first == '-') - { - minus_sign = true; - - // Safe to cast because the check after this if/else statement will cover EOF. - first = static_cast(NextCharacter()); - } - else - { - minus_sign = false; - } - - if (first < '0' || first > '9') return false; - - auto ch = PeekCharacter(); - - // Check for two (or more) zeros at the beginning - if (first == '0' && ch == '0') return false; - - // Parse the number assuming its integer - uint64_t val64; - bool complete = ParseInt64(first, val64); - - ch = PeekCharacter(); - if (complete && ch != '.' && ch != 'E' && ch != 'e') - { - if (minus_sign) - { - if (val64 > static_cast(1) << 63) - { - // It is negative and cannot be represented in int64, so we resort to double - token.double_val = 0 - static_cast(val64); - token.signed_number = true; - token.kind = JSON_Parser::Token::TKN_NumberLiteral; - return true; - } - - // It is negative, but fits into int64 - token.int64_val = 0 - static_cast(val64); - token.kind = JSON_Parser::Token::TKN_IntegerLiteral; - token.signed_number = true; - return true; - } - - // It is positive so we use unsigned int64 - token.uint64_val = val64; - token.kind = JSON_Parser::Token::TKN_IntegerLiteral; - token.signed_number = false; - return true; - } - - // Magic number 5 leaves room for decimal point, null terminator, etc (in most cases) - ::std::vector buf(::std::numeric_limits::digits10 + 5); - int count = print_llu(buf.data(), buf.size(), val64); - _ASSERTE(count >= 0); - _ASSERTE((size_t)count < buf.size()); - // Resize to cut off the null terminator - buf.resize(count); - - bool decimal = false; - - while (ch != eof()) - { - // Digit encountered? - if (ch >= '0' && ch <= '9') - { - buf.push_back(static_cast(ch)); - NextCharacter(); - ch = PeekCharacter(); - } - - // Decimal dot? - else if (ch == '.') - { - if (decimal) return false; - - decimal = true; - buf.push_back(static_cast(ch)); - - NextCharacter(); - ch = PeekCharacter(); - - // Check that the following char is a digit - if (ch < '0' || ch > '9') return false; - - buf.push_back(static_cast(ch)); - NextCharacter(); - ch = PeekCharacter(); - } - - // Exponent? - else if (ch == 'E' || ch == 'e') - { - buf.push_back(static_cast(ch)); - NextCharacter(); - ch = PeekCharacter(); - - // Check for the exponent sign - if (ch == '+') - { - buf.push_back(static_cast(ch)); - NextCharacter(); - ch = PeekCharacter(); - } - else if (ch == '-') - { - buf.push_back(static_cast(ch)); - NextCharacter(); - ch = PeekCharacter(); - } - - // First number of the exponent - if (ch >= '0' && ch <= '9') - { - buf.push_back(static_cast(ch)); - NextCharacter(); - ch = PeekCharacter(); - } - else - return false; - - // The rest of the exponent - while (ch >= '0' && ch <= '9') - { - buf.push_back(static_cast(ch)); - NextCharacter(); - ch = PeekCharacter(); - } - - // The peeked character is not a number, so we can break from the loop and construct the number - break; - } - else - { - // Not expected number character? - break; - } - }; - - buf.push_back('\0'); - token.double_val = anystod(buf.data()); - if (minus_sign) - { - token.double_val = -token.double_val; - } - token.kind = (JSON_Parser::Token::TKN_NumberLiteral); - - return true; -} - -template -bool JSON_Parser::CompleteComment(Token& token) -{ - // We already found a '/' character as the first of a token -- what kind of comment is it? - - auto ch = NextCharacter(); - - if (ch == eof() || (ch != '/' && ch != '*')) return false; - - if (ch == '/') - { - // Line comment -- look for a newline or EOF to terminate. - - ch = NextCharacter(); - - while (ch != eof() && ch != '\n') - { - ch = NextCharacter(); - } - } - else - { - // Block comment -- look for a terminating "*/" sequence. - - ch = NextCharacter(); - - while (true) - { - if (ch == eof()) return false; - - if (ch == '*') - { - auto ch1 = PeekCharacter(); - - if (ch1 == eof()) return false; - - if (ch1 == '/') - { - // Consume the character - NextCharacter(); - break; - } - - ch = ch1; - } - - ch = NextCharacter(); - } - } - - token.kind = Token::TKN_Comment; - - return true; -} - -template -bool JSON_StringParser::CompleteComment(typename JSON_Parser::Token& token) -{ - // This function is specialized for the string parser, since we can be slightly more - // efficient in copying data from the input to the token: do a memcpy() rather than - // one character at a time. - - auto ch = JSON_StringParser::NextCharacter(); - - if (ch == eof() || (ch != '/' && ch != '*')) return false; - - if (ch == '/') - { - // Line comment -- look for a newline or EOF to terminate. - - ch = JSON_StringParser::NextCharacter(); - - while (ch != eof() && ch != '\n') - { - ch = JSON_StringParser::NextCharacter(); - } - } - else - { - // Block comment -- look for a terminating "*/" sequence. - - ch = JSON_StringParser::NextCharacter(); - - while (true) - { - if (ch == eof()) return false; - - if (ch == '*') - { - ch = JSON_StringParser::PeekCharacter(); - - if (ch == eof()) return false; - - if (ch == '/') - { - // Consume the character - JSON_StringParser::NextCharacter(); - break; - } - } - - ch = JSON_StringParser::NextCharacter(); - } - } - - token.kind = JSON_Parser::Token::TKN_Comment; - - return true; -} - -void convert_append_unicode_code_unit(JSON_Parser::Token& token, utf16string value) -{ - token.string_val.append(value); -} -void convert_append_unicode_code_unit(JSON_Parser::Token& token, utf16string value) -{ - token.string_val.append(::utility::conversions::utf16_to_utf8(value)); -} -void convert_append_unicode_code_unit(JSON_Parser::Token& token, utf16char value) -{ - token.string_val.push_back(value); -} -void convert_append_unicode_code_unit(JSON_Parser::Token& token, utf16char value) -{ - utf16string utf16(reinterpret_cast(&value), 1); - token.string_val.append(::utility::conversions::utf16_to_utf8(utf16)); -} - -template -int JSON_Parser::convert_unicode_to_code_point() -{ - // A four-hexdigit Unicode character. - // Transform into a 16 bit code point. - int decoded = 0; - for (int i = 0; i < 4; ++i) - { - auto ch = NextCharacter(); - int ch_int = static_cast(ch); - if (ch_int < 0 || ch_int > 127) return -1; -#ifdef _WIN32 - const int isxdigitResult = _isxdigit_l(ch_int, utility::details::scoped_c_thread_locale::c_locale()); -#else - const int isxdigitResult = isxdigit(ch_int); -#endif - if (!isxdigitResult) return -1; - - int val = _hexval[static_cast(ch_int)]; - - _ASSERTE(val != -1); - - // Add the input char to the decoded number - decoded |= (val << (4 * (3 - i))); - } - return decoded; -} - -#define H_SURROGATE_START 0xD800 -#define H_SURROGATE_END 0xDBFF - -template -inline bool JSON_Parser::handle_unescape_char(Token& token) -{ - token.has_unescape_symbol = true; - - // This function converts unescaped character pairs (e.g. "\t") into their ASCII or Unicode representations (e.g. - // tab sign) Also it handles \u + 4 hexadecimal digits - auto ch = NextCharacter(); - switch (ch) - { - case '\"': token.string_val.push_back('\"'); return true; - case '\\': token.string_val.push_back('\\'); return true; - case '/': token.string_val.push_back('/'); return true; - case 'b': token.string_val.push_back('\b'); return true; - case 'f': token.string_val.push_back('\f'); return true; - case 'r': token.string_val.push_back('\r'); return true; - case 'n': token.string_val.push_back('\n'); return true; - case 't': token.string_val.push_back('\t'); return true; - case 'u': - { - int decoded = convert_unicode_to_code_point(); - if (decoded == -1) - { - return false; - } - - // handle multi-block characters that start with a high-surrogate - if (decoded >= H_SURROGATE_START && decoded <= H_SURROGATE_END) - { - // skip escape character '\u' - if (NextCharacter() != '\\' || NextCharacter() != 'u') - { - return false; - } - int decoded2 = convert_unicode_to_code_point(); - - if (decoded2 == -1) - { - return false; - } - - utf16string compoundUTF16 = {static_cast(decoded), static_cast(decoded2)}; - convert_append_unicode_code_unit(token, compoundUTF16); - - return true; - } - - // Construct the character based on the decoded number - convert_append_unicode_code_unit(token, static_cast(decoded)); - - return true; - } - default: return false; - } -} - -template -bool JSON_Parser::CompleteStringLiteral(Token& token) -{ - token.has_unescape_symbol = false; - auto ch = NextCharacter(); - while (ch != '"') - { - if (ch == '\\') - { - handle_unescape_char(token); - } - else if (ch >= CharType(0x0) && ch < CharType(0x20)) - { - return false; - } - else - { - if (ch == eof()) return false; - - token.string_val.push_back(static_cast(ch)); - } - ch = NextCharacter(); - } - - if (ch == '"') - { - token.kind = Token::TKN_StringLiteral; - } - else - { - return false; - } - - return true; -} - -template -bool JSON_StringParser::CompleteStringLiteral(typename JSON_Parser::Token& token) -{ - // This function is specialized for the string parser, since we can be slightly more - // efficient in copying data from the input to the token: do a memcpy() rather than - // one character at a time. - - auto start = m_position; - token.has_unescape_symbol = false; - - auto ch = JSON_StringParser::NextCharacter(); - - while (ch != '"') - { - if (ch == eof()) return false; - - if (ch == '\\') - { - const size_t numChars = m_position - start - 1; - const size_t prevSize = token.string_val.size(); - token.string_val.resize(prevSize + numChars); - memcpy(const_cast(token.string_val.c_str() + prevSize), start, numChars * sizeof(CharType)); - - if (!JSON_StringParser::handle_unescape_char(token)) - { - return false; - } - - // Reset start position and continue. - start = m_position; - } - else if (ch >= CharType(0x0) && ch < CharType(0x20)) - { - return false; - } - - ch = JSON_StringParser::NextCharacter(); - } - - const size_t numChars = m_position - start - 1; - const size_t prevSize = token.string_val.size(); - token.string_val.resize(prevSize + numChars); - memcpy(const_cast(token.string_val.c_str() + prevSize), start, numChars * sizeof(CharType)); - - token.kind = JSON_Parser::Token::TKN_StringLiteral; - - return true; -} - -template -void JSON_Parser::GetNextToken(typename JSON_Parser::Token& result) -{ -try_again: - auto ch = EatWhitespace(); - - CreateToken(result, Token::TKN_EOF); - - if (ch == eof()) return; - - switch (ch) - { - case '{': - case '[': - { - if (++m_currentParsingDepth > JSON_Parser::maxParsingDepth) - { - SetErrorCode(result, json_error::nesting); - break; - } - - typename JSON_Parser::Token::Kind tk = ch == '{' ? Token::TKN_OpenBrace : Token::TKN_OpenBracket; - CreateToken(result, tk, result.start); - break; - } - case '}': - case ']': - { - if ((signed int)(--m_currentParsingDepth) < 0) - { - SetErrorCode(result, json_error::mismatched_brances); - break; - } - - typename JSON_Parser::Token::Kind tk = - ch == '}' ? Token::TKN_CloseBrace : Token::TKN_CloseBracket; - CreateToken(result, tk, result.start); - break; - } - case ',': CreateToken(result, Token::TKN_Comma, result.start); break; - - case ':': CreateToken(result, Token::TKN_Colon, result.start); break; - - case 't': - if (!CompleteKeywordTrue(result)) - { - SetErrorCode(result, json_error::malformed_literal); - } - break; - case 'f': - if (!CompleteKeywordFalse(result)) - { - SetErrorCode(result, json_error::malformed_literal); - } - break; - case 'n': - if (!CompleteKeywordNull(result)) - { - SetErrorCode(result, json_error::malformed_literal); - } - break; - case '/': - if (!CompleteComment(result)) - { - SetErrorCode(result, json_error::malformed_comment); - break; - } - // For now, we're ignoring comments. - goto try_again; - case '"': - if (!CompleteStringLiteral(result)) - { - SetErrorCode(result, json_error::malformed_string_literal); - } - break; - - case '-': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (!CompleteNumberLiteral(static_cast(ch), result)) - { - SetErrorCode(result, json_error::malformed_numeric_literal); - } - break; - default: SetErrorCode(result, json_error::malformed_token); break; - } -} - -template -std::unique_ptr JSON_Parser::_ParseObject( - typename JSON_Parser::Token& tkn) -{ - auto obj = utility::details::make_unique(g_keep_json_object_unsorted); - auto& elems = obj->m_object.m_elements; - - GetNextToken(tkn); - if (tkn.m_error) goto error; - - if (tkn.kind != JSON_Parser::Token::TKN_CloseBrace) - { - while (true) - { - // State 1: New field or end of object, looking for field name or closing brace - std::basic_string fieldName; - switch (tkn.kind) - { - case JSON_Parser::Token::TKN_StringLiteral: fieldName = std::move(tkn.string_val); break; - default: goto error; - } - - GetNextToken(tkn); - if (tkn.m_error) goto error; - - // State 2: Looking for a colon. - if (tkn.kind != JSON_Parser::Token::TKN_Colon) goto done; - - GetNextToken(tkn); - if (tkn.m_error) goto error; - - // State 3: Looking for an expression. -#ifdef ENABLE_JSON_VALUE_VISUALIZER - auto fieldValue = _ParseValue(tkn); - auto type = fieldValue->type(); - elems.emplace_back(utility::conversions::to_string_t(std::move(fieldName)), - json::value(std::move(fieldValue), type)); -#else - elems.emplace_back(utility::conversions::to_string_t(std::move(fieldName)), json::value(_ParseValue(tkn))); -#endif - if (tkn.m_error) goto error; - - // State 4: Looking for a comma or a closing brace - switch (tkn.kind) - { - case JSON_Parser::Token::TKN_Comma: - GetNextToken(tkn); - if (tkn.m_error) goto error; - break; - case JSON_Parser::Token::TKN_CloseBrace: goto done; - default: goto error; - } - } - } - -done: - GetNextToken(tkn); - if (tkn.m_error) return utility::details::make_unique(); - - if (!g_keep_json_object_unsorted) - { - ::std::sort(elems.begin(), elems.end(), json::object::compare_pairs); - } - - return std::unique_ptr(obj.release()); - -error: - if (!tkn.m_error) - { - SetErrorCode(tkn, json_error::malformed_object_literal); - } - return utility::details::make_unique(); -} - -template -std::unique_ptr JSON_Parser::_ParseArray( - typename JSON_Parser::Token& tkn) -{ - GetNextToken(tkn); - if (tkn.m_error) return utility::details::make_unique(); - - auto result = utility::details::make_unique(); - - if (tkn.kind != JSON_Parser::Token::TKN_CloseBracket) - { - while (true) - { - // State 1: Looking for an expression. - result->m_array.m_elements.emplace_back(ParseValue(tkn)); - if (tkn.m_error) return utility::details::make_unique(); - - // State 4: Looking for a comma or a closing bracket - switch (tkn.kind) - { - case JSON_Parser::Token::TKN_Comma: - GetNextToken(tkn); - if (tkn.m_error) return utility::details::make_unique(); - break; - case JSON_Parser::Token::TKN_CloseBracket: - GetNextToken(tkn); - if (tkn.m_error) return utility::details::make_unique(); - return std::move(result); - default: - SetErrorCode(tkn, json_error::malformed_array_literal); - return utility::details::make_unique(); - } - } - } - - GetNextToken(tkn); - if (tkn.m_error) return utility::details::make_unique(); - - return std::unique_ptr(result.release()); -} - -template -std::unique_ptr JSON_Parser::_ParseValue( - typename JSON_Parser::Token& tkn) -{ - switch (tkn.kind) - { - case JSON_Parser::Token::TKN_OpenBrace: - { - return _ParseObject(tkn); - } - case JSON_Parser::Token::TKN_OpenBracket: - { - return _ParseArray(tkn); - } - case JSON_Parser::Token::TKN_StringLiteral: - { - auto value = utility::details::make_unique(std::move(tkn.string_val), - tkn.has_unescape_symbol); - GetNextToken(tkn); - if (tkn.m_error) return utility::details::make_unique(); - return std::move(value); - } - case JSON_Parser::Token::TKN_IntegerLiteral: - { - std::unique_ptr value; - if (tkn.signed_number) - value = utility::details::make_unique(tkn.int64_val); - else - value = utility::details::make_unique(tkn.uint64_val); - - GetNextToken(tkn); - if (tkn.m_error) return utility::details::make_unique(); - return std::move(value); - } - case JSON_Parser::Token::TKN_NumberLiteral: - { - auto value = utility::details::make_unique(tkn.double_val); - GetNextToken(tkn); - if (tkn.m_error) return utility::details::make_unique(); - return std::move(value); - } - case JSON_Parser::Token::TKN_BooleanLiteral: - { - auto value = utility::details::make_unique(tkn.boolean_val); - GetNextToken(tkn); - if (tkn.m_error) return utility::details::make_unique(); - return std::move(value); - } - case JSON_Parser::Token::TKN_NullLiteral: - { - GetNextToken(tkn); - // Returning a null value whether or not an error occurred. - return utility::details::make_unique(); - } - default: - { - SetErrorCode(tkn, json_error::malformed_token); - return utility::details::make_unique(); - } - } -} - -} // namespace details -} // namespace json -} // namespace web - -static web::json::value _parse_stream(utility::istream_t& stream) -{ - web::json::details::JSON_StreamParser parser(stream); - web::json::details::JSON_Parser::Token tkn; - - parser.GetNextToken(tkn); - if (tkn.m_error) - { - web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); - } - - auto value = parser.ParseValue(tkn); - if (tkn.m_error) - { - web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); - } - else if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) - { - web::json::details::CreateException(tkn, - _XPLATSTR("Left-over characters in stream after parsing a JSON value")); - } - return value; -} - -static web::json::value _parse_stream(utility::istream_t& stream, std::error_code& error) -{ - web::json::details::JSON_StreamParser parser(stream); - web::json::details::JSON_Parser::Token tkn; - - parser.GetNextToken(tkn); - if (tkn.m_error) - { - error = std::move(tkn.m_error); - return web::json::value(); - } - - auto returnObject = parser.ParseValue(tkn); - if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) - { - web::json::details::SetErrorCode(tkn, web::json::details::json_error::left_over_character_in_stream); - } - - error = std::move(tkn.m_error); - return returnObject; -} - -#ifdef _WIN32 -static web::json::value _parse_narrow_stream(std::istream& stream) -{ - web::json::details::JSON_StreamParser parser(stream); - web::json::details::JSON_StreamParser::Token tkn; - - parser.GetNextToken(tkn); - if (tkn.m_error) - { - web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); - } - - auto value = parser.ParseValue(tkn); - if (tkn.m_error) - { - web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); - } - else if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) - { - web::json::details::CreateException(tkn, - _XPLATSTR("Left-over characters in stream after parsing a JSON value")); - } - return value; -} - -static web::json::value _parse_narrow_stream(std::istream& stream, std::error_code& error) -{ - web::json::details::JSON_StreamParser parser(stream); - web::json::details::JSON_StreamParser::Token tkn; - - parser.GetNextToken(tkn); - if (tkn.m_error) - { - error = std::move(tkn.m_error); - return web::json::value(); - } - - auto returnObject = parser.ParseValue(tkn); - if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) - { - returnObject = web::json::value(); - web::json::details::SetErrorCode(tkn, web::json::details::json_error::left_over_character_in_stream); - } - - error = std::move(tkn.m_error); - return returnObject; -} -#endif - -web::json::value web::json::value::parse(const utility::string_t& str) -{ - web::json::details::JSON_StringParser parser(str); - web::json::details::JSON_Parser::Token tkn; - - parser.GetNextToken(tkn); - if (tkn.m_error) - { - web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); - } - - auto value = parser.ParseValue(tkn); - if (tkn.m_error) - { - web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); - } - else if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) - { - web::json::details::CreateException(tkn, - _XPLATSTR("Left-over characters in stream after parsing a JSON value")); - } - return value; -} - -web::json::value web::json::value::parse(const utility::string_t& str, std::error_code& error) -{ - web::json::details::JSON_StringParser parser(str); - web::json::details::JSON_Parser::Token tkn; - - parser.GetNextToken(tkn); - if (tkn.m_error) - { - error = std::move(tkn.m_error); - return web::json::value(); - } - - auto returnObject = parser.ParseValue(tkn); - if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) - { - returnObject = web::json::value(); - web::json::details::SetErrorCode(tkn, web::json::details::json_error::left_over_character_in_stream); - } - - error = std::move(tkn.m_error); - return returnObject; -} - -web::json::value web::json::value::parse(utility::istream_t& stream) { return _parse_stream(stream); } - -web::json::value web::json::value::parse(utility::istream_t& stream, std::error_code& error) -{ - return _parse_stream(stream, error); -} - -#ifdef _WIN32 -web::json::value web::json::value::parse(std::istream& stream) { return _parse_narrow_stream(stream); } - -web::json::value web::json::value::parse(std::istream& stream, std::error_code& error) -{ - return _parse_narrow_stream(stream, error); -} -#endif diff --git a/deps/cpprestsdk/src/json/json_serialization.cpp b/deps/cpprestsdk/src/json/json_serialization.cpp deleted file mode 100644 index 6ff2e37d74..0000000000 --- a/deps/cpprestsdk/src/json/json_serialization.cpp +++ /dev/null @@ -1,254 +0,0 @@ -/*** - * Copyright (C) Microsoft. All rights reserved. - * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. - * - * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - * - * HTTP Library: JSON parser and writer - * - * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk - * - * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - ****/ - -#include "pch.h" - -#include - -#ifndef _WIN32 -#define __STDC_FORMAT_MACROS -#include -#endif - -using namespace web; -using namespace web::json; -using namespace utility; -using namespace utility::conversions; - -// -// JSON Serialization -// - -#ifdef _WIN32 -void web::json::value::serialize(std::ostream& stream) const -{ - // This has better performance than writing directly to stream. - std::string str; - m_value->serialize_impl(str); - stream << str; -} -void web::json::value::format(std::basic_string& string) const { m_value->format(string); } -#endif - -void web::json::value::serialize(utility::ostream_t& stream) const -{ -#ifndef _WIN32 - utility::details::scoped_c_thread_locale locale; -#endif - - // This has better performance than writing directly to stream. - utility::string_t str; - m_value->serialize_impl(str); - stream << str; -} - -void web::json::value::format(std::basic_string& string) const { m_value->format(string); } - -template -void web::json::details::append_escape_string(std::basic_string& str, - const std::basic_string& escaped) -{ - for (const auto& ch : escaped) - { - switch (ch) - { - case '\"': - str += '\\'; - str += '\"'; - break; - case '\\': - str += '\\'; - str += '\\'; - break; - case '\b': - str += '\\'; - str += 'b'; - break; - case '\f': - str += '\\'; - str += 'f'; - break; - case '\r': - str += '\\'; - str += 'r'; - break; - case '\n': - str += '\\'; - str += 'n'; - break; - case '\t': - str += '\\'; - str += 't'; - break; - default: - - // If a control character then must unicode escaped. - if (ch >= 0 && ch <= 0x1F) - { - static const std::array intToHex = { - {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}}; - str += '\\'; - str += 'u'; - str += '0'; - str += '0'; - str += intToHex[(ch & 0xF0) >> 4]; - str += intToHex[ch & 0x0F]; - } - else - { - str += ch; - } - } - } -} - -void web::json::details::format_string(const utility::string_t& key, utility::string_t& str) -{ - str.push_back('"'); - append_escape_string(str, key); - str.push_back('"'); -} - -#ifdef _WIN32 -void web::json::details::format_string(const utility::string_t& key, std::string& str) -{ - str.push_back('"'); - append_escape_string(str, utility::conversions::to_utf8string(key)); - str.push_back('"'); -} -#endif - -void web::json::details::_String::format(std::basic_string& str) const -{ - str.push_back('"'); - - if (m_has_escape_char) - { - append_escape_string(str, utility::conversions::to_utf8string(m_string)); - } - else - { - str.append(utility::conversions::to_utf8string(m_string)); - } - - str.push_back('"'); -} - -void web::json::details::_Number::format(std::basic_string& stream) const -{ - if (m_number.m_type != number::type::double_type) - { - // #digits + 1 to avoid loss + 1 for the sign + 1 for null terminator. - const size_t tempSize = std::numeric_limits::digits10 + 3; - char tempBuffer[tempSize]; - -#ifdef _WIN32 - // This can be improved performance-wise if we implement our own routine - if (m_number.m_type == number::type::signed_type) - _i64toa_s(m_number.m_intval, tempBuffer, tempSize, 10); - else - _ui64toa_s(m_number.m_uintval, tempBuffer, tempSize, 10); - - const auto numChars = strnlen_s(tempBuffer, tempSize); -#else - int numChars; - if (m_number.m_type == number::type::signed_type) - numChars = snprintf(tempBuffer, tempSize, "%" PRId64, m_number.m_intval); - else - numChars = snprintf(tempBuffer, tempSize, "%" PRIu64, m_number.m_uintval); -#endif - stream.append(tempBuffer, numChars); - } - else - { - // #digits + 2 to avoid loss + 1 for the sign + 1 for decimal point + 5 for exponent (e+xxx) + 1 for null - // terminator - const size_t tempSize = std::numeric_limits::digits10 + 10; - char tempBuffer[tempSize]; -#ifdef _WIN32 - const auto numChars = _sprintf_s_l(tempBuffer, - tempSize, - "%.*g", - utility::details::scoped_c_thread_locale::c_locale(), - std::numeric_limits::digits10 + 2, - m_number.m_value); -#else - const auto numChars = - snprintf(tempBuffer, tempSize, "%.*g", std::numeric_limits::digits10 + 2, m_number.m_value); -#endif - stream.append(tempBuffer, numChars); - } -} - -#ifdef _WIN32 - -void web::json::details::_String::format(std::basic_string& str) const -{ - str.push_back(L'"'); - - if (m_has_escape_char) - { - append_escape_string(str, m_string); - } - else - { - str.append(m_string); - } - - str.push_back(L'"'); -} - -void web::json::details::_Number::format(std::basic_string& stream) const -{ - if (m_number.m_type != number::type::double_type) - { - // #digits + 1 to avoid loss + 1 for the sign + 1 for null terminator. - const size_t tempSize = std::numeric_limits::digits10 + 3; - wchar_t tempBuffer[tempSize]; - - if (m_number.m_type == number::type::signed_type) - _i64tow_s(m_number.m_intval, tempBuffer, tempSize, 10); - else - _ui64tow_s(m_number.m_uintval, tempBuffer, tempSize, 10); - - stream.append(tempBuffer, wcsnlen_s(tempBuffer, tempSize)); - } - else - { - // #digits + 2 to avoid loss + 1 for the sign + 1 for decimal point + 5 for exponent (e+xxx) + 1 for null - // terminator - const size_t tempSize = std::numeric_limits::digits10 + 10; - wchar_t tempBuffer[tempSize]; - const int numChars = _swprintf_s_l(tempBuffer, - tempSize, - L"%.*g", - utility::details::scoped_c_thread_locale::c_locale(), - std::numeric_limits::digits10 + 2, - m_number.m_value); - stream.append(tempBuffer, numChars); - } -} - -#endif - -const utility::string_t& web::json::details::_String::as_string() const { return m_string; } - -const utility::string_t& web::json::value::as_string() const { return m_value->as_string(); } - -utility::string_t json::value::serialize() const -{ -#ifndef _WIN32 - utility::details::scoped_c_thread_locale locale; -#endif - return m_value->to_string(); -} diff --git a/deps/cpprestsdk/src/utilities/asyncrt_utils.cpp b/deps/cpprestsdk/src/utilities/asyncrt_utils.cpp deleted file mode 100644 index b9b03d8299..0000000000 --- a/deps/cpprestsdk/src/utilities/asyncrt_utils.cpp +++ /dev/null @@ -1,1490 +0,0 @@ -/*** - * Copyright (C) Microsoft. All rights reserved. - * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. - * - * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - * - * Utilities - * - * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk - * - * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - ****/ - -#include "pch.h" - -#include -#include -#include -#include -#include - -using namespace web; -using namespace utility; -using namespace utility::conversions; - -namespace -{ -struct to_lower_ch_impl -{ - char operator()(char c) const CPPREST_NOEXCEPT - { - if (c >= 'A' && c <= 'Z') return static_cast(c - 'A' + 'a'); - return c; - } - - wchar_t operator()(wchar_t c) const CPPREST_NOEXCEPT - { - if (c >= L'A' && c <= L'Z') return static_cast(c - L'A' + L'a'); - return c; - } -}; - -CPPREST_CONSTEXPR to_lower_ch_impl to_lower_ch {}; - -struct eq_lower_ch_impl -{ - template - inline CharT operator()(const CharT left, const CharT right) const CPPREST_NOEXCEPT - { - return to_lower_ch(left) == to_lower_ch(right); - } -}; - -CPPREST_CONSTEXPR eq_lower_ch_impl eq_lower_ch {}; - -struct lt_lower_ch_impl -{ - template - inline CharT operator()(const CharT left, const CharT right) const CPPREST_NOEXCEPT - { - return to_lower_ch(left) < to_lower_ch(right); - } -}; - -CPPREST_CONSTEXPR lt_lower_ch_impl lt_lower_ch {}; -} // namespace - -namespace utility -{ -namespace details -{ -_ASYNCRTIMP bool __cdecl str_iequal(const std::string& left, const std::string& right) CPPREST_NOEXCEPT -{ - return left.size() == right.size() && std::equal(left.cbegin(), left.cend(), right.cbegin(), eq_lower_ch); -} - -_ASYNCRTIMP bool __cdecl str_iequal(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT -{ - return left.size() == right.size() && std::equal(left.cbegin(), left.cend(), right.cbegin(), eq_lower_ch); -} - -_ASYNCRTIMP bool __cdecl str_iless(const std::string& left, const std::string& right) CPPREST_NOEXCEPT -{ - return std::lexicographical_compare(left.cbegin(), left.cend(), right.cbegin(), right.cend(), lt_lower_ch); -} - -_ASYNCRTIMP bool __cdecl str_iless(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT -{ - return std::lexicographical_compare(left.cbegin(), left.cend(), right.cbegin(), right.cend(), lt_lower_ch); -} - -_ASYNCRTIMP void __cdecl inplace_tolower(std::string& target) CPPREST_NOEXCEPT -{ - for (auto& ch : target) - { - ch = to_lower_ch(ch); - } -} - -_ASYNCRTIMP void __cdecl inplace_tolower(std::wstring& target) CPPREST_NOEXCEPT -{ - for (auto& ch : target) - { - ch = to_lower_ch(ch); - } -} - -#if !defined(ANDROID) && !defined(__ANDROID__) -std::once_flag g_c_localeFlag; -std::unique_ptr g_c_locale( - nullptr, [](scoped_c_thread_locale::xplat_locale*) {}); -scoped_c_thread_locale::xplat_locale scoped_c_thread_locale::c_locale() -{ - std::call_once(g_c_localeFlag, [&]() { - scoped_c_thread_locale::xplat_locale* clocale = new scoped_c_thread_locale::xplat_locale(); -#ifdef _WIN32 - *clocale = _create_locale(LC_ALL, "C"); - if (clocale == nullptr || *clocale == nullptr) - { - throw std::runtime_error("Unable to create 'C' locale."); - } - auto deleter = [](scoped_c_thread_locale::xplat_locale* clocale) { - _free_locale(*clocale); - delete clocale; - }; -#else - *clocale = newlocale(LC_ALL, "C", nullptr); - if (clocale == nullptr || *clocale == nullptr) - { - throw std::runtime_error("Unable to create 'C' locale."); - } - auto deleter = [](scoped_c_thread_locale::xplat_locale *clocale) - { - freelocale(*clocale); - delete clocale; - }; -#endif - g_c_locale = - std::unique_ptr( - clocale, deleter); - }); - return *g_c_locale; -} -#endif - -#ifdef _WIN32 -scoped_c_thread_locale::scoped_c_thread_locale() : m_prevLocale(), m_prevThreadSetting(-1) -{ - char* prevLocale = setlocale(LC_ALL, nullptr); - if (prevLocale == nullptr) - { - throw std::runtime_error("Unable to retrieve current locale."); - } - - if (std::strcmp(prevLocale, "C") != 0) - { - m_prevLocale = prevLocale; - m_prevThreadSetting = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); - if (m_prevThreadSetting == -1) - { - throw std::runtime_error("Unable to enable per thread locale."); - } - if (setlocale(LC_ALL, "C") == nullptr) - { - _configthreadlocale(m_prevThreadSetting); - throw std::runtime_error("Unable to set locale"); - } - } -} - -scoped_c_thread_locale::~scoped_c_thread_locale() -{ - if (m_prevThreadSetting != -1) - { - setlocale(LC_ALL, m_prevLocale.c_str()); - _configthreadlocale(m_prevThreadSetting); - } -} -#elif (defined(ANDROID) || defined(__ANDROID__)) -scoped_c_thread_locale::scoped_c_thread_locale() {} -scoped_c_thread_locale::~scoped_c_thread_locale() {} -#else -scoped_c_thread_locale::scoped_c_thread_locale() : m_prevLocale(nullptr) -{ - char* prevLocale = setlocale(LC_ALL, nullptr); - if (prevLocale == nullptr) - { - throw std::runtime_error("Unable to retrieve current locale."); - } - - if (std::strcmp(prevLocale, "C") != 0) - { - m_prevLocale = uselocale(c_locale()); - if (m_prevLocale == nullptr) - { - throw std::runtime_error("Unable to set locale"); - } - } -} - -scoped_c_thread_locale::~scoped_c_thread_locale() -{ - if (m_prevLocale != nullptr) - { - uselocale(m_prevLocale); - } -} -#endif -} // namespace details - -namespace details -{ -const std::error_category& __cdecl platform_category() -{ -#ifdef _WIN32 - return windows_category(); -#else - return linux_category(); -#endif -} - -#ifdef _WIN32 - -// Remove once VS 2013 is no longer supported. -#if _MSC_VER < 1900 -static details::windows_category_impl instance; -#endif -const std::error_category& __cdecl windows_category() -{ -#if _MSC_VER >= 1900 - static details::windows_category_impl instance; -#endif - return instance; -} - -std::string windows_category_impl::message(int errorCode) const CPPREST_NOEXCEPT -{ - const size_t buffer_size = 4096; - DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM; - LPCVOID lpSource = NULL; - -#if !defined(__cplusplus_winrt) - if (errorCode >= 12000) - { - dwFlags = FORMAT_MESSAGE_FROM_HMODULE; - lpSource = GetModuleHandleA("winhttp.dll"); // this handle DOES NOT need to be freed - } -#endif - - std::wstring buffer(buffer_size, 0); - - const auto result = ::FormatMessageW(dwFlags, lpSource, errorCode, 0, &buffer[0], buffer_size, NULL); - - if (result == 0) - { - return "Unable to get an error message for error code: " + std::to_string(errorCode) + "."; - } - - // strip exceeding characters of the initial resize call - buffer.resize(result); - - return utility::conversions::to_utf8string(buffer); -} - -std::error_condition windows_category_impl::default_error_condition(int errorCode) const CPPREST_NOEXCEPT -{ - // First see if the STL implementation can handle the mapping for common cases. - const std::error_condition errCondition = std::system_category().default_error_condition(errorCode); - const std::string errConditionMsg = errCondition.message(); - if (!utility::details::str_iequal(errConditionMsg, "unknown error")) - { - return errCondition; - } - - switch (errorCode) - { - /* -#ifndef __cplusplus_winrt - - case ERROR_WINHTTP_TIMEOUT: return std::errc::timed_out; - case ERROR_WINHTTP_CANNOT_CONNECT: return std::errc::host_unreachable; - case ERROR_WINHTTP_CONNECTION_ERROR: return std::errc::connection_aborted; -#endif*/ - case INET_E_RESOURCE_NOT_FOUND: - case INET_E_CANNOT_CONNECT: return std::errc::host_unreachable; - case INET_E_CONNECTION_TIMEOUT: return std::errc::timed_out; - case INET_E_DOWNLOAD_FAILURE: return std::errc::connection_aborted; - default: break; - } - - return std::error_condition(errorCode, *this); -} - -#else - -const std::error_category& __cdecl linux_category() -{ - // On Linux we are using boost error codes which have the exact same - // mapping and are equivalent with std::generic_category error codes. - return std::generic_category(); -} - -#endif - -} // namespace details - -#define LOW_3BITS 0x7 -#define LOW_4BITS 0xF -#define LOW_5BITS 0x1F -#define LOW_6BITS 0x3F -#define BIT4 0x8 -#define BIT5 0x10 -#define BIT6 0x20 -#define BIT7 0x40 -#define BIT8 0x80 -#define L_SURROGATE_START 0xDC00 -#define L_SURROGATE_END 0xDFFF -#define H_SURROGATE_START 0xD800 -#define H_SURROGATE_END 0xDBFF -#define SURROGATE_PAIR_START 0x10000 - -// Create a dedicated type for characters to avoid the issue -// of different platforms defaulting char to be either signed -// or unsigned. -using UtilCharInternal_t = signed char; - -inline size_t count_utf8_to_utf16(const std::string& s) -{ - const size_t sSize = s.size(); - auto const sData = reinterpret_cast(s.data()); - size_t result {sSize}; - - for (size_t index = 0; index < sSize;) - { - if (sData[index] >= 0) - { - // use fast inner loop to skip single byte code points (which are - // expected to be the most frequent) - while ((++index < sSize) && (sData[index] >= 0)) - ; - - if (index >= sSize) break; - } - - // start special handling for multi-byte code points - const UtilCharInternal_t c {sData[index++]}; - - if ((c & BIT7) == 0) - { - throw std::range_error("UTF-8 string character can never start with 10xxxxxx"); - } - else if ((c & BIT6) == 0) // 2 byte character, 0x80 to 0x7FF - { - if (index == sSize) - { - throw std::range_error("UTF-8 string is missing bytes in character"); - } - - const UtilCharInternal_t c2 {sData[index++]}; - if ((c2 & 0xC0) != BIT8) - { - throw std::range_error("UTF-8 continuation byte is missing leading bit mask"); - } - - // can't require surrogates for 7FF - --result; - } - else if ((c & BIT5) == 0) // 3 byte character, 0x800 to 0xFFFF - { - if (sSize - index < 2) - { - throw std::range_error("UTF-8 string is missing bytes in character"); - } - - const UtilCharInternal_t c2 {sData[index++]}; - const UtilCharInternal_t c3 {sData[index++]}; - if (((c2 | c3) & 0xC0) != BIT8) - { - throw std::range_error("UTF-8 continuation byte is missing leading bit mask"); - } - - result -= 2; - } - else if ((c & BIT4) == 0) // 4 byte character, 0x10000 to 0x10FFFF - { - if (sSize - index < 3) - { - throw std::range_error("UTF-8 string is missing bytes in character"); - } - - const UtilCharInternal_t c2 {sData[index++]}; - const UtilCharInternal_t c3 {sData[index++]}; - const UtilCharInternal_t c4 {sData[index++]}; - if (((c2 | c3 | c4) & 0xC0) != BIT8) - { - throw std::range_error("UTF-8 continuation byte is missing leading bit mask"); - } - - const uint32_t codePoint = - ((c & LOW_3BITS) << 18) | ((c2 & LOW_6BITS) << 12) | ((c3 & LOW_6BITS) << 6) | (c4 & LOW_6BITS); - result -= (3 - (codePoint >= SURROGATE_PAIR_START)); - } - else - { - throw std::range_error("UTF-8 string has invalid Unicode code point"); - } - } - - return result; -} - -utf16string __cdecl conversions::utf8_to_utf16(const std::string& s) -{ - // Save repeated heap allocations, use the length of resulting sequence. - const size_t srcSize = s.size(); - auto const srcData = reinterpret_cast(s.data()); - utf16string dest(count_utf8_to_utf16(s), L'\0'); - utf16string::value_type* const destData = &dest[0]; - size_t destIndex = 0; - - for (size_t index = 0; index < srcSize; ++index) - { - UtilCharInternal_t src = srcData[index]; - switch (src & 0xF0) - { - case 0xF0: // 4 byte character, 0x10000 to 0x10FFFF - { - const UtilCharInternal_t c2 {srcData[++index]}; - const UtilCharInternal_t c3 {srcData[++index]}; - const UtilCharInternal_t c4 {srcData[++index]}; - uint32_t codePoint = - ((src & LOW_3BITS) << 18) | ((c2 & LOW_6BITS) << 12) | ((c3 & LOW_6BITS) << 6) | (c4 & LOW_6BITS); - if (codePoint >= SURROGATE_PAIR_START) - { - // In UTF-16 U+10000 to U+10FFFF are represented as two 16-bit code units, surrogate pairs. - // - 0x10000 is subtracted from the code point - // - high surrogate is 0xD800 added to the top ten bits - // - low surrogate is 0xDC00 added to the low ten bits - codePoint -= SURROGATE_PAIR_START; - destData[destIndex++] = static_cast((codePoint >> 10) | H_SURROGATE_START); - destData[destIndex++] = - static_cast((codePoint & 0x3FF) | L_SURROGATE_START); - } - else - { - // In UTF-16 U+0000 to U+D7FF and U+E000 to U+FFFF are represented exactly as the Unicode code point - // value. U+D800 to U+DFFF are not valid characters, for simplicity we assume they are not present - // but will encode them if encountered. - destData[destIndex++] = static_cast(codePoint); - } - } - break; - case 0xE0: // 3 byte character, 0x800 to 0xFFFF - { - const UtilCharInternal_t c2 {srcData[++index]}; - const UtilCharInternal_t c3 {srcData[++index]}; - destData[destIndex++] = static_cast( - ((src & LOW_4BITS) << 12) | ((c2 & LOW_6BITS) << 6) | (c3 & LOW_6BITS)); - } - break; - case 0xD0: // 2 byte character, 0x80 to 0x7FF - case 0xC0: - { - const UtilCharInternal_t c2 {srcData[++index]}; - destData[destIndex++] = - static_cast(((src & LOW_5BITS) << 6) | (c2 & LOW_6BITS)); - } - break; - default: // single byte character, 0x0 to 0x7F - // try to use a fast inner loop for following single byte characters, - // since they are quite probable - do - { - destData[destIndex++] = static_cast(srcData[index++]); - } while (index < srcSize && srcData[index] > 0); - // adjust index since it will be incremented by the for loop - --index; - } - } - return dest; -} - -inline size_t count_utf16_to_utf8(const utf16string& w) -{ - const utf16string::value_type* const srcData = &w[0]; - const size_t srcSize = w.size(); - size_t destSize(srcSize); - for (size_t index = 0; index < srcSize; ++index) - { - const utf16string::value_type ch(srcData[index]); - if (ch <= 0x7FF) - { - if (ch > 0x7F) // 2 bytes needed (11 bits used) - { - ++destSize; - } - } - // Check for high surrogate. - else if (ch >= H_SURROGATE_START && ch <= H_SURROGATE_END) // 4 bytes needed (21 bits used) - { - ++index; - if (index == srcSize) - { - throw std::range_error("UTF-16 string is missing low surrogate"); - } - - const auto lowSurrogate = srcData[index]; - if (lowSurrogate < L_SURROGATE_START || lowSurrogate > L_SURROGATE_END) - { - throw std::range_error("UTF-16 string has invalid low surrogate"); - } - - destSize += 2; - } - else // 3 bytes needed (16 bits used) - { - destSize += 2; - } - } - - return destSize; -} - -std::string __cdecl conversions::utf16_to_utf8(const utf16string& w) -{ - const size_t srcSize = w.size(); - const utf16string::value_type* const srcData = &w[0]; - std::string dest(count_utf16_to_utf8(w), '\0'); - std::string::value_type* const destData = &dest[0]; - size_t destIndex(0); - - for (size_t index = 0; index < srcSize; ++index) - { - const utf16string::value_type src = srcData[index]; - if (src <= 0x7FF) - { - if (src <= 0x7F) // single byte character - { - destData[destIndex++] = static_cast(src); - } - else // 2 bytes needed (11 bits used) - { - destData[destIndex++] = static_cast(char((src >> 6) | 0xC0)); // leading 5 bits - destData[destIndex++] = static_cast(char((src & LOW_6BITS) | BIT8)); // trailing 6 bits - } - } - // Check for high surrogate. - else if (src >= H_SURROGATE_START && src <= H_SURROGATE_END) - { - const auto highSurrogate = src; - const auto lowSurrogate = srcData[++index]; - - // To get from surrogate pair to Unicode code point: - // - subtract 0xD800 from high surrogate, this forms top ten bits - // - subtract 0xDC00 from low surrogate, this forms low ten bits - // - add 0x10000 - // Leaves a code point in U+10000 to U+10FFFF range. - uint32_t codePoint = highSurrogate - H_SURROGATE_START; - codePoint <<= 10; - codePoint |= lowSurrogate - L_SURROGATE_START; - codePoint += SURROGATE_PAIR_START; - - // 4 bytes needed (21 bits used) - destData[destIndex++] = static_cast((codePoint >> 18) | 0xF0); // leading 3 bits - destData[destIndex++] = static_cast(((codePoint >> 12) & LOW_6BITS) | BIT8); // next 6 bits - destData[destIndex++] = static_cast(((codePoint >> 6) & LOW_6BITS) | BIT8); // next 6 bits - destData[destIndex++] = static_cast((codePoint & LOW_6BITS) | BIT8); // trailing 6 bits - } - else // 3 bytes needed (16 bits used) - { - destData[destIndex++] = static_cast((src >> 12) | 0xE0); // leading 4 bits - destData[destIndex++] = static_cast(((src >> 6) & LOW_6BITS) | BIT8); // middle 6 bits - destData[destIndex++] = static_cast((src & LOW_6BITS) | BIT8); // trailing 6 bits - } - } - - return dest; -} - -utf16string __cdecl conversions::usascii_to_utf16(const std::string& s) -{ - // Ascii is a subset of UTF-8 so just convert to UTF-16 - return utf8_to_utf16(s); -} - -utf16string __cdecl conversions::latin1_to_utf16(const std::string& s) -{ - // Latin1 is the first 256 code points in Unicode. - // In UTF-16 encoding each of these is represented as exactly the numeric code point. - utf16string dest; - // Prefer resize combined with for-loop over constructor dest(s.begin(), s.end()) - // for faster assignment. - dest.resize(s.size()); - for (size_t i = 0; i < s.size(); ++i) - { - dest[i] = utf16char(static_cast(s[i])); - } - return dest; -} - -utf8string __cdecl conversions::latin1_to_utf8(const std::string& s) { return utf16_to_utf8(latin1_to_utf16(s)); } - -#ifndef _UTF16_STRINGS -utility::string_t __cdecl conversions::to_string_t(utf16string&& s) { return utf16_to_utf8(std::move(s)); } -#endif - -#ifdef _UTF16_STRINGS -utility::string_t __cdecl conversions::to_string_t(std::string&& s) { return utf8_to_utf16(std::move(s)); } -#endif - -#ifndef _UTF16_STRINGS -utility::string_t __cdecl conversions::to_string_t(const utf16string& s) { return utf16_to_utf8(s); } -#endif - -#ifdef _UTF16_STRINGS -utility::string_t __cdecl conversions::to_string_t(const std::string& s) { return utf8_to_utf16(s); } -#endif - -std::string __cdecl conversions::to_utf8string(const utf16string& value) { return utf16_to_utf8(value); } - -utf16string __cdecl conversions::to_utf16string(const std::string& value) { return utf8_to_utf16(value); } - -static const int64_t NtToUnixOffsetSeconds = 11644473600; // diff between windows and unix epochs (seconds) - -static bool year_is_leap_year(int year) { return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); } - -static const int SecondsInMinute = 60; -static const int SecondsInHour = SecondsInMinute * 60; -static const int SecondsInDay = SecondsInHour * 24; - -static const int DaysInYear = 365; -static const int DaysIn4Years = DaysInYear * 4 + 1; -static const int DaysIn100Years = DaysIn4Years * 25 - 1; -static const int DaysIn400Years = DaysIn100Years * 4 + 1; - -static const int SecondsInYear = SecondsInDay * DaysInYear; -static const int SecondsIn4Years = SecondsInDay * DaysIn4Years; -static const int64_t SecondsIn100Years = static_cast(SecondsInDay) * DaysIn100Years; -static const int64_t SecondsIn400Years = static_cast(SecondsInDay) * DaysIn400Years; -static const int64_t SecondsFrom1900To2001 = INT64_C(3187296000); - -static const int64_t NtTo1900OffsetInterval = INT64_C(0x014F373BFDE04000); - -static int count_leap_years(const int yearsSince1900) -{ - int tmpYears = yearsSince1900 + 299; // shift into 1601, the first 400 year cycle including 1900 - - int year400 = tmpYears / 400; - tmpYears -= year400 * 400; - int result = year400 * 97; - - int year100 = tmpYears / 100; - tmpYears -= year100 * 100; - result += year100 * 24; - - result += tmpYears / 4; - - // subtract off leap years from 1601 - result -= 72; - - return result; -} - -// The following table assumes no leap year; leap year is added separately -static const unsigned short cumulative_days_to_month[12] = { - 0, // Jan - 31, // Feb - 59, // Mar - 90, // Apr - 120, // May - 151, // Jun - 181, // Jul - 212, // Aug - 243, // Sep - 273, // Oct - 304, // Nov - 334 // Dec -}; - -static const unsigned short cumulative_days_to_month_leap[12] = { - 0, // Jan - 31, // Feb - 60, // Mar - 91, // Apr - 121, // May - 152, // Jun - 182, // Jul - 213, // Aug - 244, // Sep - 274, // Oct - 305, // Nov - 335 // Dec -}; - -datetime __cdecl datetime::utc_now() -{ -#ifdef _WIN32 - ULARGE_INTEGER largeInt; - FILETIME fileTime; - GetSystemTimeAsFileTime(&fileTime); - - largeInt.LowPart = fileTime.dwLowDateTime; - largeInt.HighPart = fileTime.dwHighDateTime; - - return datetime(largeInt.QuadPart); -#else // LINUX - struct timeval time; - gettimeofday(&time, nullptr); - int64_t result = NtToUnixOffsetSeconds + time.tv_sec; - result *= _secondTicks; // convert to 10e-7 - result += time.tv_usec * 10; // convert and add microseconds, 10e-6 to 10e-7 - return datetime(static_cast(result)); -#endif -} - -static const char dayNames[] = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat"; -static const char monthNames[] = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\0Dec"; - -struct compute_year_result -{ - int year; - int secondsLeftThisYear; -}; - -static const int64_t secondsFrom1601To1900 = INT64_C(9435484800); - -static compute_year_result compute_year(int64_t secondsSince1900) -{ - int64_t secondsLeft = secondsSince1900 + secondsFrom1601To1900; // shift to start of this 400 year cycle - - int year400 = static_cast(secondsLeft / SecondsIn400Years); - secondsLeft -= year400 * SecondsIn400Years; - - int year100 = static_cast(secondsLeft / SecondsIn100Years); - secondsLeft -= year100 * SecondsIn100Years; - - int year4 = static_cast(secondsLeft / SecondsIn4Years); - int secondsInt = static_cast(secondsLeft - year4 * SecondsIn4Years); - - int year1 = secondsInt / SecondsInYear; - secondsInt -= year1 * SecondsInYear; - - // shift back to 1900 base from 1601: - return {year400 * 400 + year100 * 100 + year4 * 4 + year1 - 299, secondsInt}; -} - -utility::string_t datetime::to_string(date_format format) const -{ - if (m_interval > INT64_C(2650467743990000000)) - { - throw std::out_of_range("The requested year exceeds the year 9999."); - } - - const int64_t epochAdjusted = static_cast(m_interval) - NtTo1900OffsetInterval; - const int64_t secondsSince1900 = epochAdjusted / _secondTicks; // convert to seconds - const int fracSec = static_cast(epochAdjusted % _secondTicks); - - const auto yearData = compute_year(secondsSince1900); - const int year = yearData.year; - const int yearDay = yearData.secondsLeftThisYear / SecondsInDay; - int leftover = yearData.secondsLeftThisYear % SecondsInDay; - const int hour = leftover / SecondsInHour; - leftover = leftover % SecondsInHour; - const int minute = leftover / SecondsInMinute; - leftover = leftover % SecondsInMinute; - - const auto& monthTable = year_is_leap_year(year) ? cumulative_days_to_month_leap : cumulative_days_to_month; - int month = 0; - while (month < 11 && monthTable[month + 1] <= yearDay) - { - ++month; - } - - const auto monthDay = yearDay - monthTable[month] + 1; - const auto weekday = static_cast((secondsSince1900 / SecondsInDay + 1) % 7); - - char outBuffer[38]; // Thu, 01 Jan 1970 00:00:00 GMT\0 - // 1970-01-01T00:00:00.1234567Z\0 - char* outCursor = outBuffer; - switch (format) - { - case RFC_1123: -#ifdef _MSC_VER - sprintf_s(outCursor, - 26, - "%s, %02d %s %04d %02d:%02d:%02d", - dayNames + 4 * weekday, - monthDay, - monthNames + 4 * month, - year + 1900, - hour, - minute, - leftover); -#else // ^^^ _MSC_VER // !_MSC_VER vvv - sprintf(outCursor, - "%s, %02d %s %04d %02d:%02d:%02d", - dayNames + 4 * weekday, - monthDay, - monthNames + 4 * month, - year + 1900, - hour, - minute, - leftover); -#endif // _MSC_VER - outCursor += 25; - memcpy(outCursor, " GMT", 4); - outCursor += 4; - return utility::string_t(outBuffer, outCursor); - case ISO_8601: -#ifdef _MSC_VER - sprintf_s(outCursor, - 20, - "%04d-%02d-%02dT%02d:%02d:%02d", - year + 1900, - month + 1, - monthDay, - hour, - minute, - leftover); -#else // ^^^ _MSC_VER // !_MSC_VER vvv - sprintf( - outCursor, "%04d-%02d-%02dT%02d:%02d:%02d", year + 1900, month + 1, monthDay, hour, minute, leftover); -#endif // _MSC_VER - outCursor += 19; - if (fracSec != 0) - { - // Append fractional second, which is a 7-digit value with no trailing zeros - // This way, '1200' becomes '00012' -#ifdef _MSC_VER - size_t appended = sprintf_s(outCursor, 9, ".%07d", fracSec); -#else // ^^^ _MSC_VER // !_MSC_VER vvv - size_t appended = sprintf(outCursor, ".%07d", fracSec); -#endif // _MSC_VER - while (outCursor[appended - 1] == '0') - { - --appended; // trim trailing zeros - } - - outCursor += appended; - } - - *outCursor = 'Z'; - ++outCursor; - return utility::string_t(outBuffer, outCursor); - default: throw std::invalid_argument("Unrecognized date format."); - } -} - -template -static bool string_starts_with(const CharT* haystack, const char* needle) -{ - while (*needle) - { - if (*haystack != static_cast(*needle)) - { - return false; - } - - ++haystack; - ++needle; - } - - return true; -} - -#define ascii_isdigit(c) ((unsigned char)((unsigned char)(c) - '0') <= 9) -#define ascii_isdigit6(c) ((unsigned char)((unsigned char)(c) - '0') <= 6) -#define ascii_isdigit5(c) ((unsigned char)((unsigned char)(c) - '0') <= 5) -#define ascii_isdigit3(c) ((unsigned char)((unsigned char)(c) - '0') <= 3) -#define ascii_isdigit2(c) ((unsigned char)((unsigned char)(c) - '0') <= 2) -#define ascii_isdigit1(c) ((unsigned char)((unsigned char)(c) - '0') <= 1) - -static const unsigned char max_days_in_month[12] = { - 31, // Jan - 00, // Feb, special handling for leap years - 31, // Mar - 30, // Apr - 31, // May - 30, // Jun - 31, // Jul - 31, // Aug - 30, // Sep - 31, // Oct - 30, // Nov - 31 // Dec -}; - -static bool validate_day_month(int day, int month, int year) -{ - int maxDaysThisMonth; - if (month == 1) - { // Feb needs leap year testing - maxDaysThisMonth = 28 + year_is_leap_year(year); - } - else - { - maxDaysThisMonth = max_days_in_month[month]; - } - - return day >= 1 && day <= maxDaysThisMonth; -} - -static int get_year_day(int month, int monthDay, int year) -{ - return cumulative_days_to_month[month] + monthDay + (year_is_leap_year(year) && month > 1) - 1; -} - -template -static int atoi2(const CharT* str) -{ - return (static_cast(str[0]) - '0') * 10 + (static_cast(str[1]) - '0'); -} - -static int64_t timezone_adjust(int64_t result, unsigned char chSign, int adjustHours, int adjustMinutes) -{ - if (adjustHours > 23) - { - return -1; - } - - // adjustMinutes > 59 is impossible due to digit 5 check - const int tzAdjust = adjustMinutes * 60 + adjustHours * 60 * 60; - if (chSign == '-') - { - if (INT64_MAX - result < tzAdjust) - { - return -1; - } - - result += tzAdjust; - } - else - { - if (tzAdjust > result) - { - return -1; - } - - result -= tzAdjust; - } - - return result; -} - -/* -https://tools.ietf.org/html/rfc822 -https://tools.ietf.org/html/rfc1123 - -date-time = [ day "," ] date time ; dd mm yy - ; hh:mm:ss zzz - -day = "Mon" / "Tue" / "Wed" / "Thu" - / "Fri" / "Sat" / "Sun" - -date = 1*2DIGIT month 2DIGIT ; day month year - ; e.g. 20 Jun 82 -RFC1123 changes this to: -date = 1*2DIGIT month 2*4DIGIT ; day month year - ; e.g. 20 Jun 1982 -This implementation only accepts 4 digit years. - -month = "Jan" / "Feb" / "Mar" / "Apr" - / "May" / "Jun" / "Jul" / "Aug" - / "Sep" / "Oct" / "Nov" / "Dec" - -time = hour zone ; ANSI and Military - -hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT] - ; 00:00:00 - 23:59:59 - -zone = "UT" / "GMT" ; Universal Time - ; North American : UT - / "EST" / "EDT" ; Eastern: - 5/ - 4 - / "CST" / "CDT" ; Central: - 6/ - 5 - / "MST" / "MDT" ; Mountain: - 7/ - 6 - / "PST" / "PDT" ; Pacific: - 8/ - 7 - -// military time deleted by RFC 1123 - - / ( ("+" / "-") 4DIGIT ) ; Local differential - ; hours+min. (HHMM) -*/ - - -datetime __cdecl datetime::from_string(const utility::string_t& dateString, date_format format) -{ - datetime result; - int64_t secondsSince1900; - uint64_t fracSec = 0; - auto str = dateString.c_str(); - if (format == RFC_1123) - { - int parsedWeekday = 0; - for (; parsedWeekday < 7; ++parsedWeekday) - { - if (string_starts_with(str, dayNames + parsedWeekday * 4) && str[3] == _XPLATSTR(',') && - str[4] == _XPLATSTR(' ')) - { - str += 5; // parsed day of week - break; - } - } - - int monthDay; - if (ascii_isdigit3(str[0]) && ascii_isdigit(str[1]) && str[2] == _XPLATSTR(' ')) - { - monthDay = atoi2(str); // validity checked later - str += 3; // parsed day - } - else if (ascii_isdigit(str[0]) && str[1] == _XPLATSTR(' ')) - { - monthDay = str[0] - _XPLATSTR('0'); - str += 2; // parsed day - } - else - { - return result; - } - - if (monthDay == 0) - { - return result; - } - - int month = 0; - for (;;) - { - if (string_starts_with(str, monthNames + month * 4)) - { - break; - } - - ++month; - if (month == 12) - { - return result; - } - } - - if (str[3] != _XPLATSTR(' ')) - { - return result; - } - - str += 4; // parsed month - - if (!ascii_isdigit(str[0]) || !ascii_isdigit(str[1]) || !ascii_isdigit(str[2]) || !ascii_isdigit(str[3]) || - str[4] != ' ') - { - return result; - } - - int year = (str[0] - _XPLATSTR('0')) * 1000 + (str[1] - _XPLATSTR('0')) * 100 + (str[2] - _XPLATSTR('0')) * 10 + - (str[3] - _XPLATSTR('0')); - if (year < 1900) - { - return result; - } - - // days in month validity check - if (!validate_day_month(monthDay, month, year)) - { - return result; - } - - str += 5; // parsed year - const int yearDay = get_year_day(month, monthDay, year); - - if (!ascii_isdigit2(str[0]) || !ascii_isdigit(str[1]) || str[2] != _XPLATSTR(':') || !ascii_isdigit5(str[3]) || - !ascii_isdigit(str[4])) - { - return result; - } - - const int hour = atoi2(str); - if (hour > 23) - { - return result; - } - - str += 3; // parsed hour - const int minute = atoi2(str); - str += 2; // parsed mins - - int sec; - if (str[0] == ':') - { - if (!ascii_isdigit6(str[1]) || !ascii_isdigit(str[2]) || str[3] != _XPLATSTR(' ')) - { - return result; - } - - sec = atoi2(str + 1); - str += 4; // parsed seconds - } - else if (str[0] == _XPLATSTR(' ')) - { - sec = 0; - str += 1; // parsed seconds - } - else - { - return result; - } - - if (sec > 60) - { // 60 to allow leap seconds - return result; - } - - year -= 1900; - int daysSince1900 = year * DaysInYear + count_leap_years(year) + yearDay; - - if (parsedWeekday != 7) - { - const int actualWeekday = (daysSince1900 + 1) % 7; - - if (parsedWeekday != actualWeekday) - { - return result; - } - } - - secondsSince1900 = - static_cast(daysSince1900) * SecondsInDay + hour * SecondsInHour + minute * SecondsInMinute + sec; - - if (!string_starts_with(str, "GMT") && !string_starts_with(str, "UT")) - { - // some timezone adjustment necessary - auto tzCh = _XPLATSTR('-'); - int tzHours; - int tzMinutes = 0; - if (string_starts_with(str, "EDT")) - { - tzHours = 4; - } - else if (string_starts_with(str, "EST") || string_starts_with(str, "CDT")) - { - tzHours = 5; - } - else if (string_starts_with(str, "CST") || string_starts_with(str, "MDT")) - { - tzHours = 6; - } - else if (string_starts_with(str, "MST") || string_starts_with(str, "PDT")) - { - tzHours = 7; - } - else if (string_starts_with(str, "PST")) - { - tzHours = 8; - } - else if ((tzCh == _XPLATSTR('+') || tzCh == _XPLATSTR('-')) && ascii_isdigit2(str[1]) && - ascii_isdigit(str[2]) && ascii_isdigit5(str[3]) && ascii_isdigit(str[4])) - { - tzCh = str[0]; - tzHours = atoi2(str + 1); - tzMinutes = atoi2(str + 3); - } - else - { - return result; - } - - secondsSince1900 = timezone_adjust(secondsSince1900, static_cast(tzCh), tzHours, tzMinutes); - if (secondsSince1900 < 0) - { - return result; - } - } - } - else if (format == ISO_8601) - { - // parse year - if (!ascii_isdigit(str[0]) || !ascii_isdigit(str[1]) || !ascii_isdigit(str[2]) || !ascii_isdigit(str[3])) - { - return result; - } - - int year = (str[0] - _XPLATSTR('0')) * 1000 + (str[1] - _XPLATSTR('0')) * 100 + (str[2] - _XPLATSTR('0')) * 10 + - (str[3] - _XPLATSTR('0')); - if (year < 1900) - { - return result; - } - - str += 4; - if (*str == _XPLATSTR('-')) - { - ++str; - } - - // parse month - if (!ascii_isdigit1(str[0]) || !ascii_isdigit(str[1])) - { - return result; - } - - int month = atoi2(str); - if (month < 1 || month > 12) - { - return result; - } - - month -= 1; - str += 2; - - if (*str == _XPLATSTR('-')) - { - ++str; - } - - // parse day - if (!ascii_isdigit3(str[0]) || !ascii_isdigit(str[1])) - { - return result; - } - - int monthDay = atoi2(str); - if (!validate_day_month(monthDay, month, year)) - { - return result; - } - - const int yearDay = get_year_day(month, monthDay, year); - - str += 2; - year -= 1900; - int daysSince1900 = year * DaysInYear + count_leap_years(year) + yearDay; - - if (str[0] != _XPLATSTR('T') && str[0] != _XPLATSTR('t')) - { - // No time - secondsSince1900 = static_cast(daysSince1900) * SecondsInDay; - - result.m_interval = - static_cast(secondsSince1900 * _secondTicks + fracSec + NtTo1900OffsetInterval); - return result; - } - - ++str; // skip 'T' - - // parse hour - if (!ascii_isdigit2(str[0]) || !ascii_isdigit(str[1])) - { - return result; - } - - const int hour = atoi2(str); - str += 2; - if (hour > 23) - { - return result; - } - - if (*str == _XPLATSTR(':')) - { - ++str; - } - - // parse minute - if (!ascii_isdigit5(str[0]) || !ascii_isdigit(str[1])) - { - return result; - } - - const int minute = atoi2(str); - // minute > 59 is impossible because we checked that the first digit is <= 5 in the basic format - // check above - - str += 2; - - if (*str == _XPLATSTR(':')) - { - ++str; - } - - // parse seconds - if (!ascii_isdigit6(str[0]) || !ascii_isdigit(str[1])) - { - return result; - } - - const int sec = atoi2(str); - // We allow 60 to account for leap seconds - if (sec > 60) - { - return result; - } - - str += 2; - if (str[0] == _XPLATSTR('.') && ascii_isdigit(str[1])) - { - ++str; - int digits = 7; - for (;;) - { - fracSec *= 10; - fracSec += *str - _XPLATSTR('0'); - --digits; - ++str; - if (digits == 0) - { - while (ascii_isdigit(*str)) - { - // consume remaining fractional second digits we can't use - ++str; - } - - break; - } - - if (!ascii_isdigit(*str)) - { - // no more digits in the input, do the remaining multiplies we need - for (; digits != 0; --digits) - { - fracSec *= 10; - } - - break; - } - } - } - - secondsSince1900 = - static_cast(daysSince1900) * SecondsInDay + hour * SecondsInHour + minute * SecondsInMinute + sec; - - if (str[0] == _XPLATSTR('Z') || str[0] == _XPLATSTR('z')) - { - // no adjustment needed for zulu time - } - else if (str[0] == _XPLATSTR('+') || str[0] == _XPLATSTR('-')) - { - const unsigned char offsetDirection = static_cast(str[0]); - if (!ascii_isdigit2(str[1]) || !ascii_isdigit(str[2]) || str[3] != _XPLATSTR(':') || - !ascii_isdigit5(str[4]) || !ascii_isdigit(str[5])) - { - return result; - } - - secondsSince1900 = timezone_adjust(secondsSince1900, offsetDirection, atoi2(str + 1), atoi2(str + 4)); - if (secondsSince1900 < 0) - { - return result; - } - } - else - { - // the timezone is malformed, but cpprestsdk currently accepts this as no timezone - } - } - else - { - throw std::invalid_argument("unrecognized date format"); - } - - result.m_interval = static_cast(secondsSince1900 * _secondTicks + fracSec + NtTo1900OffsetInterval); - return result; -} - -/// -/// Converts a timespan/interval in seconds to xml duration string as specified by -/// http://www.w3.org/TR/xmlschema-2/#duration -/// -utility::string_t __cdecl timespan::seconds_to_xml_duration(utility::seconds durationSecs) -{ - auto numSecs = durationSecs.count(); - - // Find the number of minutes - auto numMins = numSecs / 60; - if (numMins > 0) - { - numSecs = numSecs % 60; - } - - // Hours - auto numHours = numMins / 60; - if (numHours > 0) - { - numMins = numMins % 60; - } - - // Days - auto numDays = numHours / 24; - if (numDays > 0) - { - numHours = numHours % 24; - } - - // The format is: - // PdaysDThoursHminutesMsecondsS - utility::string_t result; - // (approximate mins/hours/secs as 2 digits each + 1 prefix character) + 1 for P prefix + 1 for T - size_t baseReserveSize = ((numHours > 0) + (numMins > 0) + (numSecs > 0)) * 3 + 1; - if (numDays > 0) - { - utility::string_t daysStr = utility::conversions::details::to_string_t(numDays); - result.reserve(baseReserveSize + daysStr.size() + 1); - result += _XPLATSTR('P'); - result += daysStr; - result += _XPLATSTR('D'); - } - else - { - result.reserve(baseReserveSize); - result += _XPLATSTR('P'); - } - - result += _XPLATSTR('T'); - - if (numHours > 0) - { - result += utility::conversions::details::to_string_t(numHours); - result += _XPLATSTR('H'); - } - - if (numMins > 0) - { - result += utility::conversions::details::to_string_t(numMins); - result += _XPLATSTR('M'); - } - - if (numSecs > 0) - { - result += utility::conversions::details::to_string_t(numSecs); - result += _XPLATSTR('S'); - } - - return result; -} - -utility::seconds __cdecl timespan::xml_duration_to_seconds(const utility::string_t& timespanString) -{ - // The format is: - // PnDTnHnMnS - // if n == 0 then the field could be omitted - // The final S could be omitted - - int64_t numSecs = 0; - auto cursor = timespanString.c_str(); - auto c = *cursor++; // skip 'P' - while (c) - { - int val = 0; - c = *cursor++; - - while (ascii_isdigit(c)) - { - val = val * 10 + (c - _XPLATSTR('0')); - c = *cursor++; - - if (c == _XPLATSTR('.')) - { - // decimal point is not handled - do - { - c = *cursor++; - } while (ascii_isdigit(c)); - } - } - - if (c == _XPLATSTR('D')) numSecs += val * 24 * 3600; // days - if (c == _XPLATSTR('H')) numSecs += val * 3600; // Hours - if (c == _XPLATSTR('M')) numSecs += val * 60; // Minutes - if (c == _XPLATSTR('S') || c == _XPLATSTR('\0')) - { - numSecs += val; // seconds - break; - } - } - - return utility::seconds(numSecs); -} - -static const char c_allowed_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; -static const int chars_count = static_cast(sizeof(c_allowed_chars) - 1); - -utility::string_t nonce_generator::generate() -{ - std::uniform_int_distribution<> distr(0, chars_count - 1); - utility::string_t result; - result.reserve(length()); - std::generate_n(std::back_inserter(result), length(), [&] { - return static_cast(c_allowed_chars[distr(m_random)]); - }); - return result; -} - -} // namespace utility diff --git a/deps/cpprestsdk/src/utilities/base64.cpp b/deps/cpprestsdk/src/utilities/base64.cpp deleted file mode 100644 index cff4e84fd4..0000000000 --- a/deps/cpprestsdk/src/utilities/base64.cpp +++ /dev/null @@ -1,260 +0,0 @@ -/*** - * Copyright (C) Microsoft. All rights reserved. - * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. - * - * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - * - * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk - * - * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - ****/ -#include "pch.h" - -using namespace web; -using namespace utility; - -std::vector _from_base64(const utility::string_t& str); -utility::string_t _to_base64(const unsigned char* ptr, size_t size); - -std::vector __cdecl conversions::from_base64(const utility::string_t& str) { return _from_base64(str); } - -utility::string_t __cdecl conversions::to_base64(const std::vector& input) -{ - if (input.size() == 0) - { - // return empty string - return utility::string_t(); - } - - return _to_base64(&input[0], input.size()); -} - -utility::string_t __cdecl conversions::to_base64(uint64_t input) -{ - return _to_base64(reinterpret_cast(&input), sizeof(input)); -} - -static const char* _base64_enctbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -const std::array _base64_dectbl = { - {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, - 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 254, 255, 255, 255, 0, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, - 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, - 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255}}; - -struct _triple_byte -{ - unsigned char _1_1 : 2; - unsigned char _0 : 6; - unsigned char _2_1 : 4; - unsigned char _1_2 : 4; - unsigned char _3 : 6; - unsigned char _2_2 : 2; -}; - -struct _double_byte -{ - unsigned char _1_1 : 2; - unsigned char _0 : 6; - unsigned char _2_1 : 4; - unsigned char _1_2 : 4; -}; - -struct _single_byte -{ - unsigned char _1_1 : 2; - unsigned char _0 : 6; -}; - -// -// A note on the implementation of BASE64 encoding and decoding: -// -// This is a fairly basic and naive implementation; there is probably a lot of room for -// performance improvement, as well as for adding options such as support for URI-safe base64, -// ignoring CRLF, relaxed validation rules, etc. The decoder is currently pretty strict. -// - -#ifdef __GNUC__ -// gcc is concerned about the bitfield uses in the code, something we simply need to ignore. -#pragma GCC diagnostic ignored "-Wconversion" -#endif -std::vector _from_base64(const utility::string_t& input) -{ - std::vector result; - - if (input.empty()) return result; - - size_t padding = 0; - - // Validation - { - auto size = input.size(); - - if ((size % 4) != 0) - { - throw std::runtime_error("length of base64 string is not an even multiple of 4"); - } - - for (auto iter = input.begin(); iter != input.end(); ++iter, --size) - { - const size_t ch_sz = static_cast(*iter); - if (ch_sz >= _base64_dectbl.size() || _base64_dectbl[ch_sz] == 255) - { - throw std::runtime_error("invalid character found in base64 string"); - } - if (_base64_dectbl[ch_sz] == 254) - { - padding++; - // padding only at the end - if (size > 2) - { - throw std::runtime_error("invalid padding character found in base64 string"); - } - if (size == 2) - { - const size_t ch2_sz = static_cast(*(iter + 1)); - if (ch2_sz >= _base64_dectbl.size() || _base64_dectbl[ch2_sz] != 254) - { - throw std::runtime_error("invalid padding character found in base64 string"); - } - } - } - } - } - - auto size = input.size(); - const char_t* ptr = &input[0]; - - auto outsz = (size / 4) * 3; - outsz -= padding; - - result.resize(outsz); - - size_t idx = 0; - for (; size > 4; ++idx) - { - unsigned char target[3]; - memset(target, 0, sizeof(target)); - _triple_byte* record = reinterpret_cast<_triple_byte*>(target); - - unsigned char val0 = _base64_dectbl[ptr[0]]; - unsigned char val1 = _base64_dectbl[ptr[1]]; - unsigned char val2 = _base64_dectbl[ptr[2]]; - unsigned char val3 = _base64_dectbl[ptr[3]]; - - record->_0 = val0; - record->_1_1 = val1 >> 4; - result[idx] = target[0]; - - record->_1_2 = val1 & 0xF; - record->_2_1 = val2 >> 2; - result[++idx] = target[1]; - - record->_2_2 = val2 & 0x3; - record->_3 = val3 & 0x3F; - result[++idx] = target[2]; - - ptr += 4; - size -= 4; - } - - // Handle the last four bytes separately, to avoid having the conditional statements - // in all the iterations (a performance issue). - - { - unsigned char target[3]; - memset(target, 0, sizeof(target)); - _triple_byte* record = reinterpret_cast<_triple_byte*>(target); - - unsigned char val0 = _base64_dectbl[ptr[0]]; - unsigned char val1 = _base64_dectbl[ptr[1]]; - unsigned char val2 = _base64_dectbl[ptr[2]]; - unsigned char val3 = _base64_dectbl[ptr[3]]; - - record->_0 = val0; - record->_1_1 = val1 >> 4; - result[idx] = target[0]; - - record->_1_2 = val1 & 0xF; - if (val2 != 254) - { - record->_2_1 = val2 >> 2; - result[++idx] = target[1]; - } - else - { - // There shouldn't be any information (ones) in the unused bits, - if (record->_1_2 != 0) - { - throw std::runtime_error("Invalid end of base64 string"); - } - return result; - } - - record->_2_2 = val2 & 0x3; - if (val3 != 254) - { - record->_3 = val3 & 0x3F; - result[++idx] = target[2]; - } - else - { - // There shouldn't be any information (ones) in the unused bits. - if (record->_2_2 != 0) - { - throw std::runtime_error("Invalid end of base64 string"); - } - return result; - } - } - - return result; -} - -utility::string_t _to_base64(const unsigned char* ptr, size_t size) -{ - utility::string_t result; - - for (; size >= 3;) - { - const _triple_byte* record = reinterpret_cast(ptr); - unsigned char idx0 = record->_0; - unsigned char idx1 = (record->_1_1 << 4) | record->_1_2; - unsigned char idx2 = (record->_2_1 << 2) | record->_2_2; - unsigned char idx3 = record->_3; - result.push_back(char_t(_base64_enctbl[idx0])); - result.push_back(char_t(_base64_enctbl[idx1])); - result.push_back(char_t(_base64_enctbl[idx2])); - result.push_back(char_t(_base64_enctbl[idx3])); - size -= 3; - ptr += 3; - } - switch (size) - { - case 1: - { - const _single_byte* record = reinterpret_cast(ptr); - unsigned char idx0 = record->_0; - unsigned char idx1 = (record->_1_1 << 4); - result.push_back(char_t(_base64_enctbl[idx0])); - result.push_back(char_t(_base64_enctbl[idx1])); - result.push_back('='); - result.push_back('='); - break; - } - case 2: - { - const _double_byte* record = reinterpret_cast(ptr); - unsigned char idx0 = record->_0; - unsigned char idx1 = (record->_1_1 << 4) | record->_1_2; - unsigned char idx2 = (record->_2_1 << 2); - result.push_back(char_t(_base64_enctbl[idx0])); - result.push_back(char_t(_base64_enctbl[idx1])); - result.push_back(char_t(_base64_enctbl[idx2])); - result.push_back('='); - break; - } - } - return result; -} diff --git a/deps/cpprestsdk/src/utilities/web_utilities.cpp b/deps/cpprestsdk/src/utilities/web_utilities.cpp deleted file mode 100644 index 811b390ab5..0000000000 --- a/deps/cpprestsdk/src/utilities/web_utilities.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/*** - * Copyright (C) Microsoft. All rights reserved. - * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. - * - * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - * - * Credential and proxy utilities. - * - * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk - * - * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - ****/ - -#include "pch.h" - -#include - -#if defined(_WIN32) && !defined(__cplusplus_winrt) -#include -#endif - -#if defined(__cplusplus_winrt) -#include -#endif - -namespace web -{ -namespace details -{ -#if defined(_WIN32) && !defined(CPPREST_TARGET_XP) -#if defined(__cplusplus_winrt) - -// Helper function to zero out memory of an IBuffer. -void winrt_secure_zero_buffer(Windows::Storage::Streams::IBuffer ^ buffer) -{ - Microsoft::WRL::ComPtr bufferInspectable(reinterpret_cast(buffer)); - Microsoft::WRL::ComPtr bufferByteAccess; - bufferInspectable.As(&bufferByteAccess); - - // This shouldn't happen but if can't get access to the raw bytes for some reason - // then we can't zero out. - byte* rawBytes; - if (bufferByteAccess->Buffer(&rawBytes) == S_OK) - { - SecureZeroMemory(rawBytes, buffer->Length); - } -} - -winrt_encryption::winrt_encryption(const std::wstring& data) -{ - auto provider = ref new Windows::Security::Cryptography::DataProtection::DataProtectionProvider( - ref new Platform::String(L"Local=user")); - - // Create buffer containing plain text password. - Platform::ArrayReference arrayref( - reinterpret_cast(const_cast(data.c_str())), - static_cast(data.size()) * sizeof(std::wstring::value_type)); - Windows::Storage::Streams::IBuffer ^ plaintext = - Windows::Security::Cryptography::CryptographicBuffer::CreateFromByteArray(arrayref); - m_buffer = pplx::create_task(provider->ProtectAsync(plaintext)); - m_buffer.then( - [plaintext](pplx::task) { winrt_secure_zero_buffer(plaintext); }); -} - -plaintext_string winrt_encryption::decrypt() const -{ - // To fully guarantee asynchrony would require significant impact on existing code. This code path - // is never run on a user's thread and is only done once when setting up a connection. - auto encrypted = m_buffer.get(); - auto provider = ref new Windows::Security::Cryptography::DataProtection::DataProtectionProvider(); - auto plaintext = pplx::create_task(provider->UnprotectAsync(encrypted)).get(); - - // Get access to raw bytes in plain text buffer. - Microsoft::WRL::ComPtr bufferInspectable(reinterpret_cast(plaintext)); - Microsoft::WRL::ComPtr bufferByteAccess; - bufferInspectable.As(&bufferByteAccess); - byte* rawPlaintext; - const auto& result = bufferByteAccess->Buffer(&rawPlaintext); - if (result != S_OK) - { - throw ::utility::details::create_system_error(result); - } - - // Construct string and zero out memory from plain text buffer. - auto data = plaintext_string( - new std::wstring(reinterpret_cast(rawPlaintext), plaintext->Length / 2)); - SecureZeroMemory(rawPlaintext, plaintext->Length); - return std::move(data); -} - -#else - -win32_encryption::win32_encryption(const std::wstring& data) : m_numCharacters(data.size()) -{ - // Early return because CryptProtectMemory crashes with empty string - if (m_numCharacters == 0) - { - return; - } - - if (data.size() > (std::numeric_limits::max)() / sizeof(wchar_t)) - { - throw std::length_error("Encryption string too long"); - } - - const auto dataSizeDword = static_cast(data.size() * sizeof(wchar_t)); - - // Round up dataSizeDword to be a multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE - static_assert(CRYPTPROTECTMEMORY_BLOCK_SIZE == 16, "Power of 2 assumptions in this bit masking violated"); - const auto mask = static_cast(CRYPTPROTECTMEMORY_BLOCK_SIZE - 1u); - const auto dataNumBytes = (dataSizeDword & ~mask) + ((dataSizeDword & mask) != 0) * CRYPTPROTECTMEMORY_BLOCK_SIZE; - assert((dataNumBytes % CRYPTPROTECTMEMORY_BLOCK_SIZE) == 0); - assert(dataNumBytes >= dataSizeDword); - m_buffer.resize(dataNumBytes); - memcpy_s(m_buffer.data(), m_buffer.size(), data.c_str(), dataNumBytes); - if (!CryptProtectMemory(m_buffer.data(), dataNumBytes, CRYPTPROTECTMEMORY_SAME_PROCESS)) - { - throw ::utility::details::create_system_error(GetLastError()); - } -} - -win32_encryption::~win32_encryption() { SecureZeroMemory(m_buffer.data(), m_buffer.size()); } - -plaintext_string win32_encryption::decrypt() const -{ - // Copy the buffer and decrypt to avoid having to re-encrypt. - auto result = plaintext_string(new std::wstring(reinterpret_cast(m_buffer.data()), - m_buffer.size() / sizeof(wchar_t))); - auto& data = *result; - if (!m_buffer.empty()) - { - if (!CryptUnprotectMemory(&data[0], static_cast(m_buffer.size()), CRYPTPROTECTMEMORY_SAME_PROCESS)) - { - throw ::utility::details::create_system_error(GetLastError()); - } - - assert(m_numCharacters <= m_buffer.size()); - SecureZeroMemory(&data[m_numCharacters], data.size() - m_numCharacters); - data.erase(m_numCharacters); - } - - return result; -} -#endif -#endif - -void zero_memory_deleter::operator()(::utility::string_t* data) const -{ - CASABLANCA_UNREFERENCED_PARAMETER(data); -#if defined(_WIN32) - SecureZeroMemory(&(*data)[0], data->size() * sizeof(::utility::string_t::value_type)); - delete data; -#endif -} -} // namespace details - -} // namespace web diff --git a/doc/coding/organization.md b/doc/coding/organization.md index 801269f1a2..077a3535ea 100644 --- a/doc/coding/organization.md +++ b/doc/coding/organization.md @@ -10,9 +10,6 @@ General project organization: -#### The [`deps`](/deps) folder -Contains other projects that PowerToys uses as dependencies. - #### The [`doc`](/doc) folder Documentation for the project, including a [coding guide](/doc/coding) and [design docs](/doc/specs). diff --git a/src/common/UnitTests-CommonLib/Settings.Tests.cpp b/src/common/UnitTests-CommonLib/Settings.Tests.cpp index 470806c529..8090f6b8ef 100644 --- a/src/common/UnitTests-CommonLib/Settings.Tests.cpp +++ b/src/common/UnitTests-CommonLib/Settings.Tests.cpp @@ -15,48 +15,45 @@ namespace UnitTestsCommonLib TEST_METHOD(LoadFromJsonBoolTrue) { PowerToyValues values = PowerToyValues::from_json_string(m_json); - Assert::IsTrue(values.is_bool_value(L"bool_toggle_true")); - - bool value = values.get_bool_value(L"bool_toggle_true"); - Assert::AreEqual(true, value); + auto value = values.get_bool_value(L"bool_toggle_true"); + Assert::IsTrue(value.has_value()); + Assert::AreEqual(true, *value); } TEST_METHOD(LoadFromJsonBoolFalse) { PowerToyValues values = PowerToyValues::from_json_string(m_json); - Assert::IsTrue(values.is_bool_value(L"bool_toggle_false")); - - bool value = values.get_bool_value(L"bool_toggle_false"); - Assert::AreEqual(false, value); + auto value = values.get_bool_value(L"bool_toggle_false"); + Assert::IsTrue(value.has_value()); + Assert::AreEqual(false, *value); } TEST_METHOD(LoadFromJsonInt) { PowerToyValues values = PowerToyValues::from_json_string(m_json); - Assert::IsTrue(values.is_int_value(L"int_spinner")); - - int value = values.get_int_value(L"int_spinner"); - Assert::AreEqual(10, value); + auto value = values.get_int_value(L"int_spinner"); + Assert::IsTrue(value.has_value()); + Assert::AreEqual(10, *value); } TEST_METHOD(LoadFromJsonString) { PowerToyValues values = PowerToyValues::from_json_string(m_json); - Assert::IsTrue(values.is_string_value(L"string_text")); + auto value = values.get_string_value(L"string_text"); - std::wstring value = values.get_string_value(L"string_text"); + Assert::IsTrue(value.has_value()); std::wstring expected = L"a quick fox"; - Assert::AreEqual(expected, value); + Assert::AreEqual(expected, *value); } TEST_METHOD(LoadFromJsonColorPicker) { PowerToyValues values = PowerToyValues::from_json_string(m_json); - Assert::IsTrue(values.is_string_value(L"color_picker")); + auto value = values.get_string_value(L"color_picker"); - std::wstring value = values.get_string_value(L"color_picker"); + Assert::IsTrue(value.has_value()); std::wstring expected = L"#ff8d12"; - Assert::AreEqual(expected, value); + Assert::AreEqual(expected, *value); } }; } diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj index f9a6bbc185..afe34d470a 100644 --- a/src/common/common.vcxproj +++ b/src/common/common.vcxproj @@ -98,6 +98,7 @@ + @@ -119,6 +120,7 @@ + @@ -132,11 +134,6 @@ - - - {4e577735-dfab-41af-8a6e-b6e8872a2928} - - diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters index cfb6986ac6..3b1073ec18 100644 --- a/src/common/common.vcxproj.filters +++ b/src/common/common.vcxproj.filters @@ -75,6 +75,9 @@ Header Files + + Header Files + @@ -120,5 +123,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/src/common/json.cpp b/src/common/json.cpp new file mode 100644 index 0000000000..e9d7986764 --- /dev/null +++ b/src/common/json.cpp @@ -0,0 +1,26 @@ +#include "pch.h" +#include "json.h" + +#include + +namespace json +{ + std::optional from_file(std::wstring_view file_name) + { + try + { + std::wifstream file(file_name.data(), std::ios::binary); + using isbi = std::istreambuf_iterator; + return JsonValue::Parse(std::wstring{isbi{file}, isbi{}}).GetObjectW(); + } + catch(...) + { + return std::nullopt; + } + } + + void to_file(std::wstring_view file_name, const JsonObject & obj) + { + std::wofstream{file_name.data(), std::ios::binary} << obj.Stringify().c_str(); + } +} diff --git a/src/common/json.h b/src/common/json.h new file mode 100644 index 0000000000..30292962d8 --- /dev/null +++ b/src/common/json.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include + +#include + +namespace json +{ + using namespace winrt::Windows::Data::Json; + + std::optional from_file(std::wstring_view file_name); + + void to_file(std::wstring_view file_name, const JsonObject& obj); + + inline bool has( + const json::JsonObject & o, + std::wstring_view name, + const json::JsonValueType type = JsonValueType::Object) + { + return o.HasKey(name) && o.GetNamedValue(name).ValueType() == type; + } + + template + inline std::enable_if_t, JsonValue> value(const T arithmetic) + { + return json::JsonValue::CreateNumberValue(arithmetic); + } + + template + inline std::enable_if_t, JsonValue> value(T s) + { + return json::JsonValue::CreateStringValue(s); + } + + inline JsonValue value(const bool boolean) + { + return json::JsonValue::CreateBooleanValue(boolean); + } + + inline JsonValue value(JsonObject value) + { + return value.as(); + } + + inline JsonValue value(JsonValue value) + { + return value; // identity function overload for convenience + } +} diff --git a/src/common/settings_helpers.cpp b/src/common/settings_helpers.cpp index c60a3c59e7..8d3c567796 100644 --- a/src/common/settings_helpers.cpp +++ b/src/common/settings_helpers.cpp @@ -4,12 +4,13 @@ #include namespace PTSettingsHelper { + + constexpr inline const wchar_t * settings_filename = L"\\settings.json"; + std::wstring get_root_save_folder_location() { PWSTR local_app_path; - std::wstring result(L""); - winrt::check_hresult(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &local_app_path)); - result = std::wstring(local_app_path); + std::wstring result{local_app_path}; CoTaskMemFree(local_app_path); result += L"\\Microsoft\\PowerToys"; @@ -20,7 +21,7 @@ namespace PTSettingsHelper { return result; } - std::wstring get_module_save_folder_location(const std::wstring& powertoy_name) { + std::wstring get_module_save_folder_location(std::wstring_view powertoy_name) { std::wstring result = get_root_save_folder_location(); result += L"\\"; result += powertoy_name; @@ -31,45 +32,33 @@ namespace PTSettingsHelper { return result; } - std::wstring get_module_save_file_location(const std::wstring& powertoy_name) { - std::wstring result = get_module_save_folder_location(powertoy_name); - result += L"\\settings.json"; - return result; + std::wstring get_module_save_file_location(std::wstring_view powertoy_name) { + return get_module_save_folder_location(powertoy_name) + settings_filename; } std::wstring get_powertoys_general_save_file_location() { - std::wstring result = get_root_save_folder_location(); - result += L"\\settings.json"; - return result; + return get_root_save_folder_location() + settings_filename; } - void save_module_settings(const std::wstring& powertoy_name, web::json::value& settings) { - std::wstring save_file_location = get_module_save_file_location(powertoy_name); - std::ofstream save_file(save_file_location, std::ios::binary); - settings.serialize(save_file); - save_file.close(); + void save_module_settings(std::wstring_view powertoy_name, json::JsonObject& settings) { + const std::wstring save_file_location = get_module_save_file_location(powertoy_name); + json::to_file(save_file_location, settings); } - web::json::value load_module_settings(const std::wstring& powertoy_name) { - std::wstring save_file_location = get_module_save_file_location(powertoy_name); - std::ifstream save_file(save_file_location, std::ios::binary); - web::json::value result = web::json::value::parse(save_file); - save_file.close(); - return result; + json::JsonObject load_module_settings(std::wstring_view powertoy_name) { + const std::wstring save_file_location = get_module_save_file_location(powertoy_name); + auto saved_settings = json::from_file(save_file_location); + return saved_settings.has_value() ? std::move(*saved_settings) : json::JsonObject{}; } - void save_general_settings(web::json::value& settings) { - std::wstring save_file_location = get_powertoys_general_save_file_location(); - std::ofstream save_file(save_file_location, std::ios::binary); - settings.serialize(save_file); - save_file.close(); + void save_general_settings(const json::JsonObject& settings) { + const std::wstring save_file_location = get_powertoys_general_save_file_location(); + json::to_file(save_file_location, settings); } - web::json::value load_general_settings() { - std::wstring save_file_location = get_powertoys_general_save_file_location(); - std::ifstream save_file(save_file_location, std::ios::binary); - web::json::value result = web::json::value::parse(save_file); - save_file.close(); - return result; + json::JsonObject load_general_settings() { + const std::wstring save_file_location = get_powertoys_general_save_file_location(); + auto saved_settings = json::from_file(save_file_location); + return saved_settings.has_value() ? std::move(*saved_settings) : json::JsonObject{}; } } diff --git a/src/common/settings_helpers.h b/src/common/settings_helpers.h index 3ce1c6e7d6..6143bc2e5c 100644 --- a/src/common/settings_helpers.h +++ b/src/common/settings_helpers.h @@ -1,13 +1,14 @@ #pragma once #include #include -#include + +#include "json.h" namespace PTSettingsHelper { - void save_module_settings(const std::wstring& powertoy_name, web::json::value& settings); - web::json::value load_module_settings(const std::wstring& powertoy_name); - void save_general_settings(web::json::value& settings); - web::json::value load_general_settings(); + void save_module_settings(std::wstring_view powertoy_name, json::JsonObject& settings); + json::JsonObject load_module_settings(std::wstring_view powertoy_name); + void save_general_settings(const json::JsonObject& settings); + json::JsonObject load_general_settings(); } diff --git a/src/common/settings_objects.cpp b/src/common/settings_objects.cpp index 647cd20f02..b2fe76a084 100644 --- a/src/common/settings_objects.cpp +++ b/src/common/settings_objects.cpp @@ -4,128 +4,127 @@ namespace PowerToysSettings { - Settings::Settings(const HINSTANCE hinstance, const std::wstring& powertoy_name) { + Settings::Settings(const HINSTANCE hinstance, std::wstring_view powertoy_name) { m_instance = hinstance; - m_json = web::json::value::object(); - m_json.as_object()[L"version"] = web::json::value::string(L"1.0"); - m_json.as_object()[L"name"] = web::json::value::string(powertoy_name); - m_json.as_object()[L"properties"] = web::json::value::object(); + m_json.SetNamedValue(L"version", json::value(L"1.0")); + m_json.SetNamedValue(L"name", json::value(powertoy_name)); + m_json.SetNamedValue(L"properties", json::JsonObject{}); } void Settings::set_description(UINT resource_id) { - m_json.as_object()[L"description"] = web::json::value::string(get_resource(resource_id)); + m_json.SetNamedValue(L"description", json::value(get_resource(resource_id))); } - void Settings::set_description(const std::wstring& description) { - m_json.as_object()[L"description"] = web::json::value::string(description); + void Settings::set_description(std::wstring_view description) { + m_json.SetNamedValue(L"description", json::value(description)); } - void Settings::set_icon_key(const std::wstring& icon_key) { - m_json.as_object()[L"icon_key"] = web::json::value::string(icon_key); + void Settings::set_icon_key(std::wstring_view icon_key) { + m_json.SetNamedValue(L"icon_key", json::value(icon_key)); } - void Settings::set_overview_link(const std::wstring& overview_link) { - m_json.as_object()[L"overview_link"] = web::json::value::string(overview_link); + void Settings::set_overview_link(std::wstring_view overview_link) { + m_json.SetNamedValue(L"overview_link", json::value(overview_link)); } - void Settings::set_video_link(const std::wstring& video_link) { - m_json.as_object()[L"video_link"] = web::json::value::string(video_link); + void Settings::set_video_link(std::wstring_view video_link) { + m_json.SetNamedValue(L"video_link", json::value(video_link)); } // add_bool_toogle overloads. - void Settings::add_bool_toogle(const std::wstring& name, UINT description_resource_id, bool value) { + void Settings::add_bool_toogle(std::wstring_view name, UINT description_resource_id, bool value) { add_bool_toogle(name, get_resource(description_resource_id), value); } - void Settings::add_bool_toogle(const std::wstring& name, const std::wstring& description, bool value) { - web::json::value item = web::json::value::object(); - item.as_object()[L"display_name"] = web::json::value::string(description); - item.as_object()[L"editor_type"] = web::json::value::string(L"bool_toggle"); - item.as_object()[L"value"] = web::json::value::boolean(value); - item.as_object()[L"order"] = web::json::value::number(++m_curr_priority); + void Settings::add_bool_toogle(std::wstring_view name, std::wstring_view description, bool value) { + json::JsonObject toggle; + toggle.SetNamedValue(L"display_name", json::value(description)); + toggle.SetNamedValue(L"editor_type", json::value(L"bool_toggle")); + toggle.SetNamedValue(L"value", json::value(value)); + toggle.SetNamedValue(L"order", json::value(++m_curr_priority)); - m_json.as_object()[L"properties"].as_object()[name] = item; + m_json.GetNamedObject(L"properties").SetNamedValue(name, toggle); } // add_int_spinner overloads. - void Settings::add_int_spinner(const std::wstring& name, UINT description_resource_id, int value, int min, int max, int step) { + void Settings::add_int_spinner(std::wstring_view name, UINT description_resource_id, int value, int min, int max, int step) { add_int_spinner(name, get_resource(description_resource_id), value, min, max, step); } - void Settings::add_int_spinner(const std::wstring& name, const std::wstring& description, int value, int min, int max, int step) { - web::json::value item = web::json::value::object(); - item.as_object()[L"display_name"] = web::json::value::string(description); - item.as_object()[L"editor_type"] = web::json::value::string(L"int_spinner"); - item.as_object()[L"value"] = web::json::value::number(value); - item.as_object()[L"min"] = web::json::value::number(min); - item.as_object()[L"max"] = web::json::value::number(max); - item.as_object()[L"step"] = web::json::value::number(step); - item.as_object()[L"order"] = web::json::value::number(++m_curr_priority); + void Settings::add_int_spinner(std::wstring_view name, std::wstring_view description, int value, int min, int max, int step) { + json::JsonObject spinner; + spinner.SetNamedValue(L"display_name", json::value(description)); + spinner.SetNamedValue(L"editor_type", json::value(L"int_spinner")); + spinner.SetNamedValue(L"value", json::value(value)); + spinner.SetNamedValue(L"min", json::value(min)); + spinner.SetNamedValue(L"max", json::value(max)); + spinner.SetNamedValue(L"step", json::value(step)); + spinner.SetNamedValue(L"order", json::value(++m_curr_priority)); - m_json.as_object()[L"properties"].as_object()[name] = item; + m_json.GetNamedObject(L"properties").SetNamedValue(name, spinner); } // add_string overloads. - void Settings::add_string(const std::wstring& name, UINT description_resource_id, const std::wstring& value) { + void Settings::add_string(std::wstring_view name, UINT description_resource_id, std::wstring_view value) { add_string(name, get_resource(description_resource_id), value); } - void Settings::add_string(const std::wstring& name, const std::wstring& description, const std::wstring& value) { - web::json::value item = web::json::value::object(); - item.as_object()[L"display_name"] = web::json::value::string(description); - item.as_object()[L"editor_type"] = web::json::value::string(L"string_text"); - item.as_object()[L"value"] = web::json::value::string(value); - item.as_object()[L"order"] = web::json::value::number(++m_curr_priority); + void Settings::add_string(std::wstring_view name, std::wstring_view description, std::wstring_view value) { + json::JsonObject string; + string.SetNamedValue(L"display_name", json::value(description)); + string.SetNamedValue(L"editor_type", json::value(L"string_text")); + string.SetNamedValue(L"value", json::value(value)); + string.SetNamedValue(L"order", json::value(++m_curr_priority)); - m_json.as_object()[L"properties"].as_object()[name] = item; + m_json.GetNamedObject(L"properties").SetNamedValue(name, string); } // add_multiline_string overloads. - void Settings::add_multiline_string(const std::wstring& name, UINT description_resource_id, const std::wstring& value) { + void Settings::add_multiline_string(std::wstring_view name, UINT description_resource_id, std::wstring_view value) { add_multiline_string(name, get_resource(description_resource_id), value); } - void Settings::add_multiline_string(const std::wstring& name, const std::wstring& description, const std::wstring& value) { - web::json::value item = web::json::value::object(); - item.as_object()[L"display_name"] = web::json::value::string(description); - item.as_object()[L"editor_type"] = web::json::value::string(L"string_text"); - item.as_object()[L"value"] = web::json::value::string(value); - item.as_object()[L"order"] = web::json::value::number(++m_curr_priority); - item.as_object()[L"multiline"] = web::json::value::boolean(true); + void Settings::add_multiline_string(std::wstring_view name, std::wstring_view description, std::wstring_view value) { + json::JsonObject ml_string; + ml_string.SetNamedValue(L"display_name", json::value(description)); + ml_string.SetNamedValue(L"editor_type", json::value(L"string_text")); + ml_string.SetNamedValue(L"value", json::value(value)); + ml_string.SetNamedValue(L"order", json::value(++m_curr_priority)); + ml_string.SetNamedValue(L"multiline", json::value(true)); - m_json.as_object()[L"properties"].as_object()[name] = item; + m_json.GetNamedObject(L"properties").SetNamedValue(name, ml_string); } // add_color_picker overloads. - void Settings::add_color_picker(const std::wstring& name, UINT description_resource_id, const std::wstring& value) { + void Settings::add_color_picker(std::wstring_view name, UINT description_resource_id, std::wstring_view value) { add_color_picker(name, get_resource(description_resource_id), value); } - void Settings::add_color_picker(const std::wstring& name, const std::wstring& description, const std::wstring& value) { - web::json::value item = web::json::value::object(); - item.as_object()[L"display_name"] = web::json::value::string(description); - item.as_object()[L"editor_type"] = web::json::value::string(L"color_picker"); - item.as_object()[L"value"] = web::json::value::string(value); - item.as_object()[L"order"] = web::json::value::number(++m_curr_priority); + void Settings::add_color_picker(std::wstring_view name, std::wstring_view description, std::wstring_view value) { + json::JsonObject picker; + picker.SetNamedValue(L"display_name", json::value(description)); + picker.SetNamedValue(L"editor_type", json::value(L"color_picker")); + picker.SetNamedValue(L"value", json::value(value)); + picker.SetNamedValue(L"order", json::value(++m_curr_priority)); - m_json.as_object()[L"properties"].as_object()[name] = item; + m_json.GetNamedObject(L"properties").SetNamedValue(name, picker); } - void Settings::add_hotkey(const std::wstring& name, UINT description_resource_id, const HotkeyObject& hotkey) { + void Settings::add_hotkey(std::wstring_view name, UINT description_resource_id, const HotkeyObject& hotkey) { add_hotkey(name, get_resource(description_resource_id), hotkey); } - void Settings::add_hotkey(const std::wstring& name, const std::wstring& description, const HotkeyObject& hotkey) { - web::json::value item = web::json::value::object(); - item.as_object()[L"display_name"] = web::json::value::string(description); - item.as_object()[L"editor_type"] = web::json::value::string(L"hotkey"); - item.as_object()[L"value"] = hotkey.get_json(); - item.as_object()[L"order"] = web::json::value::number(++m_curr_priority); + void Settings::add_hotkey(std::wstring_view name, std::wstring_view description, const HotkeyObject& hotkey_obj) { + json::JsonObject hotkey; + hotkey.SetNamedValue(L"display_name", json::value(description)); + hotkey.SetNamedValue(L"editor_type", json::value(L"hotkey")); + hotkey.SetNamedValue(L"value", hotkey_obj.get_json()); + hotkey.SetNamedValue(L"order", json::value(++m_curr_priority)); - m_json.as_object()[L"properties"].as_object()[name] = item; + m_json.GetNamedObject(L"properties").SetNamedValue(name, hotkey); } - void Settings::add_choice_group(const std::wstring& name, UINT description_resource_id, const std::wstring& value, const std::vector>& keys_and_text_ids) { + void Settings::add_choice_group(std::wstring_view name, UINT description_resource_id, std::wstring_view value, const std::vector>& keys_and_text_ids) { std::vector> keys_and_texts; keys_and_texts.reserve(keys_and_text_ids.size()); for (const auto& kv : keys_and_text_ids) { @@ -134,25 +133,25 @@ namespace PowerToysSettings { add_choice_group(name, get_resource(description_resource_id), value, keys_and_texts); } - void Settings::add_choice_group(const std::wstring& name, const std::wstring& description, const std::wstring& value, const std::vector>& keys_and_texts) { - web::json::value item = web::json::value::object(); - item.as_object()[L"display_name"] = web::json::value::string(description); - item.as_object()[L"editor_type"] = web::json::value::string(L"choice_group"); - auto options = web::json::value::array(keys_and_texts.size()); - for (std::size_t i = 0; i < keys_and_texts.size(); ++i) { - auto entry = web::json::value::object(); - entry.as_object()[L"key"] = web::json::value::string(keys_and_texts[i].first); - entry.as_object()[L"text"] = web::json::value::string(keys_and_texts[i].second); - options.as_array()[i] = entry; + void Settings::add_choice_group(std::wstring_view name, std::wstring_view description, std::wstring_view value, const std::vector>& keys_and_texts) { + json::JsonObject choice_group; + choice_group.SetNamedValue(L"display_name", json::value(description)); + choice_group.SetNamedValue(L"editor_type", json::value(L"choice_group")); + json::JsonArray options; + for(const auto & [key, text] : keys_and_texts) { + json::JsonObject entry; + entry.SetNamedValue(L"key", json::value(key)); + entry.SetNamedValue(L"text", json::value(text)); + options.Append(std::move(entry)); } - item.as_object()[L"options"] = options; - item.as_object()[L"value"] = web::json::value::string(value); - item.as_object()[L"order"] = web::json::value::number(++m_curr_priority); + choice_group.SetNamedValue(L"options", std::move(options)); + choice_group.SetNamedValue(L"value", json::value(value)); + choice_group.SetNamedValue(L"order", json::value(++m_curr_priority)); - m_json.as_object()[L"properties"].as_object()[name] = item; + m_json.GetNamedObject(L"properties").SetNamedValue(name, choice_group); } - void Settings::add_dropdown(const std::wstring& name, UINT description_resource_id, const std::wstring& value, const std::vector>& keys_and_text_ids) { + void Settings::add_dropdown(std::wstring_view name, UINT description_resource_id, std::wstring_view value, const std::vector>& keys_and_text_ids) { std::vector> keys_and_texts; keys_and_texts.reserve(keys_and_text_ids.size()); for (const auto& kv : keys_and_text_ids) { @@ -161,55 +160,55 @@ namespace PowerToysSettings { add_dropdown(name, get_resource(description_resource_id), value, keys_and_texts); } - void Settings::add_dropdown(const std::wstring& name, const std::wstring& description, const std::wstring& value, const std::vector>& keys_and_texts) { - web::json::value item = web::json::value::object(); - item.as_object()[L"display_name"] = web::json::value::string(description); - item.as_object()[L"editor_type"] = web::json::value::string(L"dropdown"); - auto options = web::json::value::array(keys_and_texts.size()); - for (std::size_t i = 0; i < keys_and_texts.size(); ++i) { - auto entry = web::json::value::object(); - entry.as_object()[L"key"] = web::json::value::string(keys_and_texts[i].first); - entry.as_object()[L"text"] = web::json::value::string(keys_and_texts[i].second); - options.as_array()[i] = entry; + void Settings::add_dropdown(std::wstring_view name, std::wstring_view description, std::wstring_view value, const std::vector>& keys_and_texts) { + json::JsonObject dropdown; + dropdown.SetNamedValue(L"display_name", json::value(description)); + dropdown.SetNamedValue(L"editor_type", json::value(L"dropdown")); + json::JsonArray options; + for(const auto & [key, text] : keys_and_texts) { + json::JsonObject entry; + entry.SetNamedValue(L"key", json::value(key)); + entry.SetNamedValue(L"text", json::value(text)); + options.Append(std::move(entry)); } - item.as_object()[L"options"] = options; - item.as_object()[L"value"] = web::json::value::string(value); - item.as_object()[L"order"] = web::json::value::number(++m_curr_priority); + dropdown.SetNamedValue(L"options", std::move(options)); + dropdown.SetNamedValue(L"value", json::value(value)); + dropdown.SetNamedValue(L"order", json::value(++m_curr_priority)); - m_json.as_object()[L"properties"].as_object()[name] = item; + m_json.GetNamedObject(L"properties").SetNamedValue(name, dropdown); } // add_custom_action overloads. - void Settings::add_custom_action(const std::wstring& name, UINT description_resource_id, UINT button_text_resource_id, UINT ext_description_resource_id) { + void Settings::add_custom_action(std::wstring_view name, UINT description_resource_id, UINT button_text_resource_id, UINT ext_description_resource_id) { add_custom_action(name, get_resource(description_resource_id), get_resource(button_text_resource_id), get_resource(ext_description_resource_id)); } - void Settings::add_custom_action(const std::wstring& name, UINT description_resource_id, UINT button_text_resource_id, const std::wstring& value) { + void Settings::add_custom_action(std::wstring_view name, UINT description_resource_id, UINT button_text_resource_id, std::wstring_view value) { add_custom_action(name, get_resource(description_resource_id), get_resource(button_text_resource_id), value); } - void Settings::add_custom_action(const std::wstring& name, const std::wstring& description, const std::wstring& button_text, const std::wstring& value) { - web::json::value item = web::json::value::object(); - item.as_object()[L"display_name"] = web::json::value::string(description); - item.as_object()[L"button_text"] = web::json::value::string(button_text); - item.as_object()[L"editor_type"] = web::json::value::string(L"custom_action"); - item.as_object()[L"value"] = web::json::value::string(value); - item.as_object()[L"order"] = web::json::value::number(++m_curr_priority); + void Settings::add_custom_action(std::wstring_view name, std::wstring_view description, std::wstring_view button_text, std::wstring_view value) { + json::JsonObject custom_action; + custom_action.SetNamedValue(L"display_name", json::value(description)); + custom_action.SetNamedValue(L"button_text", json::value(button_text)); + custom_action.SetNamedValue(L"editor_type", json::value(L"custom_action")); + custom_action.SetNamedValue(L"value", json::value(value)); + custom_action.SetNamedValue(L"order", json::value(++m_curr_priority)); - m_json.as_object()[L"properties"].as_object()[name] = item; + m_json.GetNamedObject(L"properties").SetNamedValue(name, custom_action); } // Serialization methods. std::wstring Settings::serialize() { - return m_json.serialize(); + return m_json.Stringify().c_str(); } bool Settings::serialize_to_buffer(wchar_t* buffer, int *buffer_size) { - std::wstring result = m_json.serialize(); - int result_len = (int)result.length(); + auto result = m_json.Stringify(); + const int result_len = (int)result.size() + 1; if (buffer == nullptr || *buffer_size < result_len) { - *buffer_size = result_len + 1; + *buffer_size = result_len; return false; } else { wcscpy_s(buffer, *buffer_size, result.c_str()); @@ -220,110 +219,73 @@ namespace PowerToysSettings { // Resource helper. std::wstring Settings::get_resource(UINT resource_id) { if (resource_id != 0) { - wchar_t buffer[512]; - if (LoadString(m_instance, resource_id, buffer, ARRAYSIZE(buffer)) > 0) { - return std::wstring(buffer); + wchar_t* res_ptr; + const size_t resource_length = LoadStringW(m_instance, resource_id, reinterpret_cast(&res_ptr), 0); + if (resource_length != 0) { + return {*reinterpret_cast(&res_ptr), resource_length}; } } return L"RESOURCE ID NOT FOUND: " + std::to_wstring(resource_id); } - PowerToyValues::PowerToyValues(const std::wstring& powertoy_name) { + PowerToyValues::PowerToyValues(std::wstring_view powertoy_name) { _name = powertoy_name; - m_json = web::json::value::object(); set_version(); - m_json.as_object()[L"name"] = web::json::value::string(powertoy_name); - m_json.as_object()[L"properties"] = web::json::value::object(); + m_json.SetNamedValue(L"name", json::value(powertoy_name)); + m_json.SetNamedValue(L"properties", json::JsonObject{}); } - PowerToyValues PowerToyValues::from_json_string(const std::wstring& json) { + PowerToyValues PowerToyValues::from_json_string(std::wstring_view json) { PowerToyValues result = PowerToyValues(); - result.m_json = web::json::value::parse(json); - result._name = result.m_json.as_object()[L"name"].as_string(); + result.m_json = json::JsonValue::Parse(json).GetObjectW(); + result._name = result.m_json.GetNamedString(L"name"); return result; } - PowerToyValues PowerToyValues::load_from_settings_file(const std::wstring & powertoy_name) { + PowerToyValues PowerToyValues::load_from_settings_file(std::wstring_view powertoy_name) { PowerToyValues result = PowerToyValues(); result.m_json = PTSettingsHelper::load_module_settings(powertoy_name); result._name = powertoy_name; return result; } - template - web::json::value add_property_generic(const std::wstring& name, T value) { - std::vector> vector = { std::make_pair(L"value", web::json::value(value)) }; - return web::json::value::object(vector); + inline bool has_property(const json::JsonObject& o, std::wstring_view name, const json::JsonValueType type) { + const json::JsonObject props = o.GetNamedObject(L"properties", json::JsonObject{}); + return json::has(props, name) && json::has(props.GetNamedObject(name), L"value", type); } - template <> - void PowerToyValues::add_property(const std::wstring& name, bool value) { - m_json.as_object()[L"properties"].as_object()[name] = add_property_generic(name, value); - }; - - template <> - void PowerToyValues::add_property(const std::wstring& name, int value) { - m_json.as_object()[L"properties"].as_object()[name] = add_property_generic(name, value); - }; - - template <> - void PowerToyValues::add_property(const std::wstring& name, std::wstring value) { - m_json.as_object()[L"properties"].as_object()[name] = add_property_generic(name, value); - }; - - template <> - void PowerToyValues::add_property(const std::wstring& name, HotkeyObject value) { - m_json.as_object()[L"properties"].as_object()[name] = add_property_generic(name, value.get_json()); - }; - - bool PowerToyValues::is_bool_value(const std::wstring& property_name) { - return m_json.is_object() && - m_json.has_object_field(L"properties") && - m_json[L"properties"].has_object_field(property_name) && - m_json[L"properties"][property_name].has_boolean_field(L"value"); + std::optional PowerToyValues::get_bool_value(std::wstring_view property_name) { + if (!has_property(m_json, property_name, json::JsonValueType::Boolean)) { + return std::nullopt; + } + return m_json.GetNamedObject(L"properties").GetNamedObject(property_name).GetNamedBoolean(L"value"); } - bool PowerToyValues::is_int_value(const std::wstring& property_name) { - return m_json.is_object() && - m_json.has_object_field(L"properties") && - m_json[L"properties"].has_object_field(property_name) && - m_json[L"properties"][property_name].has_integer_field(L"value"); + std::optional PowerToyValues::get_int_value(std::wstring_view property_name) { + if (!has_property(m_json, property_name, json::JsonValueType::Number)) { + return std::nullopt; + } + return static_cast(m_json.GetNamedObject(L"properties").GetNamedObject(property_name).GetNamedNumber(L"value")); } - bool PowerToyValues::is_string_value(const std::wstring& property_name) { - return m_json.is_object() && - m_json.has_object_field(L"properties") && - m_json[L"properties"].has_object_field(property_name) && - m_json[L"properties"][property_name].has_string_field(L"value"); + std::optional PowerToyValues::get_string_value(std::wstring_view property_name) { + if (!has_property(m_json, property_name, json::JsonValueType::String)) { + return std::nullopt; + } + return m_json.GetNamedObject(L"properties").GetNamedObject(property_name).GetNamedString(L"value").c_str(); } - bool PowerToyValues::is_object_value(const std::wstring& property_name) { - return m_json.is_object() && - m_json.has_object_field(L"properties") && - m_json[L"properties"].has_object_field(property_name) && - m_json[L"properties"][property_name].has_object_field(L"value"); - } - - bool PowerToyValues::get_bool_value(const std::wstring& property_name) { - return m_json[L"properties"][property_name][L"value"].as_bool(); - } - - int PowerToyValues::get_int_value(const std::wstring& property_name) { - return m_json[L"properties"][property_name][L"value"].as_integer(); - } - - std::wstring PowerToyValues::get_string_value(const std::wstring& property_name) { - return m_json[L"properties"][property_name][L"value"].as_string(); - } - - web::json::value PowerToyValues::get_json(const std::wstring& property_name) { - return m_json[L"properties"][property_name][L"value"]; + std::optional PowerToyValues::get_json(std::wstring_view property_name) { + if (!has_property(m_json, property_name, json::JsonValueType::Object)) { + return std::nullopt; + } + return m_json.GetNamedObject(L"properties").GetNamedObject(property_name).GetNamedObject(L"value"); } std::wstring PowerToyValues::serialize() { set_version(); - return m_json.serialize(); + return m_json.Stringify().c_str(); } void PowerToyValues::save_to_settings_file() { @@ -332,6 +294,6 @@ namespace PowerToysSettings { } void PowerToyValues::set_version() { - m_json.as_object()[L"version"] = web::json::value::string(m_version); + m_json.SetNamedValue(L"version", json::value(m_version)); } } \ No newline at end of file diff --git a/src/common/settings_objects.h b/src/common/settings_objects.h index 024f988432..a6b1e17795 100644 --- a/src/common/settings_objects.h +++ b/src/common/settings_objects.h @@ -1,6 +1,6 @@ #pragma once -#include -#include + +#include "json.h" namespace PowerToysSettings { @@ -10,45 +10,45 @@ namespace PowerToysSettings { public: Settings( const HINSTANCE hinstance, // Module handle of the PowerToy DLL 'IMAGE_DOS_HEADER __ImageBase' - const std::wstring& powertoy_name + std::wstring_view powertoy_name ); // Add additional general information to the PowerToy settings. void set_description(UINT resource_id); - void set_description(const std::wstring& description); + void set_description(std::wstring_view description); - void set_icon_key(const std::wstring& icon_key); - void set_overview_link(const std::wstring& overview_link); - void set_video_link(const std::wstring& video_link); + void set_icon_key(std::wstring_view icon_key); + void set_overview_link(std::wstring_view overview_link); + void set_video_link(std::wstring_view video_link); // Add properties to the PowerToy settings. - void add_bool_toogle(const std::wstring& name, UINT description_resource_id, bool value); - void add_bool_toogle(const std::wstring& name, const std::wstring& description, bool value); + void add_bool_toogle(std::wstring_view name, UINT description_resource_id, bool value); + void add_bool_toogle(std::wstring_view name, std::wstring_view description, bool value); - void add_int_spinner(const std::wstring& name, UINT description_resource_id, int value, int min, int max, int step); - void add_int_spinner(const std::wstring& name, const std::wstring& description, int value, int min, int max, int step); + void add_int_spinner(std::wstring_view name, UINT description_resource_id, int value, int min, int max, int step); + void add_int_spinner(std::wstring_view name, std::wstring_view description, int value, int min, int max, int step); - void add_string(const std::wstring& name, UINT description_resource_id, const std::wstring& value); - void add_string(const std::wstring& name, const std::wstring& description, const std::wstring& value); + void add_string(std::wstring_view name, UINT description_resource_id, std::wstring_view value); + void add_string(std::wstring_view name, std::wstring_view description, std::wstring_view value); - void add_multiline_string(const std::wstring& name, UINT description_resource_id, const std::wstring& value); - void add_multiline_string(const std::wstring& name, const std::wstring& description, const std::wstring& value); + void add_multiline_string(std::wstring_view name, UINT description_resource_id, std::wstring_view value); + void add_multiline_string(std::wstring_view name, std::wstring_view description, std::wstring_view value); - void add_color_picker(const std::wstring& name, UINT description_resource_id, const std::wstring& value); - void add_color_picker(const std::wstring& name, const std::wstring& description, const std::wstring& value); + void add_color_picker(std::wstring_view name, UINT description_resource_id, std::wstring_view value); + void add_color_picker(std::wstring_view name, std::wstring_view description, std::wstring_view value); - void add_hotkey(const std::wstring& name, UINT description_resource_id, const HotkeyObject& hotkey); - void add_hotkey(const std::wstring& name, const std::wstring& description, const HotkeyObject& hotkey); + void add_hotkey(std::wstring_view name, UINT description_resource_id, const HotkeyObject& hotkey); + void add_hotkey(std::wstring_view name, std::wstring_view description, const HotkeyObject& hotkey); - void add_choice_group(const std::wstring& name, UINT description_resource_id, const std::wstring& value, const std::vector>& keys_and_text_ids); - void add_choice_group(const std::wstring& name, const std::wstring& description, const std::wstring& value, const std::vector>& keys_and_texts); + void add_choice_group(std::wstring_view name, UINT description_resource_id, std::wstring_view value, const std::vector>& keys_and_text_ids); + void add_choice_group(std::wstring_view name, std::wstring_view description, std::wstring_view value, const std::vector>& keys_and_texts); - void add_dropdown(const std::wstring& name, UINT description_resource_id, const std::wstring& value, const std::vector>& keys_and_text_ids); - void add_dropdown(const std::wstring& name, const std::wstring& description, const std::wstring& value, const std::vector>& keys_and_texts); + void add_dropdown(std::wstring_view name, UINT description_resource_id, std::wstring_view value, const std::vector>& keys_and_text_ids); + void add_dropdown(std::wstring_view name, std::wstring_view description, std::wstring_view value, const std::vector>& keys_and_texts); - void add_custom_action(const std::wstring& name, UINT description_resource_id, UINT button_text_resource_id, UINT ext_description_resource_id); - void add_custom_action(const std::wstring& name, UINT description_resource_id, UINT button_text_resource_id, const std::wstring& value); - void add_custom_action(const std::wstring& name, const std::wstring& description, const std::wstring& button_text, const std::wstring& value); + void add_custom_action(std::wstring_view name, UINT description_resource_id, UINT button_text_resource_id, UINT ext_description_resource_id); + void add_custom_action(std::wstring_view name, UINT description_resource_id, UINT button_text_resource_id, std::wstring_view value); + void add_custom_action(std::wstring_view name, std::wstring_view description, std::wstring_view button_text, std::wstring_view value); // Serialize the internal json to a string. @@ -57,7 +57,7 @@ namespace PowerToysSettings { bool serialize_to_buffer(wchar_t* buffer, int* buffer_size); private: - web::json::value m_json; + json::JsonObject m_json; int m_curr_priority = 0; // For keeping order when adding elements. HINSTANCE m_instance; @@ -66,24 +66,22 @@ namespace PowerToysSettings { class PowerToyValues { public: - PowerToyValues(const std::wstring& powertoy_name); - static PowerToyValues from_json_string(const std::wstring& json); - static PowerToyValues load_from_settings_file(const std::wstring& powertoy_name); + PowerToyValues(std::wstring_view powertoy_name); + static PowerToyValues from_json_string(std::wstring_view json); + static PowerToyValues load_from_settings_file(std::wstring_view powertoy_name); template - void add_property(const std::wstring& name, T value); + inline void add_property(std::wstring_view name, T value) + { + json::JsonObject prop_value; + prop_value.SetNamedValue(L"value", json::value(value)); + m_json.GetNamedObject(L"properties").SetNamedValue(name, prop_value); + } - // Check property value type - bool is_bool_value(const std::wstring& property_name); - bool is_int_value(const std::wstring& property_name); - bool is_string_value(const std::wstring& property_name); - bool is_object_value(const std::wstring& property_name); - - // Get property value - bool get_bool_value(const std::wstring& property_name); - int get_int_value(const std::wstring& property_name); - std::wstring get_string_value(const std::wstring& property_name); - web::json::value get_json(const std::wstring& property_name); + std::optional get_bool_value(std::wstring_view property_name); + std::optional get_int_value(std::wstring_view property_name); + std::optional get_string_value(std::wstring_view property_name); + std::optional get_json(std::wstring_view property_name); std::wstring serialize(); void save_to_settings_file(); @@ -91,60 +89,58 @@ namespace PowerToysSettings { private: const std::wstring m_version = L"1.0"; void set_version(); - web::json::value m_json; + json::JsonObject m_json; std::wstring _name; PowerToyValues() {} }; class CustomActionObject { public: - static CustomActionObject from_json_string(const std::wstring& json) { - web::json::value parsed_json = web::json::value::parse(json); - return CustomActionObject(parsed_json); + static CustomActionObject from_json_string(std::wstring_view json) { + return CustomActionObject(json::JsonValue::Parse(json).GetObjectW()); } - std::wstring get_name() { return m_json[L"action_name"].as_string(); } - std::wstring get_value() { return m_json[L"value"].as_string(); } + std::wstring get_name() { return m_json.GetNamedString(L"action_name").c_str(); } + std::wstring get_value() { return m_json.GetNamedString(L"value").c_str(); } protected: - CustomActionObject(web::json::value action_json) : m_json(action_json) {}; - web::json::value m_json; + CustomActionObject(json::JsonObject action_json) : m_json(std::move(action_json)) {}; + json::JsonObject m_json; }; class HotkeyObject { public: - static HotkeyObject from_json(web::json::value json) { - return HotkeyObject(json); + static HotkeyObject from_json(json::JsonObject json) { + return HotkeyObject(std::move(json)); } - static HotkeyObject from_json_string(const std::wstring& json) { - web::json::value parsed_json = web::json::value::parse(json); - return HotkeyObject(parsed_json); + static HotkeyObject from_json_string(std::wstring_view json) { + return HotkeyObject(json::JsonValue::Parse(json).GetObjectW()); } static HotkeyObject from_settings(bool win_pressed, bool ctrl_pressed, bool alt_pressed, bool shift_pressed, UINT vk_code) { - web::json::value json = web::json::value::object(); - json.as_object()[L"win"] = web::json::value::boolean(win_pressed); - json.as_object()[L"ctrl"] = web::json::value::boolean(ctrl_pressed); - json.as_object()[L"alt"] = web::json::value::boolean(alt_pressed); - json.as_object()[L"shift"] = web::json::value::boolean(shift_pressed); - json.as_object()[L"code"] = web::json::value::number(vk_code); - json.as_object()[L"key"] = web::json::value::string(key_from_code(vk_code)); - return HotkeyObject(json); + json::JsonObject json; + json.SetNamedValue(L"win", json::value(win_pressed)); + json.SetNamedValue(L"ctrl", json::value(ctrl_pressed)); + json.SetNamedValue(L"alt", json::value(alt_pressed)); + json.SetNamedValue(L"shift", json::value(shift_pressed)); + json.SetNamedValue(L"code", json::value(vk_code)); + json.SetNamedValue(L"key", json::value(key_from_code(vk_code))); + return std::move(json); } - const web::json::value& get_json() const { return m_json; } + const json::JsonObject& get_json() const { return m_json; } - std::wstring get_key() { return m_json[L"key"].as_string(); } - UINT get_code() { return m_json[L"code"].as_integer(); } - bool win_pressed() { return m_json[L"win"].as_bool(); } - bool ctrl_pressed() { return m_json[L"ctrl"].as_bool(); } - bool alt_pressed() { return m_json[L"alt"].as_bool(); } - bool shift_pressed() { return m_json[L"shift"].as_bool(); } - UINT get_modifiers_repeat() { + std::wstring get_key() const { return m_json.GetNamedString(L"key").c_str(); } + UINT get_code() const { return static_cast(m_json.GetNamedNumber(L"code")); } + bool win_pressed() const { return m_json.GetNamedBoolean(L"win"); } + bool ctrl_pressed() const { return m_json.GetNamedBoolean(L"ctrl"); } + bool alt_pressed() const { return m_json.GetNamedBoolean(L"alt"); } + bool shift_pressed() const { return m_json.GetNamedBoolean(L"shift"); } + UINT get_modifiers_repeat() const { return (win_pressed() ? MOD_WIN : 0) | (ctrl_pressed() ? MOD_CONTROL : 0) | (alt_pressed() ? MOD_ALT : 0) | (shift_pressed() ? MOD_SHIFT : 0); } - UINT get_modifiers() { + UINT get_modifiers() const { return get_modifiers_repeat() | MOD_NOREPEAT; } protected: @@ -178,12 +174,12 @@ namespace PowerToysSettings { } return L"(Key " + std::to_wstring(key_code) + L")"; } - HotkeyObject(web::json::value hotkey_json) : m_json(hotkey_json) { + HotkeyObject(json::JsonObject hotkey_json) : m_json(std::move(hotkey_json)) { if (get_key() == L"~" && get_modifiers_repeat() == MOD_WIN) { - m_json.as_object()[L"key"] = web::json::value::string(key_from_code(get_code())); + m_json.SetNamedValue(L"key", json::value(key_from_code(get_code()))); } }; - web::json::value m_json; + json::JsonObject m_json; }; } diff --git a/src/modules/fancyzones/lib/Settings.cpp b/src/modules/fancyzones/lib/Settings.cpp index 10ba0c40fa..0600cbe14f 100644 --- a/src/modules/fancyzones/lib/Settings.cpp +++ b/src/modules/fancyzones/lib/Settings.cpp @@ -112,25 +112,25 @@ void FancyZonesSettings::LoadSettings(PCWSTR config, bool fromFile) noexcept try for (auto const& setting : m_configBools) { - if (values.is_bool_value(setting.name)) + if (const auto val = values.get_bool_value(setting.name)) { - *setting.value = values.get_bool_value(setting.name); + *setting.value = *val; } } - if (values.is_string_value(m_zoneHiglightName)) + if (auto val = values.get_string_value(m_zoneHiglightName)) { - m_settings.zoneHightlightColor = values.get_string_value(m_zoneHiglightName); + m_settings.zoneHightlightColor = std::move(*val); } - if (values.is_object_value(m_editorHotkeyName)) + if (const auto val = values.get_json(m_editorHotkeyName)) { - m_settings.editorHotkey = PowerToysSettings::HotkeyObject::from_json(values.get_json(m_editorHotkeyName)); + m_settings.editorHotkey = PowerToysSettings::HotkeyObject::from_json(*val); } - if (values.is_string_value(m_excludedAppsName)) + if (auto val = values.get_string_value(m_excludedAppsName)) { - m_settings.excludedApps = values.get_string_value(m_excludedAppsName); + m_settings.excludedApps = std::move(*val); m_settings.excludedAppsArray.clear(); auto excludedUppercase = m_settings.excludedApps; CharUpperBuffW(excludedUppercase.data(), (DWORD)excludedUppercase.length()); @@ -163,7 +163,7 @@ void FancyZonesSettings::SaveSettings() noexcept try } values.add_property(m_zoneHiglightName, m_settings.zoneHightlightColor); - values.add_property(m_editorHotkeyName, m_settings.editorHotkey); + values.add_property(m_editorHotkeyName, m_settings.editorHotkey.get_json()); values.add_property(m_excludedAppsName, m_settings.excludedApps); values.save_to_settings_file(); diff --git a/src/modules/powerrename/dll/dllmain.cpp b/src/modules/powerrename/dll/dllmain.cpp index c12dde78d0..ac47385185 100644 --- a/src/modules/powerrename/dll/dllmain.cpp +++ b/src/modules/powerrename/dll/dllmain.cpp @@ -252,11 +252,11 @@ public: PowerToysSettings::PowerToyValues values = PowerToysSettings::PowerToyValues::from_json_string(config); - CSettings::SetPersistState(values.get_bool_value(L"bool_persist_input")); - CSettings::SetMRUEnabled(values.get_bool_value(L"bool_mru_enabled")); - CSettings::SetMaxMRUSize(values.get_int_value(L"int_max_mru_size")); - CSettings::SetShowIconOnMenu(values.get_bool_value(L"bool_show_icon_on_menu")); - CSettings::SetExtendedContextMenuOnly(values.get_bool_value(L"bool_show_extended_menu")); + CSettings::SetPersistState(values.get_bool_value(L"bool_persist_input").value()); + CSettings::SetMRUEnabled(values.get_bool_value(L"bool_mru_enabled").value()); + CSettings::SetMaxMRUSize(values.get_int_value(L"int_max_mru_size").value()); + CSettings::SetShowIconOnMenu(values.get_bool_value(L"bool_show_icon_on_menu").value()); + CSettings::SetExtendedContextMenuOnly(values.get_bool_value(L"bool_show_extended_menu").value()); } catch (std::exception) { // Improper JSON. diff --git a/src/modules/shortcut_guide/shortcut_guide.cpp b/src/modules/shortcut_guide/shortcut_guide.cpp index f447b36cd8..30aa5e010e 100644 --- a/src/modules/shortcut_guide/shortcut_guide.cpp +++ b/src/modules/shortcut_guide/shortcut_guide.cpp @@ -61,39 +61,37 @@ void OverlayWindow::set_config(const wchar_t * config) { try { PowerToysSettings::PowerToyValues _values = PowerToysSettings::PowerToyValues::from_json_string(config); - if (_values.is_int_value(pressTime.name)) { - int press_delay_time = _values.get_int_value(pressTime.name); - pressTime.value = press_delay_time; + if (const auto press_delay_time = _values.get_int_value(pressTime.name)) { + pressTime.value = *press_delay_time; if (target_state) { - target_state->set_delay(press_delay_time); + target_state->set_delay(*press_delay_time); } } - if (_values.is_int_value(overlayOpacity.name)) { - int overlay_opacity = _values.get_int_value(overlayOpacity.name); - overlayOpacity.value = overlay_opacity; + if (const auto overlay_opacity = _values.get_int_value(overlayOpacity.name)) { + overlayOpacity.value = *overlay_opacity; if (winkey_popup) { winkey_popup->apply_overlay_opacity(((float)overlayOpacity.value) / 100.0f); } } - if (_values.is_string_value(theme.name)) { - theme.value = _values.get_string_value(theme.name); + if (auto val = _values.get_string_value(theme.name)) { + theme.value = std::move(*val); winkey_popup->set_theme(theme.value); } _values.save_to_settings_file(); Trace::SettingsChanged(pressTime.value, overlayOpacity.value, theme.value); } - catch (std::exception&) { - // Improper JSON. + catch (...) { + // Improper JSON. TODO: handle the error. } } void OverlayWindow::enable() { if (!_enabled) { Trace::EnableShortcutGuide(true); - winkey_popup = new D2DOverlayWindow(); + winkey_popup = std::make_unique(); winkey_popup->apply_overlay_opacity(((float)overlayOpacity.value)/100.0f); winkey_popup->set_theme(theme.value); - target_state = new TargetState(pressTime.value); + target_state = std::make_unique(pressTime.value); winkey_popup->initialize(); } _enabled = true; @@ -107,10 +105,8 @@ void OverlayWindow::disable(bool trace_event) { } winkey_popup->hide(); target_state->exit(); - delete target_state; - delete winkey_popup; - target_state = nullptr; - winkey_popup = nullptr; + target_state.reset(); + winkey_popup.reset(); } } @@ -164,14 +160,14 @@ void OverlayWindow::init_settings() { try { PowerToysSettings::PowerToyValues settings = PowerToysSettings::PowerToyValues::load_from_settings_file(OverlayWindow::get_name()); - if (settings.is_int_value(pressTime.name)) { - pressTime.value = settings.get_int_value(pressTime.name); + if (const auto val = settings.get_int_value(pressTime.name)) { + pressTime.value = *val; } - if (settings.is_int_value(overlayOpacity.name)) { - overlayOpacity.value = settings.get_int_value(overlayOpacity.name); + if (const auto val = settings.get_int_value(overlayOpacity.name)) { + overlayOpacity.value = *val; } - if (settings.is_string_value(theme.name)) { - theme.value = settings.get_string_value(theme.name); + if (auto val = settings.get_string_value(theme.name)) { + theme.value = std::move(*val); } } catch (std::exception&) { diff --git a/src/modules/shortcut_guide/shortcut_guide.h b/src/modules/shortcut_guide/shortcut_guide.h index 2a6154287a..ea0d4c1a40 100644 --- a/src/modules/shortcut_guide/shortcut_guide.h +++ b/src/modules/shortcut_guide/shortcut_guide.h @@ -32,8 +32,8 @@ public: virtual void destroy() override; private: - TargetState* target_state; - D2DOverlayWindow *winkey_popup; + std::unique_ptr target_state; + std::unique_ptr winkey_popup; bool _enabled = false; void init_settings(); diff --git a/src/runner/general_settings.cpp b/src/runner/general_settings.cpp index 8b4f213806..4357867fe7 100644 --- a/src/runner/general_settings.cpp +++ b/src/runner/general_settings.cpp @@ -5,45 +5,38 @@ #include "powertoy_module.h" #include -using namespace web; - static std::wstring settings_theme = L"system"; -web::json::value load_general_settings() { +json::JsonObject load_general_settings() { auto loaded = PTSettingsHelper::load_general_settings(); - if (loaded.has_string_field(L"theme")) { - settings_theme = loaded.as_object()[L"theme"].as_string(); - if (settings_theme != L"dark" && settings_theme != L"light") { - settings_theme = L"system"; - } - } else { + settings_theme = loaded.GetNamedString(L"theme", L"system"); + if (settings_theme != L"dark" && settings_theme != L"light") { settings_theme = L"system"; } return loaded; } -web::json::value get_general_settings() { - json::value result = json::value::object(); - bool startup = is_auto_start_task_active_for_this_user(); - result.as_object()[L"startup"] = json::value::boolean(startup); +json::JsonObject get_general_settings() { + json::JsonObject result; + const bool startup = is_auto_start_task_active_for_this_user(); + result.SetNamedValue(L"startup", json::value(startup)); - json::value enabled = json::value::object(); + json::JsonObject enabled; for (auto&[name, powertoy] : modules()) { - enabled.as_object()[name] = json::value::boolean(powertoy.is_enabled()); + enabled.SetNamedValue(name, json::value(powertoy.is_enabled())); } - result.as_object()[L"enabled"] = enabled; + result.SetNamedValue(L"enabled", std::move(enabled)); - result.as_object()[L"theme"] = json::value::string(settings_theme); - result.as_object()[L"system_theme"] = json::value::string(WindowsColors::is_dark_mode() ? L"dark" : L"light"); - result.as_object()[L"powertoys_version"] = json::value::string(get_product_version()); + result.SetNamedValue(L"theme", json::value(settings_theme)); + result.SetNamedValue(L"system_theme", json::value(WindowsColors::is_dark_mode() ? L"dark" : L"light")); + result.SetNamedValue(L"powertoys_version", json::value(get_product_version())); return result; } -void apply_general_settings(const json::value& general_configs) { - bool contains_startup = general_configs.has_boolean_field(L"startup"); - if (contains_startup) { - bool startup = general_configs.at(L"startup").as_bool(); - bool current_startup = is_auto_start_task_active_for_this_user(); +void apply_general_settings(const json::JsonObject& general_configs) { + if (json::has(general_configs, L"startup", json::JsonValueType::Boolean)) { + const bool startup = general_configs.GetNamedBoolean(L"startup"); + const bool current_startup = is_auto_start_task_active_for_this_user(); if (current_startup != startup) { if (startup) { enable_auto_start_task_for_this_user(); @@ -52,26 +45,33 @@ void apply_general_settings(const json::value& general_configs) { } } } - bool contains_enabled = general_configs.has_object_field(L"enabled"); - if (contains_enabled) { - for (auto enabled_element : general_configs.at(L"enabled").as_object()) { - if (enabled_element.second.is_boolean() && modules().find(enabled_element.first) != modules().end()) { - bool module_inst_enabled = modules().at(enabled_element.first).is_enabled(); - bool target_enabled = enabled_element.second.as_bool(); - if (module_inst_enabled != target_enabled) { - if (target_enabled) { - modules().at(enabled_element.first).enable(); - } else { - modules().at(enabled_element.first).disable(); - } - } + if (json::has(general_configs, L"enabled")) { + for (const auto& enabled_element : general_configs.GetNamedObject(L"enabled")) { + const auto value = enabled_element.Value(); + if (value.ValueType() != json::JsonValueType::Boolean) { + continue; + } + const std::wstring name{enabled_element.Key().c_str()}; + const bool found = modules().find(name) != modules().end(); + if (!found) { + continue; + } + const bool module_inst_enabled = modules().at(name).is_enabled(); + const bool target_enabled = value.GetBoolean(); + if (module_inst_enabled == target_enabled) { + continue; + } + if (target_enabled) { + modules().at(name).enable(); + } else { + modules().at(name).disable(); } } } - if (general_configs.has_string_field(L"theme")) { - settings_theme = general_configs.at(L"theme").as_string(); + if (json::has(general_configs, L"theme", json::JsonValueType::String)) { + settings_theme = general_configs.GetNamedString(L"theme"); } - json::value save_settings = get_general_settings(); + json::JsonObject save_settings = get_general_settings(); PTSettingsHelper::save_general_settings(save_settings); } @@ -80,21 +80,22 @@ void start_initial_powertoys() { std::unordered_set powertoys_to_enable; - json::value general_settings; + json::JsonObject general_settings; try { general_settings = load_general_settings(); - json::value enabled = general_settings[L"enabled"]; - for (auto enabled_element : enabled.as_object()) { - if (enabled_element.second.as_bool()) { + json::JsonObject enabled = general_settings.GetNamedObject(L"enabled"); + for (const auto & enabled_element : enabled) { + if (enabled_element.Value().GetBoolean()) { // Enable this powertoy. - powertoys_to_enable.emplace(enabled_element.first); + powertoys_to_enable.emplace(enabled_element.Key()); } } only_enable_some_powertoys = true; } - catch (std::exception&) { + catch (...) { // Couldn't read the general settings correctly. // Load all powertoys. + // TODO: notify user about invalid json config only_enable_some_powertoys = false; } diff --git a/src/runner/general_settings.h b/src/runner/general_settings.h index 5612352a97..64704d5235 100644 --- a/src/runner/general_settings.h +++ b/src/runner/general_settings.h @@ -1,6 +1,7 @@ #pragma once -#include -web::json::value get_general_settings(); -void apply_general_settings(const web::json::value& general_configs); +#include + +json::JsonObject get_general_settings(); +void apply_general_settings(const json::JsonObject & general_configs); void start_initial_powertoys(); diff --git a/src/runner/powertoy_module.cpp b/src/runner/powertoy_module.cpp index 9c9f7b09a0..0459fe00e9 100644 --- a/src/runner/powertoy_module.cpp +++ b/src/runner/powertoy_module.cpp @@ -23,3 +23,12 @@ PowertoyModule load_powertoy(const std::wstring& filename) { module->register_system_menu_helper(&SystemMenuHelperInstace()); return PowertoyModule(module, handle); } + +json::JsonObject PowertoyModule::json_config() const { + int size = 0; + module->get_config(nullptr, &size); + std::wstring result; + result.resize(size - 1); + module->get_config(result.data(), &size); + return json::JsonObject::Parse(result); +} diff --git a/src/runner/powertoy_module.h b/src/runner/powertoy_module.h index f0e5e74599..acb790f6e4 100644 --- a/src/runner/powertoy_module.h +++ b/src/runner/powertoy_module.h @@ -9,6 +9,7 @@ #include class PowertoyModule; +#include struct PowertoyModuleDeleter { void operator()(PowertoyModuleIface* module) const { @@ -48,6 +49,8 @@ public: const std::wstring& get_name() const { return name; } + + json::JsonObject json_config() const; const std::wstring get_config() const { std::wstring result; diff --git a/src/runner/settings_window.cpp b/src/runner/settings_window.cpp index 104501937b..69917cb309 100644 --- a/src/runner/settings_window.cpp +++ b/src/runner/settings_window.cpp @@ -4,89 +4,88 @@ #include #include #include -#include + #include "powertoy_module.h" #include #include "tray_icon.h" #include "general_settings.h" #include "common/windows_colors.h" -#define BUFSIZE 1024 +#include -using namespace web; +#define BUFSIZE 1024 TwoWayPipeMessageIPC* current_settings_ipc = NULL; -json::value get_power_toys_settings() { - json::value result = json::value::object(); - for (auto&[name, powertoy] : modules()) { +json::JsonObject get_power_toys_settings() { + json::JsonObject result; + for (const auto&[name, powertoy] : modules()) { try { - json::value powertoys_config = json::value::parse(powertoy.get_config()); - result.as_object()[name] = powertoys_config; + result.SetNamedValue(name, powertoy.json_config()); } - catch (json::json_exception&) { - //Malformed JSON. + catch (...) { + // TODO: handle malformed JSON. } } return result; } -json::value get_all_settings() { - json::value result = json::value::object(); - result.as_object()[L"general"] = get_general_settings(); - result.as_object()[L"powertoys"] = get_power_toys_settings(); +json::JsonObject get_all_settings() { + json::JsonObject result; + + result.SetNamedValue(L"general", get_general_settings()); + result.SetNamedValue(L"powertoys", get_power_toys_settings()); return result; } -void dispatch_json_action_to_module(const json::value& powertoys_configs) { - for (auto powertoy_element : powertoys_configs.as_object()) { - std::wstringstream ws; - ws << powertoy_element.second; - if (modules().find(powertoy_element.first) != modules().end()) { - modules().at(powertoy_element.first).call_custom_action(ws.str()); +void dispatch_json_action_to_module(const json::JsonObject& powertoys_configs) { + for (const auto& powertoy_element : powertoys_configs) { + const std::wstring name{powertoy_element.Key().c_str()}; + if (modules().find(name) != modules().end()) { + const auto element = powertoy_element.Value().Stringify(); + modules().at(name).call_custom_action(element.c_str()); } } } void send_json_config_to_module(const std::wstring& module_key, const std::wstring& settings) { if (modules().find(module_key) != modules().end()) { - modules().at(module_key).set_config(settings); + modules().at(module_key).set_config(settings.c_str()); } } -void dispatch_json_config_to_modules(const json::value& powertoys_configs) { - for (auto powertoy_element : powertoys_configs.as_object()) { - std::wstringstream ws; - ws << powertoy_element.second; - send_json_config_to_module(powertoy_element.first, ws.str()); +void dispatch_json_config_to_modules(const json::JsonObject& powertoys_configs) { + for (const auto& powertoy_element : powertoys_configs) { + const auto element = powertoy_element.Value().Stringify(); + send_json_config_to_module(powertoy_element.Key().c_str(), element.c_str()); } }; void dispatch_received_json(const std::wstring &json_to_parse) { - json::value j = json::value::parse(json_to_parse); - for(auto base_element : j.as_object()) { - if (base_element.first == L"general") { - apply_general_settings(base_element.second); - std::wstringstream ws; - ws << get_all_settings(); - if (current_settings_ipc != NULL) { - current_settings_ipc->send(ws.str()); + const json::JsonObject j = json::JsonObject::Parse(json_to_parse); + for(const auto & base_element : j) { + const auto name = base_element.Key(); + const auto value = base_element.Value(); + + if (name == L"general") { + apply_general_settings(value.GetObjectW()); + if (current_settings_ipc != nullptr) { + const std::wstring settings_string{get_all_settings().Stringify().c_str()}; + current_settings_ipc->send(settings_string); } - } else if (base_element.first == L"powertoys") { - dispatch_json_config_to_modules(base_element.second); - std::wstringstream ws; - ws << get_all_settings(); - if (current_settings_ipc != NULL) { - current_settings_ipc->send(ws.str()); + } else if (name == L"powertoys") { + dispatch_json_config_to_modules(value.GetObjectW()); + if (current_settings_ipc != nullptr) { + const std::wstring settings_string{get_all_settings().Stringify().c_str()}; + current_settings_ipc->send(settings_string); } - } else if (base_element.first == L"refresh") { - std::wstringstream ws; - ws << get_all_settings(); - if (current_settings_ipc != NULL) { - current_settings_ipc->send(ws.str()); + } else if (name == L"refresh") { + if (current_settings_ipc != nullptr) { + const std::wstring settings_string{get_all_settings().Stringify().c_str()}; + current_settings_ipc->send(settings_string); } - } else if (base_element.first == L"action") { - dispatch_json_action_to_module(base_element.second); + } else if (name == L"action") { + dispatch_json_action_to_module(value.GetObjectW()); } } return; @@ -118,7 +117,7 @@ void run_settings_window() { wcscat_s(executable_path, L"\\PowerToysSettings.exe"); WCHAR executable_args[MAX_PATH * 3]; - std::wstring settings_theme_setting = get_general_settings().at(L"theme").as_string(); + const std::wstring settings_theme_setting{get_general_settings().GetNamedString(L"theme").c_str()}; std::wstring settings_theme; if (settings_theme_setting == L"dark" || (settings_theme_setting == L"system" && WindowsColors::is_dark_mode())) { settings_theme = L" dark"; // Include arg separating space