diff --git a/change/react-native-windows-4080fb51-cbec-4c0c-b2e6-3a14a8be8965.json b/change/react-native-windows-4080fb51-cbec-4c0c-b2e6-3a14a8be8965.json new file mode 100644 index 00000000000..712e44dc708 --- /dev/null +++ b/change/react-native-windows-4080fb51-cbec-4c0c-b2e6-3a14a8be8965.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "merge in folly 2024.10.14.00", + "packageName": "react-native-windows", + "email": "tatianakapos@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/vnext/Desktop.DLL/react-native-win32.x64.def b/vnext/Desktop.DLL/react-native-win32.x64.def index 9c2f22b9a29..95ce42f28f0 100644 --- a/vnext/Desktop.DLL/react-native-win32.x64.def +++ b/vnext/Desktop.DLL/react-native-win32.x64.def @@ -5,12 +5,9 @@ ; ************************************************************************************************** EXPORTS -??0dynamic@folly@@QEAA@$$QEAU01@@Z ??8folly@@YA_NAEBUdynamic@0@0@Z ??4dynamic@folly@@QEAAAEAU01@$$QEAU01@@Z -?hash@dynamic@folly@@QEBA_KXZ ??Mfolly@@YA_NAEBUdynamic@0@0@Z -?destroy@dynamic@folly@@AEAAXXZ ??0TypeError@folly@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@W4Type@dynamic@1@@Z ??0dynamic@folly@@QEAA@AEBU01@@Z ??$safe_assert_terminate@$0A@@detail@folly@@YAXPEBUsafe_assert_arg@01@ZZ @@ -29,7 +26,6 @@ EXPORTS ?Make@IWebSocketResource@Networking@React@Microsoft@@SA?AV?$shared_ptr@UIWebSocketResource@Networking@React@Microsoft@@@std@@XZ ?Make@JSBigAbiString@react@facebook@@SA?AV?$unique_ptr@$$CBUJSBigAbiString@react@facebook@@U?$default_delete@$$CBUJSBigAbiString@react@facebook@@@std@@@std@@$$QEAV?$unique_ptr@U?$IAbiArray@D@AbiSafe@@UAbiObjectDeleter@2@@5@@Z ?at@dynamic@folly@@QEGBAAEBU12@V?$Range@PEBD@2@@Z -?atImpl@dynamic@folly@@AEGBAAEBU12@AEBU12@@Z ?getRuntimeExecutor@Instance@react@facebook@@QEAA?AV?$function@$$A6AX$$QEAV?$function@$$A6AXAEAVRuntime@jsi@facebook@@@Z@std@@@Z@std@@XZ ?callJSFunction@Instance@react@facebook@@QEAAX$$QEAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0$$QEAUdynamic@folly@@@Z ?createI18nModule@windows@react@@YA?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@V?$unique_ptr@UII18nModule@windows@react@@U?$default_delete@UII18nModule@windows@react@@@std@@@4@@Z @@ -40,13 +36,11 @@ EXPORTS ?get_ptr@dynamic@folly@@QEGBAPEBU12@V?$Range@PEBD@2@@Z ?get_ptrImpl@dynamic@folly@@AEGBAPEBU12@AEBU12@@Z ?loadScriptFromString@Instance@react@facebook@@QEAAXV?$unique_ptr@$$CBVJSBigString@react@facebook@@U?$default_delete@$$CBVJSBigString@react@facebook@@@std@@@std@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@_N@Z -?makeConversionError@folly@@YA?AVConversionError@1@W4ConversionCode@1@V?$Range@PEBD@1@@Z ?moduleNames@ModuleRegistry@react@facebook@@QEAA?AV?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@std@@XZ ?assertionFailure@detail@folly@@YAXPEBD00I0H@Z ?parseJson@folly@@YA?AUdynamic@1@V?$Range@PEBD@1@@Z ?setGlobalVariable@Instance@react@facebook@@QEAAXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$unique_ptr@$$CBVJSBigString@react@facebook@@U?$default_delete@$$CBVJSBigString@react@facebook@@@std@@@5@@Z ?size@dynamic@folly@@QEBA_KXZ -?str_to_bool@detail@folly@@YA?AV?$Expected@_NW4ConversionCode@folly@@@2@PEAV?$Range@PEBD@2@@Z ?toJson@folly@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AEBUdynamic@1@@Z ?typeName@dynamic@folly@@QEBAPEBDXZ ?Utf16ToUtf8@Unicode@Common@Microsoft@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@5@@Z diff --git a/vnext/Desktop.DLL/react-native-win32.x86.def b/vnext/Desktop.DLL/react-native-win32.x86.def index 4cd8ba317cc..68547f272f0 100644 --- a/vnext/Desktop.DLL/react-native-win32.x86.def +++ b/vnext/Desktop.DLL/react-native-win32.x86.def @@ -5,12 +5,9 @@ ; ************************************************************************************************** EXPORTS -??0dynamic@folly@@QAE@$$QAU01@@Z ??8folly@@YG_NABUdynamic@0@0@Z ??Mfolly@@YG_NABUdynamic@0@0@Z ??4dynamic@folly@@QAEAAU01@$$QAU01@@Z -?hash@dynamic@folly@@QBEIXZ -?destroy@dynamic@folly@@AAEXXZ ??0TypeError@folly@@QAE@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@W4Type@dynamic@1@@Z ??0dynamic@folly@@QAE@ABU01@@Z ??4dynamic@folly@@QAEAAU01@ABU01@@Z @@ -30,7 +27,6 @@ EXPORTS ?Make@JSBigAbiString@react@facebook@@SG?AV?$unique_ptr@$$CBUJSBigAbiString@react@facebook@@U?$default_delete@$$CBUJSBigAbiString@react@facebook@@@std@@@std@@$$QAV?$unique_ptr@U?$IAbiArray@D@AbiSafe@@UAbiObjectDeleter@2@@5@@Z ?moduleNames@ModuleRegistry@react@facebook@@QAE?AV?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@std@@XZ ?at@dynamic@folly@@QGBEABU12@V?$Range@PBD@2@@Z -?atImpl@dynamic@folly@@AGBEABU12@ABU12@@Z ?getRuntimeExecutor@Instance@react@facebook@@QAE?AV?$function@$$A6GX$$QAV?$function@$$A6GXAAVRuntime@jsi@facebook@@@Z@std@@@Z@std@@XZ ?callJSFunction@Instance@react@facebook@@QAEX$$QAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0$$QAUdynamic@folly@@@Z ?createI18nModule@windows@react@@YG?AV?$unique_ptr@VCxxModule@module@xplat@facebook@@U?$default_delete@VCxxModule@module@xplat@facebook@@@std@@@std@@V?$unique_ptr@UII18nModule@windows@react@@U?$default_delete@UII18nModule@windows@react@@@std@@@4@@Z @@ -40,11 +36,9 @@ EXPORTS ?get_ptr@dynamic@folly@@QGBEPBU12@V?$Range@PBD@2@@Z ?get_ptrImpl@dynamic@folly@@AGBEPBU12@ABU12@@Z ?loadScriptFromString@Instance@react@facebook@@QAEXV?$unique_ptr@$$CBVJSBigString@react@facebook@@U?$default_delete@$$CBVJSBigString@react@facebook@@@std@@@std@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@_N@Z -?makeConversionError@folly@@YG?AVConversionError@1@W4ConversionCode@1@V?$Range@PBD@1@@Z ?parseJson@folly@@YG?AUdynamic@1@V?$Range@PBD@1@@Z ?setGlobalVariable@Instance@react@facebook@@QAEXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$unique_ptr@$$CBVJSBigString@react@facebook@@U?$default_delete@$$CBVJSBigString@react@facebook@@@std@@@5@@Z ?size@dynamic@folly@@QBEIXZ -?str_to_bool@detail@folly@@YG?AV?$Expected@_NW4ConversionCode@folly@@@2@PAV?$Range@PBD@2@@Z ?toJson@folly@@YG?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@ABUdynamic@1@@Z ?typeName@dynamic@folly@@QBEPBDXZ ?Utf16ToUtf8@Unicode@Common@Microsoft@@YG?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@ABV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@5@@Z diff --git a/vnext/Directory.Build.props b/vnext/Directory.Build.props index 373ea4d5bdb..52be548f0b3 100644 --- a/vnext/Directory.Build.props +++ b/vnext/Directory.Build.props @@ -19,9 +19,11 @@ --> true false - - 2024.01.01.00 - 234d39a36a43106747d10cc19efada72fd810dd3 + + 2024.10.14.00 + 6.1.4 + ad90720829db5ba0c3d0e44994856dcce33d7940 + ad90720829db5ba0c3d0e44994856dcce33d7940 10.1.0 ca2e3685b160617d3d95fcd9e789c4e06ca88 diff --git a/vnext/Folly/Folly.vcxproj b/vnext/Folly/Folly.vcxproj index d98cb6d6b0e..1174fffa021 100644 --- a/vnext/Folly/Folly.vcxproj +++ b/vnext/Folly/Folly.vcxproj @@ -64,7 +64,7 @@ - + @@ -73,8 +73,8 @@ $(IntDir)\portabilityString.obj - - + + @@ -106,7 +106,7 @@ - + @@ -127,6 +127,9 @@ + + + @@ -151,7 +154,7 @@ - + @@ -220,6 +223,10 @@ + + + + @@ -234,7 +241,9 @@ + + @@ -262,7 +271,7 @@ Use false true - $(ReactNativeWindowsDir)stubs;$(FollyDir);$(FmtDir)\include;%(AdditionalIncludeDirectories) + $(ReactNativeWindowsDir)stubs;$(FollyDir);$(FollyDir)\..\fast_float-6.1.4\include;$(FmtDir)\include;%(AdditionalIncludeDirectories) FOLLY_CFG_NO_COROUTINES;FOLLY_NO_CONFIG;NOMINMAX;_CRT_SECURE_NO_WARNINGS;WINAPI_PARTITION_APP;%(PreprocessorDefinitions) 4251;4293;4305;4800;4804;4310;%(DisableSpecificWarnings) @@ -282,6 +291,10 @@ $(FollyZipDir)\folly-$(FollyVersion).zip $(MSBuildThisFileDirectory)cgmanifest.json + + $(FollyDir)..\.fastfloatzip + $(FollyDir)..\.fastfloatzip\fastfloat.zip + + + + + + + + + { @@ -312,6 +341,16 @@ } }, "DevelopmentDependency": false + }, + { + "Component": { + "Type": "git", + "Git": { + "RepositoryUrl": "https://github.com/fastfloat/fast_float", + "CommitHash": "$(FastFloatCommitHash)" + } + }, + "DevelopmentDependency": false } ] } diff --git a/vnext/Folly/Folly.vcxproj.filters b/vnext/Folly/Folly.vcxproj.filters index 64fe8659cfd..67f1bd0e4a1 100644 --- a/vnext/Folly/Folly.vcxproj.filters +++ b/vnext/Folly/Folly.vcxproj.filters @@ -48,16 +48,16 @@ Source Files - + Source Files Source Files\container\detail - + Source Files - + Source Files @@ -151,6 +151,9 @@ Header Files + + Header Files + Header Files\portability @@ -183,10 +186,19 @@ Header Files + + + Header Files + + + Header Files Header Files + + Header Files + Header Files\portability @@ -268,7 +280,7 @@ Header Files - + Header Files diff --git a/vnext/Folly/TEMP_UntilFollyUpdate/ConstexprMath.h b/vnext/Folly/TEMP_UntilFollyUpdate/ConstexprMath.h index 707eb604e8f..eddc4fe39bd 100644 --- a/vnext/Folly/TEMP_UntilFollyUpdate/ConstexprMath.h +++ b/vnext/Folly/TEMP_UntilFollyUpdate/ConstexprMath.h @@ -24,6 +24,8 @@ #include #include +#include +#include namespace folly { @@ -42,25 +44,25 @@ using enable_if_floating_t = /// /// mimic: std::numbers::e_v, C++20 template -FOLLY_INLINE_VARIABLE constexpr T e_v = detail::enable_if_floating_t( +inline constexpr T e_v = detail::enable_if_floating_t( 2.71828182845904523536028747135266249775724709369995L); /// ln2_v /// /// mimic: std::numbers::ln2_v, C++20 template -FOLLY_INLINE_VARIABLE constexpr T ln2_v = detail::enable_if_floating_t( +inline constexpr T ln2_v = detail::enable_if_floating_t( 0.69314718055994530941723212145817656807550013436025L); /// e /// /// mimic: std::numbers::e, C++20 -FOLLY_INLINE_VARIABLE constexpr double e = e_v; +inline constexpr double e = e_v; /// ln2 /// /// mimic: std::numbers::ln2, C++20 -FOLLY_INLINE_VARIABLE constexpr double ln2 = ln2_v; +inline constexpr double ln2 = ln2_v; } // namespace numbers @@ -75,11 +77,6 @@ struct floating_point_integral_constant { constexpr operator value_type() const noexcept { return value; } constexpr value_type operator()() const noexcept { return value; } }; -#if FOLLY_CPLUSPLUS < 201703L -template -constexpr typename floating_point_integral_constant::value_type - floating_point_integral_constant::value; -#endif // ---- @@ -105,7 +102,7 @@ constexpr size_t constexpr_iterated_squares_desc_size_(T const base) { /// /// For use with constexpr_iterated_squares_desc below. template -FOLLY_INLINE_VARIABLE constexpr size_t constexpr_iterated_squares_desc_size_v = +inline constexpr size_t constexpr_iterated_squares_desc_size_v = detail::constexpr_iterated_squares_desc_size_(Base::value); /// constexpr_iterated_squares_desc @@ -225,18 +222,13 @@ struct constexpr_iterated_squares_desc { return {power, scale}; } }; -#if FOLLY_CPLUSPLUS < 201703L -template -constexpr typename constexpr_iterated_squares_desc::size_type - constexpr_iterated_squares_desc::size; -#endif /// constexpr_iterated_squares_desc_v /// /// An instance of constexpr_iterated_squares_desc of max size with the given /// base. template -FOLLY_INLINE_VARIABLE constexpr auto constexpr_iterated_squares_desc_v = +inline constexpr auto constexpr_iterated_squares_desc_v = constexpr_iterated_squares_desc< typename Base::value_type, constexpr_iterated_squares_desc_size_v>{Base::value}; @@ -422,7 +414,7 @@ template constexpr T constexpr_ceil(T t, T round) { return round == T(0) ? t - : ((t + (t < T(0) ? T(0) : round - T(1))) / round) * round; + : ((t + (t <= T(0) ? T(0) : round - T(1))) / round) * round; } /// constexpr_mult @@ -765,6 +757,22 @@ constexpr T constexpr_add_overflow_clamped(T a, T b) { static_assert( !std::is_integral::value || sizeof(T) <= sizeof(M), "Integral type too large!"); + if (!folly::is_constant_evaluated_or(true)) { + if constexpr (std::is_integral_v) { + T ret{}; + if (FOLLY_UNLIKELY(!checked_add(&ret, a, b))) { + if constexpr (std::is_signed_v) { + // Could be either overflow or underflow for signed types. + // Can only be underflow if both inputs are negative. + if (a < 0 && b < 0) { + return L::min(); + } + } + return L::max(); + } + return ret; + } + } // clang-format off return // don't do anything special for non-integral types. @@ -780,7 +788,7 @@ constexpr T constexpr_add_overflow_clamped(T a, T b) { // a < 0 && b >= 0, `a + b` will always be in valid range of type T. !(b < 0) ? a + b : // a < 0 && b < 0, keep the result >= MIN. - a + constexpr_max(b, T(L::min() - a)); + a + constexpr_max(b, T(L::min() - a)); // clang-format on } diff --git a/vnext/Folly/TEMP_UntilFollyUpdate/Conv.cpp b/vnext/Folly/TEMP_UntilFollyUpdate/Conv.cpp new file mode 100644 index 00000000000..44b2851409c --- /dev/null +++ b/vnext/Folly/TEMP_UntilFollyUpdate/Conv.cpp @@ -0,0 +1,1205 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include + +#include // @manual=fbsource//third-party/fast_float:fast_float + +namespace folly { +namespace detail { + +namespace { + +/** + * Finds the first non-digit in a string. The number of digits + * searched depends on the precision of the Tgt integral. Assumes the + * string starts with NO whitespace and NO sign. + * + * The semantics of the routine is: + * for (;; ++b) { + * if (b >= e || !isdigit(*b)) return b; + * } + * + * Complete unrolling marks bottom-line (i.e. entire conversion) + * improvements of 20%. + */ +inline const char* findFirstNonDigit(const char* b, const char* e) { + for (; b < e; ++b) { + auto const c = static_cast(*b) - '0'; + if (c >= 10) { + break; + } + } + return b; +} + +// Maximum value of number when represented as a string +template +struct MaxString { + static const char* const value; +}; + +template <> +const char* const MaxString::value = "255"; +template <> +const char* const MaxString::value = "65535"; +template <> +const char* const MaxString::value = "4294967295"; +#if __SIZEOF_LONG__ == 4 +template <> +const char* const MaxString::value = "4294967295"; +#else +template <> +const char* const MaxString::value = "18446744073709551615"; +#endif +static_assert( + sizeof(unsigned long) >= 4, + "Wrong value for MaxString::value," + " please update."); +template <> +const char* const MaxString::value = "18446744073709551615"; +static_assert( + sizeof(unsigned long long) >= 8, + "Wrong value for MaxString::value" + ", please update."); + +#if FOLLY_HAVE_INT128_T +template <> +const char* const MaxString<__uint128_t>::value = + "340282366920938463463374607431768211455"; +#endif + +/* +* Lookup tables that converts from a decimal character value to an integral +* binary value, shifted by a decimal "shift" multiplier. +* For all character values in the range '0'..'9', the table at those +* index locations returns the actual decimal value shifted by the multiplier. +* For all other values, the lookup table returns an invalid OOR value. +*/ +// Out-of-range flag value, larger than the largest value that can fit in +// four decimal bytes (9999), but four of these added up together should +// still not overflow uint16_t. +constexpr int32_t OOR = 10000; + +alignas(16) constexpr uint16_t shift1[] = { + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, 1, // 40 + 2, 3, 4, 5, 6, 7, 8, 9, OOR, OOR, + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240 + OOR, OOR, OOR, OOR, OOR, OOR // 250 +}; + +alignas(16) constexpr uint16_t shift10[] = { + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, 10, // 40 + 20, 30, 40, 50, 60, 70, 80, 90, OOR, OOR, + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240 + OOR, OOR, OOR, OOR, OOR, OOR // 250 +}; + +alignas(16) constexpr uint16_t shift100[] = { + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, 100, // 40 + 200, 300, 400, 500, 600, 700, 800, 900, OOR, OOR, + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240 + OOR, OOR, OOR, OOR, OOR, OOR // 250 +}; + +alignas(16) constexpr uint16_t shift1000[] = { + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, 1000, // 40 + 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, OOR, OOR, + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230 + OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240 + OOR, OOR, OOR, OOR, OOR, OOR // 250 +}; + +struct ErrorString { + const char* string; + bool quote; +}; + +// Keep this in sync with ConversionCode in Conv.h +constexpr const std::array< + ErrorString, + static_cast(ConversionCode::NUM_ERROR_CODES)> + kErrorStrings{{ + {"Success", true}, + {"Empty input string", true}, + {"No digits found in input string", true}, + {"Integer overflow when parsing bool (must be 0 or 1)", true}, + {"Invalid value for bool", true}, + {"Non-digit character found", true}, + {"Invalid leading character", true}, + {"Overflow during conversion", true}, + {"Negative overflow during conversion", true}, + {"Unable to convert string to floating point value", true}, + {"Non-whitespace character found after end of conversion", true}, + {"Overflow during arithmetic conversion", false}, + {"Negative overflow during arithmetic conversion", false}, + {"Loss of precision during arithmetic conversion", false}, + }}; + +// Check if ASCII is really ASCII +using IsAscii = + std::bool_constant<'A' == 65 && 'Z' == 90 && 'a' == 97 && 'z' == 122>; + +// The code in this file that uses tolower() really only cares about +// 7-bit ASCII characters, so we can take a nice shortcut here. +inline char tolower_ascii(char in) { + return IsAscii::value ? in | 0x20 : char(std::tolower(in)); +} + +inline bool bool_str_cmp(const char** b, size_t len, const char* value) { + // Can't use strncasecmp, since we want to ensure that the full value matches + const char* p = *b; + const char* e = *b + len; + const char* v = value; + while (*v != '\0') { + if (p == e || tolower_ascii(*p) != *v) { // value is already lowercase + return false; + } + ++p; + ++v; + } + + *b = p; + return true; +} + +} // namespace + +Expected str_to_bool(StringPiece* src) noexcept { + auto b = src->begin(), e = src->end(); + for (;; ++b) { + if (b >= e) { + return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); + } + if ((*b < '\t' || *b > '\r') && *b != ' ') { + break; + } + } + + bool result; + auto len = size_t(e - b); + switch (*b) { + case '0': + case '1': { + result = false; + for (; b < e && isdigit(*b); ++b) { + if (result || (*b != '0' && *b != '1')) { + return makeUnexpected(ConversionCode::BOOL_OVERFLOW); + } + result = (*b == '1'); + } + break; + } + case 'y': + case 'Y': + result = true; + if (!bool_str_cmp(&b, len, "yes")) { + ++b; // accept the single 'y' character + } + break; + case 'n': + case 'N': + result = false; + if (!bool_str_cmp(&b, len, "no")) { + ++b; + } + break; + case 't': + case 'T': + result = true; + if (!bool_str_cmp(&b, len, "true")) { + ++b; + } + break; + case 'f': + case 'F': + result = false; + if (!bool_str_cmp(&b, len, "false")) { + ++b; + } + break; + case 'o': + case 'O': + if (bool_str_cmp(&b, len, "on")) { + result = true; + } else if (bool_str_cmp(&b, len, "off")) { + result = false; + } else { + return makeUnexpected(ConversionCode::BOOL_INVALID_VALUE); + } + break; + default: + return makeUnexpected(ConversionCode::BOOL_INVALID_VALUE); + } + + src->assign(b, e); + + return result; +} + +/// Uses `double_conversion` library to convert from string to a floating +/// point. +template +Expected str_to_floating_double_conversion( + StringPiece* src) noexcept { + using namespace double_conversion; + static StringToDoubleConverter conv( + StringToDoubleConverter::ALLOW_TRAILING_JUNK | + StringToDoubleConverter::ALLOW_LEADING_SPACES, + 0.0, + // return this for junk input string + std::numeric_limits::quiet_NaN(), + nullptr, + nullptr); + + if (src->empty()) { + return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); + } + + int length; // processed char count + auto result = std::is_same::value + ? conv.StringToFloat(src->data(), static_cast(src->size()), &length) + : static_cast(conv.StringToDouble( + src->data(), static_cast(src->size()), &length)); + + if (!std::isnan(result)) { + // If we get here with length = 0, the input string is empty. + // If we get here with result = 0.0, it's either because the string + // contained only whitespace, or because we had an actual zero value + // (with potential trailing junk). If it was only whitespace, we + // want to raise an error; length will point past the last character + // that was processed, so we need to check if that character was + // whitespace or not. + if (length == 0 || + (result == 0.0 && std::isspace((*src)[size_t(length) - 1]))) { + return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); + } + if (length >= 2) { + const char* suffix = src->data() + length - 1; + // double_conversion doesn't update length correctly when there is an + // incomplete exponent specifier. Converting "12e-f-g" shouldn't consume + // any more than "12", but it will consume "12e-". + + // "123-" should only parse "123" + if (*suffix == '-' || *suffix == '+') { + --suffix; + --length; + } + // "12e-f-g" or "12euro" should only parse "12" + if (*suffix == 'e' || *suffix == 'E') { + --length; + } + } + src->advance(size_t(length)); + return Tgt(result); + } + + auto* e = src->end(); + auto* b = std::find_if_not(src->begin(), e, [](char c) { + return (c >= '\t' && c <= '\r') || c == ' '; + }); + if (b == e) { + return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); + } + auto size = size_t(e - b); + + bool negative = false; + if (*b == '-') { + negative = true; + ++b; + --size; + if (size == 0) { + return makeUnexpected(ConversionCode::STRING_TO_FLOAT_ERROR); + } + } + assert(size > 0); + + result = 0.0; + + switch (tolower_ascii(*b)) { + case 'i': + if (size >= 3 && tolower_ascii(b[1]) == 'n' && + tolower_ascii(b[2]) == 'f') { + if (size >= 8 && tolower_ascii(b[3]) == 'i' && + tolower_ascii(b[4]) == 'n' && tolower_ascii(b[5]) == 'i' && + tolower_ascii(b[6]) == 't' && tolower_ascii(b[7]) == 'y') { + b += 8; + } else { + b += 3; + } + result = std::numeric_limits::infinity(); + } + break; + + case 'n': + if (size >= 3 && tolower_ascii(b[1]) == 'a' && + tolower_ascii(b[2]) == 'n') { + b += 3; + result = std::numeric_limits::quiet_NaN(); + } + break; + + default: + break; + } + + if (result == 0.0) { + // All bets are off + return makeUnexpected(ConversionCode::STRING_TO_FLOAT_ERROR); + } + + if (negative) { + result = -result; + } + + src->assign(b, e); + + return Tgt(result); +} + +/// Uses `fast_float::from_chars` to convert from string to an integer. +template +Expected str_to_floating_fast_float_from_chars( + StringPiece* src) noexcept { + if (src->empty()) { + return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); + } + + // move through leading whitespace characters + auto* e = src->end(); + auto* b = std::find_if_not(src->begin(), e, [](char c) { + return (c >= '\t' && c <= '\r') || c == ' '; + }); + if (b == e) { + return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); + } + + Tgt result; + auto [ptr, ec] = fast_float::from_chars(b, e, result); + bool isOutOfRange{ec == std::errc::result_out_of_range}; + bool isOk{ec == std::errc()}; + if (!isOk && !isOutOfRange) { + return makeUnexpected(ConversionCode::STRING_TO_FLOAT_ERROR); + } + + auto numMatchedChars = ptr - src->data(); + src->advance(numMatchedChars); + + if (isOutOfRange) { + if (*b == '-') { + return -std::numeric_limits::infinity(); + } else { + return std::numeric_limits::infinity(); + } + } + + return result; +} + +template Expected +str_to_floating_fast_float_from_chars(StringPiece* src) noexcept; +template Expected +str_to_floating_fast_float_from_chars(StringPiece* src) noexcept; + +/** +* StringPiece to double, with progress information. Alters the +* StringPiece parameter to munch the already-parsed characters. +*/ +template +Expected str_to_floating(StringPiece* src) noexcept { +#if defined(FOLLY_CONV_ATOD_MODE) && FOLLY_CONV_ATOD_MODE == 1 + return detail::str_to_floating_fast_float_from_chars(src); +#else + return detail::str_to_floating_double_conversion(src); +#endif +} + +template Expected str_to_floating( + StringPiece* src) noexcept; +template Expected str_to_floating( + StringPiece* src) noexcept; + +namespace { + +/** +* This class takes care of additional processing needed for signed values, +* like leading sign character and overflow checks. +*/ +template > +class SignedValueHandler; + +template +class SignedValueHandler { +public: + ConversionCode init(const char*& b) { + negative_ = false; + if (!std::isdigit(*b)) { + if (*b == '-') { + negative_ = true; + } else if (FOLLY_UNLIKELY(*b != '+')) { + return ConversionCode::INVALID_LEADING_CHAR; + } + ++b; + } + return ConversionCode::SUCCESS; + } + + ConversionCode overflow() { + return negative_ ? ConversionCode::NEGATIVE_OVERFLOW + : ConversionCode::POSITIVE_OVERFLOW; + } + + template + Expected finalize(U value) { + T rv; + if (negative_) { + FOLLY_PUSH_WARNING + FOLLY_MSVC_DISABLE_WARNING(4146) + + // unary minus operator applied to unsigned type, result still unsigned + rv = T(-value); + + FOLLY_POP_WARNING + + if (FOLLY_UNLIKELY(rv > 0)) { + return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW); + } + } else { + rv = T(value); + if (FOLLY_UNLIKELY(rv < 0)) { + return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW); + } + } + return rv; + } + +private: + bool negative_; +}; + +// For unsigned types, we don't need any extra processing +template +class SignedValueHandler { +public: + ConversionCode init(const char*&) { return ConversionCode::SUCCESS; } + + ConversionCode overflow() { return ConversionCode::POSITIVE_OVERFLOW; } + + Expected finalize(T value) { return value; } +}; + +} // namespace + +/** +* String represented as a pair of pointers to char to signed/unsigned +* integrals. Assumes NO whitespace before or after, and also that the +* string is composed entirely of digits (and an optional sign only for +* signed types). String may be empty, in which case digits_to returns +* an appropriate error. +*/ +template +inline Expected digits_to( + const char* b, const char* const e) noexcept { + using UT = make_unsigned_t; + assert(b <= e); + + SignedValueHandler sgn; + + auto err = sgn.init(b); + if (FOLLY_UNLIKELY(err != ConversionCode::SUCCESS)) { + return makeUnexpected(err); + } + + auto size = size_t(e - b); + + /* Although the string is entirely made of digits, we still need to + * check for overflow. + */ + if (size > std::numeric_limits::digits10) { + // Leading zeros? + if (b < e && *b == '0') { + for (++b;; ++b) { + if (b == e) { + return Tgt(0); // just zeros, e.g. "0000" + } + if (*b != '0') { + size = size_t(e - b); + break; + } + } + } + if (size > std::numeric_limits::digits10 && + (size != std::numeric_limits::digits10 + 1 || + strncmp(b, MaxString::value, size) > 0)) { + return makeUnexpected(sgn.overflow()); + } + } + + // Here we know that the number won't overflow when + // converted. Proceed without checks. + + UT result = 0; + + for (; e - b >= 4; b += 4) { + result *= UT(10000); + const int32_t r0 = shift1000[static_cast(b[0])]; + const int32_t r1 = shift100[static_cast(b[1])]; + const int32_t r2 = shift10[static_cast(b[2])]; + const int32_t r3 = shift1[static_cast(b[3])]; + const auto sum = r0 + r1 + r2 + r3; + if (sum >= OOR) { + goto outOfRange; + } + result += UT(sum); + } + + switch (e - b) { + case 3: { + const int32_t r0 = shift100[static_cast(b[0])]; + const int32_t r1 = shift10[static_cast(b[1])]; + const int32_t r2 = shift1[static_cast(b[2])]; + const auto sum = r0 + r1 + r2; + if (sum >= OOR) { + goto outOfRange; + } + result = UT(1000 * result + sum); + break; + } + case 2: { + const int32_t r0 = shift10[static_cast(b[0])]; + const int32_t r1 = shift1[static_cast(b[1])]; + const auto sum = r0 + r1; + if (sum >= OOR) { + goto outOfRange; + } + result = UT(100 * result + sum); + break; + } + case 1: { + const int32_t sum = shift1[static_cast(b[0])]; + if (sum >= OOR) { + goto outOfRange; + } + result = UT(10 * result + sum); + break; + } + default: + assert(b == e); + if (size == 0) { + return makeUnexpected(ConversionCode::NO_DIGITS); + } + break; + } + + return sgn.finalize(result); + +outOfRange: + return makeUnexpected(ConversionCode::NON_DIGIT_CHAR); +} + +template Expected digits_to( + const char*, const char*) noexcept; +template Expected digits_to( + const char*, const char*) noexcept; +template Expected digits_to( + const char*, const char*) noexcept; + +template Expected digits_to( + const char*, const char*) noexcept; +template Expected digits_to( + const char*, const char*) noexcept; + +template Expected digits_to( + const char*, const char*) noexcept; +template Expected digits_to( + const char*, const char*) noexcept; + +template Expected digits_to( + const char*, const char*) noexcept; +template Expected digits_to( + const char*, const char*) noexcept; + +template Expected digits_to( + const char*, const char*) noexcept; +template Expected +digits_to(const char*, const char*) noexcept; + +#if FOLLY_HAVE_INT128_T +template Expected<__int128, ConversionCode> digits_to<__int128>( + const char*, const char*) noexcept; +template Expected +digits_to(const char*, const char*) noexcept; +#endif + +/** +* StringPiece to integrals, with progress information. Alters the +* StringPiece parameter to munch the already-parsed characters. +*/ +template +Expected str_to_integral(StringPiece* src) noexcept { + using UT = make_unsigned_t; + + auto b = src->data(), past = src->data() + src->size(); + + for (;; ++b) { + if (FOLLY_UNLIKELY(b >= past)) { + return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); + } + if ((*b < '\t' || *b > '\r') && *b != ' ') { + break; + } + } + + SignedValueHandler sgn; + auto err = sgn.init(b); + + if (FOLLY_UNLIKELY(err != ConversionCode::SUCCESS)) { + return makeUnexpected(err); + } + if (is_signed_v && FOLLY_UNLIKELY(b >= past)) { + return makeUnexpected(ConversionCode::NO_DIGITS); + } + if (FOLLY_UNLIKELY(!isdigit(*b))) { + return makeUnexpected(ConversionCode::NON_DIGIT_CHAR); + } + + auto m = findFirstNonDigit(b + 1, past); + + auto tmp = digits_to(b, m); + + if (FOLLY_UNLIKELY(!tmp.hasValue())) { + return makeUnexpected( + tmp.error() == ConversionCode::POSITIVE_OVERFLOW ? sgn.overflow() + : tmp.error()); + } + + auto res = sgn.finalize(tmp.value()); + + if (res.hasValue()) { + src->advance(size_t(m - src->data())); + } + + return res; +} + +template Expected str_to_integral( + StringPiece* src) noexcept; +template Expected str_to_integral( + StringPiece* src) noexcept; +template Expected str_to_integral( + StringPiece* src) noexcept; + +template Expected str_to_integral( + StringPiece* src) noexcept; +template Expected +str_to_integral(StringPiece* src) noexcept; + +template Expected str_to_integral( + StringPiece* src) noexcept; +template Expected str_to_integral( + StringPiece* src) noexcept; + +template Expected str_to_integral( + StringPiece* src) noexcept; +template Expected str_to_integral( + StringPiece* src) noexcept; + +template Expected str_to_integral( + StringPiece* src) noexcept; +template Expected +str_to_integral(StringPiece* src) noexcept; + +#if FOLLY_HAVE_INT128_T +template Expected<__int128, ConversionCode> str_to_integral<__int128>( + StringPiece* src) noexcept; +template Expected +str_to_integral(StringPiece* src) noexcept; +#endif + +#if defined(FOLLY_CONV_AVALIABILITY_TO_CHARS_FLOATING_POINT) && \ + FOLLY_CONV_AVALIABILITY_TO_CHARS_FLOATING_POINT == 1 +DtoaFlagsSet::DtoaFlagsSet(DtoaFlags flags) : flags_(flags) {} + +bool DtoaFlagsSet::isSet(DtoaFlags flag) const { + return (flags_ & flag) == flag; +} + +bool DtoaFlagsSet::emitPositiveExponentSign() const { + return isSet(DtoaFlags::EMIT_POSITIVE_EXPONENT_SIGN); +} + +bool DtoaFlagsSet::emitTrailingDecimalPoint() const { + return isSet(DtoaFlags::EMIT_TRAILING_DECIMAL_POINT); +} + +bool DtoaFlagsSet::emitTrailingZeroAfterPoint() const { + return isSet(DtoaFlags::EMIT_TRAILING_ZERO_AFTER_POINT); +} + +bool DtoaFlagsSet::uniqueZero() const { + return isSet(DtoaFlags::UNIQUE_ZERO); +} + +bool DtoaFlagsSet::noTrailingZero() const { + return isSet(DtoaFlags::NO_TRAILING_ZERO); +} + +int ParsedDecimal::numPrecisionFigures() const { + int numInts = 0; + + bool intIsZero = true; + int numLeadingIntZeros = 0; + bool isLeadingIntZero = true; + for (char* p = integerBegin; p && p != integerEnd; p++) { + if (*p == '0') { + if (isLeadingIntZero) { + numLeadingIntZeros += 1; + } else { + numInts += 1; + } + } else if (std::isdigit(*p)) { + intIsZero = false; + isLeadingIntZero = false; + numInts += 1; + } else { + folly::throw_exception("non-numeric int"); + } + } + + bool fractionalIsZero = true; + int numFractional = 0; + int numLeadingFractionalZeros = 0; + bool isLeadingFractionalZero = true; + for (char* p = fractionalBegin; p && p != fractionalEnd; p++) { + if (*p == '0') { + if (isLeadingFractionalZero) { + numLeadingFractionalZeros += 1; + } else { + numFractional += 1; + } + } else if (std::isdigit(*p)) { + fractionalIsZero = false; + isLeadingFractionalZero = false; + numFractional += 1; + } else { + folly::throw_exception("non-numeric frac"); + } + } + + if (intIsZero && fractionalIsZero) { + return numLeadingIntZeros + numLeadingFractionalZeros; + } else if (intIsZero) { + return numLeadingFractionalZeros + numFractional; + } else if (fractionalIsZero) { + return numInts + numLeadingFractionalZeros + numFractional; + } else { + return numInts + numLeadingFractionalZeros + numFractional; + } +} + +std::optional +ParsedDecimal::fractionalSuffix() const { + if (exponentSymbol) { + if (exponentEnd) { + return std::make_pair(exponentSymbol, exponentEnd); + } else if (exponentSign) { + return std::make_pair(exponentSymbol, exponentSign); + } else { + return std::make_pair(exponentSymbol, exponentSymbol + 1); + } + } else if (exponentSign) { + if (exponentEnd) { + return std::make_pair(exponentSign, exponentEnd); + } else { + return std::make_pair(exponentSign, exponentSign + 1); + } + } else if (exponentBegin) { + if (exponentEnd) { + return std::make_pair(exponentEnd, exponentEnd); + } else { + return std::make_pair(exponentBegin, exponentSign + 1); + } + } else { + return std::nullopt; + } +} + +void ParsedDecimal::shiftFractionalSuffixPtrs(size_t amount) { + if (exponentSymbol) { + exponentSymbol += amount; + } + if (exponentSign) { + exponentSign += amount; + } + if (exponentBegin) { + exponentBegin += amount; + } + if (exponentEnd) { + exponentEnd += amount; + } +} + +namespace { + +struct Stream : std::istream { + struct CharBuf : std::streambuf { + CharBuf(char* begin, char* end) { setg(begin, begin, end); } + + char* pos() const { return gptr(); } + }; + CharBuf& buf_; + + explicit Stream(CharBuf& buf) : std::istream(&buf), buf_(buf) {} + + char* pos() { return buf_.pos(); } + + void advance() { get(); } +}; + +} // namespace + +ParsedDecimal::ParsedDecimal(char* begin, char* end) { + if (!begin || !end || begin >= end) { + folly::throw_exception("invalid args"); + } + + Stream::CharBuf buf(begin, end); + Stream stream(buf); + if (stream.peek() == '-') { + negativeSign = stream.pos(); + stream.advance(); + } + + if (char c = static_cast(stream.peek()); std::isdigit(c)) { // [Windows - fix conversion from 'int' to 'char'] + integerBegin = stream.pos(); + + while (!stream.eof() && std::isdigit(stream.peek())) { + stream.advance(); + } + + integerEnd = stream.pos(); + } + + if (stream.eof()) { + if (!integerBegin) { + folly::throw_exception("no int part"); + } + + return; + } + + if (stream.peek() == '.') { + decimalPoint = stream.pos(); + stream.advance(); + } + + if (stream.eof()) { + if (!integerBegin) { + folly::throw_exception("no int part"); + } + return; + } + + if (char c = static_cast(stream.peek()); std::isdigit(c)) { // [Windows - fix conversion from 'int' to 'char'] + fractionalBegin = stream.pos(); + + while (!stream.eof() && std::isdigit(stream.peek())) { + stream.advance(); + } + + fractionalEnd = stream.pos(); + } + + if (!integerBegin && !fractionalBegin) { + // there was no integer or fractional part. + folly::throw_exception("no int or frac part"); + } + + if (stream.eof()) { + return; + } + + if (stream.peek() == 'e') { + exponentSymbol = stream.pos(); + stream.advance(); + + if (stream.eof()) { + return; + } + + if (char c = static_cast(stream.peek()); c == '-' || c == '+') { // [Windows - fix conversion from 'int' to 'char'] + exponentSign = stream.pos(); + stream.advance(); + } + + if (char c = static_cast(stream.peek()); std::isdigit(c)) { // [Windows - fix conversion from 'int' to 'char'] + exponentBegin = stream.pos(); + while (!stream.eof() && std::isdigit(stream.peek())) { + stream.advance(); + } + + exponentEnd = stream.pos(); + } + } + + while (!stream.eof()) { + int c = stream.get(); + if (c != '\0' && !std::isspace(c)) { + folly::throw_exception("unexpected chars"); + } + } +} + +std::pair formatAsDoubleConversion( + bool valueIsZero, + DtoaMode mode, + unsigned int numDigits, + DtoaFlags flags, + char* resultBegin, + char* resultEnd, + char* bufferEnd) { + detail::ParsedDecimal parsedDecimal(resultBegin, resultEnd); + detail::DtoaFlagsSet flagsSet{flags}; + if (parsedDecimal.negativeSign && flagsSet.uniqueZero() && valueIsZero) { + // skip the negative sign (-) if it's a zero and UNIQUE_ZERO is set + resultBegin += 1; + } + + unsigned int numTrailingZerosToAdd = 0; + if (!flagsSet.noTrailingZero() && mode == DtoaMode::PRECISION) { + // std::to_chars outputs no trailing zeros, so if it's not set, add + // trailing zeros + unsigned int numPrecisionFigures = parsedDecimal.numPrecisionFigures(); + if (numDigits > numPrecisionFigures) { + numTrailingZerosToAdd = numDigits - numPrecisionFigures; + } + } + + bool insertDecimalPoint = false; + char* insertionPoint; + if (parsedDecimal.fractionalEnd) { + insertionPoint = parsedDecimal.fractionalEnd; + } else if (parsedDecimal.decimalPoint) { + insertionPoint = parsedDecimal.decimalPoint + 1; + } else { + insertionPoint = parsedDecimal.integerEnd; + if (flagsSet.emitTrailingDecimalPoint() || numTrailingZerosToAdd > 0) { + insertDecimalPoint = true; + } + + if (flagsSet.emitTrailingZeroAfterPoint()) { + numTrailingZerosToAdd += 1; + } + } + + unsigned int numCharsToInsert = + numTrailingZerosToAdd + (insertDecimalPoint ? 1 : 0); + + if (numCharsToInsert > 0) { + if (resultEnd + numCharsToInsert > bufferEnd) { + folly::throw_exception("buffer too small"); + } + + std::optional fractionalsuffix = + parsedDecimal.fractionalSuffix(); + if (fractionalsuffix.has_value()) { + auto [fractionalSuffixBegin, fractionalSuffixEnd] = *fractionalsuffix; + std::memmove( + insertionPoint + numCharsToInsert, + fractionalSuffixBegin, + fractionalSuffixEnd - fractionalSuffixBegin); + parsedDecimal.shiftFractionalSuffixPtrs(numCharsToInsert); + } + + resultEnd += numCharsToInsert; + } + + if (insertDecimalPoint) { + *insertionPoint++ = '.'; + } + + while (numTrailingZerosToAdd) { + *insertionPoint++ = '0'; + numTrailingZerosToAdd -= 1; + } + + if (parsedDecimal.exponentSymbol) { + // std::tochars outputs a lowercase e and it needs to be uppercase. + *parsedDecimal.exponentSymbol = 'E'; + } + + size_t charsToRemove = 0; + char* removalBegin = nullptr; + if (!flagsSet.emitPositiveExponentSign() && parsedDecimal.exponentSign && + *parsedDecimal.exponentSign == '+') { + // std::to_chars outputs a + sign, remove it if the flag wasn't set. + // e.g., 1.23e+45 -> 1.23e45 + removalBegin = parsedDecimal.exponentSign; + charsToRemove += 1; + } + + if (char* p = parsedDecimal.exponentBegin; p && *p == '0') { + // std::to_chars outputs a leading zero, remove it to match + // double_conversion formating. e.g., 1.23e+04 -> 1.23e4 + if (!removalBegin) { + removalBegin = p; + } + + while (p != parsedDecimal.exponentEnd && *p == '0') { + charsToRemove += 1; + p += 1; + } + + if (p == parsedDecimal.exponentEnd) { + // they all were 0 digits. keep a single 0. + charsToRemove -= 1; + p -= 1; + if (p == removalBegin) { + // there was only one 0, keep it. + removalBegin = nullptr; + } + } + } + + if (charsToRemove && removalBegin) { + size_t len = resultEnd - (removalBegin + charsToRemove); + std::memmove(removalBegin, removalBegin + charsToRemove, len); + resultEnd -= charsToRemove; + } + + return std::pair{resultBegin, resultEnd}; +} +#endif // FOLLY_CONV_AVALIABILITY_TO_CHARS_FLOATING_POINT +} // namespace detail + +ConversionError makeConversionError(ConversionCode code, StringPiece input) { + using namespace detail; + static_assert( + std::is_unsigned::type>::value, + "ConversionCode should be unsigned"); + auto index = static_cast(code); + FOLLY_SAFE_CHECK(index < kErrorStrings.size(), "code=", uint64_t(index)); + const ErrorString& err = kErrorStrings[index]; + if (code == ConversionCode::EMPTY_INPUT_STRING && input.empty()) { + return {err.string, code}; + } + std::string tmp(err.string); + tmp.append(": "); + if (err.quote) { + tmp.append(1, '"'); + } + if (!input.empty()) { + tmp.append(input.data(), input.size()); + } + if (err.quote) { + tmp.append(1, '"'); + } + return {tmp, code}; +} + +} // namespace folly \ No newline at end of file diff --git a/vnext/Folly/TEMP_UntilFollyUpdate/chrono/Hardware.h b/vnext/Folly/TEMP_UntilFollyUpdate/chrono/Hardware.h new file mode 100644 index 00000000000..ff93f2d6c5f --- /dev/null +++ b/vnext/Folly/TEMP_UntilFollyUpdate/chrono/Hardware.h @@ -0,0 +1,155 @@ +/* +* Copyright (c) Meta Platforms, Inc. and affiliates. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#pragma once + +#include + +#include +#include + +// Description of and implementation of sequences for precise measurement: +// https://github.com/abseil/abseil-cpp/blob/20240116.2/absl/random/internal/nanobenchmark.cc#L164-L277 + +#if defined(_MSC_VER) && !defined(__clang__) && \ +(defined(_M_IX86) || defined(_M_X64)) && !defined(_M_ARM64EC) // [Windows] +extern "C" std::uint64_t __rdtsc(); +extern "C" std::uint64_t __rdtscp(unsigned int*); +extern "C" void _ReadWriteBarrier(); +extern "C" void _mm_lfence(); +#pragma intrinsic(__rdtsc) +#pragma intrinsic(__rdtscp) +#pragma intrinsic(_ReadWriteBarrier) +#pragma intrinsic(_mm_lfence) +#endif + +namespace folly { + +inline std::uint64_t hardware_timestamp() { +#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) + return __rdtsc(); +#elif defined(__GNUC__) && (defined(__i386__) || FOLLY_X64) + return __builtin_ia32_rdtsc(); +#elif FOLLY_AARCH64 && !FOLLY_MOBILE + uint64_t cval; + asm volatile("mrs %0, cntvct_el0" : "=r"(cval)); + return cval; +#else + // use steady_clock::now() as an approximation for the timestamp counter on + // non-x86 systems + return std::chrono::steady_clock::now().time_since_epoch().count(); +#endif +} + +/// hardware_timestamp_measurement_start +/// hardware_timestamp_measurement_stop +/// +/// Suitable for beginning precise measurement of a region of code. +/// +/// Prevents the compiler from reordering instructions across the call. This is +/// in contrast with hardware_timestamp(), which may not prevent the compiler +/// from reordering instructions across the call. +/// +/// Prevents the processor from pipelining the call with loads. This is in +/// contrast with hardware_timestamp(), which allows the processor to pipeline +/// the call with surrounding loads. +/// +/// Does not prevent instruction pipelines from continuing execution across the +/// call. For example, does not prevent a store issued before the call from +/// continuing execution (becoming globally visible) across the call. This means +/// that this form may not be suitable for certain measurement use-cases which +/// would require such prevention. However, this would be suitable for typical +/// measurement use-cases which do not specifically need to measure background +/// work such as store execution. +std::uint64_t hardware_timestamp_measurement_start() noexcept; +std::uint64_t hardware_timestamp_measurement_stop() noexcept; + +inline std::uint64_t hardware_timestamp_measurement_start() noexcept { +#if defined(_MSC_VER) && !defined(__clang__) && \ + (defined(_M_IX86) || defined(_M_X64)) + // msvc does not have embedded assembly + _ReadWriteBarrier(); + _mm_lfence(); + _ReadWriteBarrier(); + auto const ret = __rdtsc(); + _ReadWriteBarrier(); + _mm_lfence(); + _ReadWriteBarrier(); + return ret; +#elif defined(__GNUC__) && FOLLY_X64 + uint64_t ret = 0; +#if !defined(__clang_major__) || __clang_major__ >= 11 + asm volatile inline( +#else + asm volatile( +#endif + "lfence\n" + "rdtsc\n" // loads 64-bit tsc into edx:eax + "shl $32, %%rdx\n" // prep rdx for combine into rax + "or %%rdx, %[ret]\n" // combine rdx into rax + "lfence\n" + : [ret] "=a"(ret) // bind ret to rax for output + : // no inputs + : "rdx", // rdtsc loads into edx:eax + "cc", // shl clobbers condition-code + "memory" // memory clobber asks gcc/clang not to reorder + ); + return ret; +#else + // use steady_clock::now() as an approximation for the timestamp counter on + // non-x64 systems + return std::chrono::steady_clock::now().time_since_epoch().count(); +#endif +} + +inline std::uint64_t hardware_timestamp_measurement_stop() noexcept { +#if defined(_MSC_VER) && !defined(__clang__) && \ + (defined(_M_IX86) || defined(_M_X64)) + // msvc does not have embedded assembly + _ReadWriteBarrier(); + unsigned int aux; + auto const ret = __rdtscp(&aux); + _ReadWriteBarrier(); + _mm_lfence(); + _ReadWriteBarrier(); + return ret; +#elif defined(__GNUC__) && FOLLY_X64 + uint64_t ret = 0; +#if !defined(__clang_major__) || __clang_major__ >= 11 + asm volatile inline( +#else + asm volatile( +#endif + "rdtscp\n" // loads 64-bit tsc into edx:eax, clobbers ecx + "shl $32, %%rdx\n" // prep rdx for combine into rax + "or %%rdx, %[ret]\n" // combine rdx into rax + "lfence\n" + : [ret] "=a"(ret) // bind ret to rax for output + : // no inputs + : "rdx", // rdtscp loads into edx:eax + "rcx", // rdtscp clobbers rcx + "cc", // shl clobbers condition-code + "memory" // memory clobber asks gcc/clang not to reorder + ); + return ret; +#else + // use steady_clock::now() as an approximation for the timestamp counter on + // non-x64 systems + return std::chrono::steady_clock::now().time_since_epoch().count(); +#endif +} + +} // namespace folly \ No newline at end of file diff --git a/vnext/Folly/TEMP_UntilFollyUpdate/concurrency/CacheLocality.cpp b/vnext/Folly/TEMP_UntilFollyUpdate/concurrency/CacheLocality.cpp new file mode 100644 index 00000000000..ab91cbab19e --- /dev/null +++ b/vnext/Folly/TEMP_UntilFollyUpdate/concurrency/CacheLocality.cpp @@ -0,0 +1,633 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifndef _MSC_VER +#define _GNU_SOURCE 1 // for RTLD_NOLOAD +#include +#endif +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace folly { + +///////////// CacheLocality + +/// Returns the CacheLocality information best for this machine +static CacheLocality getSystemLocalityInfo() { + if (kIsLinux) { + // First try to parse /proc/cpuinfo. + // If that fails, then try to parse /sys/devices/. + // The latter is slower but more accurate. + try { + return CacheLocality::readFromProcCpuinfo(); + } catch (...) { + // /proc/cpuinfo might be non-standard + // lets try with sysfs /sys/devices/cpu + } + + try { + return CacheLocality::readFromSysfs(); + } catch (...) { + // keep trying + } + } + + long numCpus = sysconf(_SC_NPROCESSORS_CONF); + if (numCpus <= 0) { + // This shouldn't happen, but if it does we should try to keep + // going. We are probably not going to be able to parse /sys on + // this box either (although we will try), which means we are going + // to fall back to the SequentialThreadId splitter. On my 16 core + // (x hyperthreading) dev box 16 stripes is enough to get pretty good + // contention avoidance with SequentialThreadId, and there is little + // improvement from going from 32 to 64. This default gives us some + // wiggle room + numCpus = 32; + } + return CacheLocality::uniform(size_t(numCpus)); +} + +template <> +const CacheLocality& CacheLocality::system() { + static std::atomic cache; + auto value = cache.load(std::memory_order_acquire); + if (value != nullptr) { + return *value; + } + auto next = new CacheLocality(getSystemLocalityInfo()); + if (cache.compare_exchange_strong(value, next, std::memory_order_acq_rel)) { + return *next; + } + delete next; + return *value; +} + +CacheLocality::CacheLocality(std::vector> equivClasses) { + numCpus = equivClasses.size(); + + for (size_t cpu = 0; cpu < numCpus; ++cpu) { + for (size_t level = 0; level < equivClasses[cpu].size(); ++level) { + if (equivClasses[cpu][level] == cpu) { + // we only want to count the equiv classes once, so we do it when we + // are processing their representative. + while (numCachesByLevel.size() <= level) { + numCachesByLevel.push_back(0); + } + numCachesByLevel[level]++; + } + } + } + + std::vector cpus(numCpus); + std::iota(cpus.begin(), cpus.end(), 0); + + std::sort(cpus.begin(), cpus.end(), [&](size_t lhs, size_t rhs) -> bool { + auto& lhsEquiv = equivClasses[lhs]; + auto& rhsEquiv = equivClasses[rhs]; + + // If different cpus have different numbers of caches group first by number + // of caches to guarantee strict weak ordering, even though the resulting + // order may be sub-optimal. + if (lhsEquiv.size() != rhsEquiv.size()) { + return lhsEquiv.size() < rhsEquiv.size(); + } + + // Order by equiv class of cache with highest index, direction doesn't + // matter. + for (size_t i = lhsEquiv.size(); i > 0; --i) { + auto idx = i - 1; + if (lhsEquiv[idx] != rhsEquiv[idx]) { + return lhsEquiv[idx] < rhsEquiv[idx]; + } + } + + // Break ties deterministically by cpu. + return lhs < rhs; + }); + + // The cpus are now sorted by locality, with neighboring entries closer + // to each other than entries that are far away. For striping we want + // the inverse map, since we are starting with the cpu. + localityIndexByCpu.resize(numCpus); + for (size_t i = 0; i < cpus.size(); ++i) { + localityIndexByCpu[cpus[i]] = i; + } + + equivClassesByCpu = std::move(equivClasses); +} + +// Each level of cache has sharing sets, which are the set of cpus that share a +// common cache at that level. These are available in a hex bitset form +// (/sys/devices/system/cpu/cpu0/cache/index0/shared_cpu_map, for example). +// They are also available in human-readable form in the shared_cpu_list file in +// the same directory. The list is a comma-separated list of numbers and +// ranges, where the ranges are pairs of decimal numbers separated by a '-'. +// +// To sort the cpus for optimum locality we don't really need to parse the +// sharing sets, we just need a unique representative from the equivalence +// class. The smallest value works fine, and happens to be the first decimal +// number in the file. We load all of the equivalence class information from +// all of the cpu*/index* directories, order the cpus first by increasing +// last-level cache equivalence class, then by the smaller caches. Finally, we +// break ties with the cpu number itself. + +/// Returns the first decimal number in the line, or throws an exception if the +/// line does not start with a number terminated by ',', '-', '\n', or EOS. +static size_t parseLeadingNumber(const std::string& line) { + auto raw = line.c_str(); + char* end; + unsigned long val = strtoul(raw, &end, 10); + if (end == raw || (*end != ',' && *end != '-' && *end != '\n' && *end != 0)) { + throw std::runtime_error(fmt::format("error parsing list '{}'", line)); + } + return val; +} + +CacheLocality CacheLocality::readFromSysfsTree( + const std::function& mapping) { + // the list of cache equivalence classes, where equivalence classes + // are named by the smallest cpu in the class + std::vector> equivClassesByCpu; + + for (size_t cpu = 0;; ++cpu) { + std::vector levels; + for (size_t index = 0;; ++index) { + auto dir = fmt::format( + "/sys/devices/system/cpu/cpu{}/cache/index{}/", cpu, index); + auto cacheType = mapping(dir + "type"); + auto equivStr = mapping(dir + "shared_cpu_list"); + if (cacheType.empty() || equivStr.empty()) { + // no more caches + break; + } + if (cacheType[0] == 'I') { + // cacheType in { "Data", "Instruction", "Unified" }. skip icache + continue; + } + auto equiv = parseLeadingNumber(equivStr); + levels.push_back(equiv); + } + + if (levels.empty()) { + // no levels at all for this cpu, we must be done + break; + } + equivClassesByCpu.emplace_back(std::move(levels)); + } + + if (equivClassesByCpu.empty()) { + throw std::runtime_error("unable to load cache sharing info"); + } + + return CacheLocality{std::move(equivClassesByCpu)}; +} + +CacheLocality CacheLocality::readFromSysfs() { + return readFromSysfsTree([](std::string const& name) { + std::ifstream xi(name.c_str()); + std::string rv; + std::getline(xi, rv); + return rv; + }); +} + +namespace { + +static bool procCpuinfoLineRelevant(std::string const& line) { + return line.size() > 4 && (line[0] == 'p' || line[0] == 'c'); +} + +std::vector> parseProcCpuinfoLines( + std::vector const& lines) { + std::vector> cpus; + size_t physicalId = 0; + size_t coreId = 0; + size_t maxCpu = 0; + size_t numberOfPhysicalIds = 0; + size_t numberOfCoreIds = 0; + for (auto iter = lines.rbegin(); iter != lines.rend(); ++iter) { + auto& line = *iter; + if (!procCpuinfoLineRelevant(line)) { + continue; + } + + auto sepIndex = line.find(':'); + if (sepIndex == std::string::npos || sepIndex + 2 > line.size()) { + continue; + } + auto arg = line.substr(sepIndex + 2); + + // "physical id" is socket, which is the most important locality + // context. "core id" is a real core, so two "processor" entries with + // the same physical id and core id are hyperthreads of each other. + // "processor" is the top line of each record, so when we hit it in + // the reverse order then we can emit a record. + if (line.find("physical id") == 0) { + physicalId = parseLeadingNumber(arg); + ++numberOfPhysicalIds; + } else if (line.find("core id") == 0) { + coreId = parseLeadingNumber(arg); + ++numberOfCoreIds; + } else if (line.find("processor") == 0) { + auto cpu = parseLeadingNumber(arg); + maxCpu = std::max(cpu, maxCpu); + cpus.emplace_back(physicalId, coreId, cpu); + } + } + + if (cpus.empty()) { + throw std::runtime_error("no CPUs parsed from /proc/cpuinfo"); + } + if (maxCpu != cpus.size() - 1) { + throw std::runtime_error( + "offline CPUs not supported for /proc/cpuinfo cache locality source"); + } + if (numberOfPhysicalIds == 0) { + throw std::runtime_error("no physical ids found"); + } + if (numberOfCoreIds == 0) { + throw std::runtime_error("no core ids found"); + } + + return cpus; +} + +} // namespace + +CacheLocality CacheLocality::readFromProcCpuinfoLines( + std::vector const& lines) { + // (physicalId, coreId, cpu) + std::vector> cpus = + parseProcCpuinfoLines(lines); + // Sort to make equivalence classes contiguous. + std::sort(cpus.begin(), cpus.end()); + + // We can't tell the real cache hierarchy from /proc/cpuinfo, but it works + // well enough to assume there are 3 levels, L1 and L2 per-core and L3 per + // socket. + std::vector> equivClassesByCpu(cpus.size()); + size_t l1Equiv = 0; + size_t l3Equiv = 0; + for (size_t i = 0; i < cpus.size(); ++i) { + auto [physicalId, coreId, cpu] = cpus[i]; + // The representative for each L1 and L3 equivalence class is the first cpu + // in the class. + if (i == 0 || physicalId != std::get<0>(cpus[i - 1]) || + coreId != std::get<1>(cpus[i - 1])) { + l1Equiv = cpu; + } + if (i == 0 || physicalId != std::get<0>(cpus[i - 1])) { + l3Equiv = cpu; + } + equivClassesByCpu[cpu] = {l1Equiv, l1Equiv, l3Equiv}; + } + + return CacheLocality{std::move(equivClassesByCpu)}; +} + +CacheLocality CacheLocality::readFromProcCpuinfo() { + std::vector lines; + { + std::ifstream xi("/proc/cpuinfo"); + if (xi.fail()) { + throw std::runtime_error("unable to open /proc/cpuinfo"); + } + char buf[8192]; + while (xi.good() && lines.size() < 20000) { + xi.getline(buf, sizeof(buf)); + std::string str(buf); + if (procCpuinfoLineRelevant(str)) { + lines.emplace_back(std::move(str)); + } + } + } + return readFromProcCpuinfoLines(lines); +} + +CacheLocality CacheLocality::uniform(size_t numCpus) { + // One cache shared by all cpus. + std::vector> equivClassesByCpu(numCpus, {0}); + return CacheLocality{std::move(equivClassesByCpu)}; +} + +////////////// Getcpu + +Getcpu::Func Getcpu::resolveVdsoFunc() { +#if !defined(FOLLY_HAVE_LINUX_VDSO) || defined(FOLLY_SANITIZE_MEMORY) + return nullptr; +#else + void* h = dlopen("linux-vdso.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); + if (h == nullptr) { + return nullptr; + } + + auto func = Getcpu::Func(dlsym(h, "__vdso_getcpu")); + if (func == nullptr) { + // technically a null result could either be a failure or a successful + // lookup of a symbol with the null value, but the second can't actually + // happen for this symbol. No point holding the handle forever if + // we don't need the code + dlclose(h); + } + + return func; +#endif +} + +/////////////// SequentialThreadId +unsigned SequentialThreadId::get() { + static std::atomic global{0}; + static thread_local unsigned local{0}; + return FOLLY_LIKELY(local) ? local : (local = ++global); +} + +/////////////// HashingThreadId +unsigned HashingThreadId::get() { + return hash::twang_32from64(getCurrentThreadID()); +} + +namespace detail { + +int AccessSpreaderBase::degenerateGetcpu(unsigned* cpu, unsigned* node, void*) { + if (cpu != nullptr) { + *cpu = 0; + } + if (node != nullptr) { + *node = 0; + } + return 0; +} + +struct AccessSpreaderStaticInit { + static AccessSpreaderStaticInit instance; + AccessSpreaderStaticInit() { (void)AccessSpreader<>::current(~size_t(0)); } +}; +AccessSpreaderStaticInit AccessSpreaderStaticInit::instance; + +bool AccessSpreaderBase::initialize( + GlobalState& state, + Getcpu::Func (&pickGetcpuFunc)(), + const CacheLocality& (&system)()) { + (void)AccessSpreaderStaticInit::instance; // ODR-use it so it is not dropped + constexpr auto relaxed = std::memory_order_relaxed; + auto& cacheLocality = system(); + auto n = cacheLocality.numCpus; + for (size_t width = 0; width <= kMaxCpus; ++width) { + auto& row = state.table[width]; + auto numStripes = std::max(size_t{1}, width); + for (size_t cpu = 0; cpu < kMaxCpus && cpu < n; ++cpu) { + auto index = cacheLocality.localityIndexByCpu[cpu]; + assert(index < n); + // as index goes from 0..n, post-transform value goes from + // 0..numStripes + make_atomic_ref(row[cpu]).store( + static_cast((index * numStripes) / n), relaxed); + assert(make_atomic_ref(row[cpu]).load(relaxed) < numStripes); + } + size_t filled = n; + while (filled < kMaxCpus) { + size_t len = std::min(filled, kMaxCpus - filled); + for (size_t i = 0; i < len; ++i) { + make_atomic_ref(row[filled + i]) + .store(make_atomic_ref(row[i]).load(relaxed), relaxed); + } + filled += len; + } + for (size_t cpu = n; cpu < kMaxCpus; ++cpu) { + assert( + make_atomic_ref(row[cpu]).load(relaxed) == + make_atomic_ref(row[cpu - n]).load(relaxed)); + } + } + state.getcpu.exchange(pickGetcpuFunc(), std::memory_order_acq_rel); + return true; +} + +} // namespace detail + +namespace { + +/** +* A simple freelist allocator. Allocates things of size sz, from slabs of size +* kAllocSize. Takes a lock on each allocation/deallocation. +*/ +class SimpleAllocator { +public: + // To support array aggregate initialization without an implicit constructor. + struct Ctor {}; + + SimpleAllocator(Ctor, size_t sz) : sz_(sz) {} + ~SimpleAllocator() { + std::lock_guard g(m_); + for (auto& block : blocks_) { + folly::aligned_free(block); + } + } + + void* allocate() { + std::lock_guard g(m_); + // Freelist allocation. + if (freelist_) { + auto mem = freelist_; + freelist_ = *static_cast(freelist_); + return mem; + } + + if (mem_) { + // Bump-ptr allocation. + if (intptr_t(mem_) % kMallocAlign == 0) { + // Avoid allocating pointers that may look like malloc + // pointers. + mem_ += std::min(sz_, max_align_v); + } + if (mem_ + sz_ <= end_) { + auto mem = mem_; + mem_ += sz_; + + assert(intptr_t(mem) % kMallocAlign != 0); + return mem; + } + } + + return allocateHard(); + } + + static void deallocate(void* ptr) { + assert(intptr_t(ptr) % kMallocAlign != 0); + // Find the allocator instance. + auto addr = + reinterpret_cast(intptr_t(ptr) & ~intptr_t(kAllocSize - 1)); + auto allocator = *static_cast(addr); + + std::lock_guard g(allocator->m_); + *static_cast(ptr) = allocator->freelist_; + if constexpr (kIsSanitizeAddress) { + // If running under ASAN, scrub the memory on deallocation, so we don't + // leave pointers that could hide leaks at shutdown, since the backing + // slabs may not be deallocated if the instance is a leaky singleton. + auto* base = static_cast(ptr); + std::fill( + base + sizeof(void*), base + allocator->sz_, static_cast(0)); + } + allocator->freelist_ = ptr; + } + + constexpr static size_t kMallocAlign = 128; + static_assert( + kMallocAlign % hardware_destructive_interference_size == 0, + "Large allocations should be cacheline-aligned"); + +private: + constexpr static size_t kAllocSize = 4096; + + void* allocateHard() { + // Allocate a new slab. + mem_ = static_cast(folly::aligned_malloc(kAllocSize, kAllocSize)); + if (!mem_) { + throw_exception(); + } + end_ = mem_ + kAllocSize; + blocks_.push_back(mem_); + + // Install a pointer to ourselves as the allocator. + *reinterpret_cast(mem_) = this; + static_assert( + max_align_v >= sizeof(SimpleAllocator*), "alignment too small"); + mem_ += std::min(sz_, max_align_v); + + // New allocation. + auto mem = mem_; + mem_ += sz_; + assert(intptr_t(mem) % kMallocAlign != 0); + return mem; + } + + std::mutex m_; + uint8_t* mem_{nullptr}; + uint8_t* end_{nullptr}; + void* freelist_{nullptr}; + size_t sz_; + std::vector blocks_; +}; + +class Allocator { +public: + void* allocate(size_t size) { + if (auto cl = sizeClass(size)) { + return allocators_[*cl].allocate(); + } + + // Fall back to malloc, returning a kMallocAlign-aligned allocation so it + // can be distinguished from SimpleAllocator allocations. + size = size + (SimpleAllocator::kMallocAlign - 1); + size &= ~size_t(SimpleAllocator::kMallocAlign - 1); + void* mem = aligned_malloc(size, SimpleAllocator::kMallocAlign); + if (!mem) { + throw_exception(); + } + return mem; + } + + static void deallocate(void* ptr) { + if (!ptr) { + return; + } + + // See if it came from SimpleAllocator or malloc. + if (intptr_t(ptr) % SimpleAllocator::kMallocAlign != 0) { + SimpleAllocator::deallocate(ptr); + } else { + aligned_free(ptr); + } + } + +private: + std::optional sizeClass(size_t size) { + if (size <= 8) { + return static_cast(0); // [Windows] + } else if (size <= 16) { + return static_cast(1); // [Windows] + } else if (size <= 32) { + return static_cast(2); // [Windows] + } else if (size <= 64) { + return static_cast(3); // [Windows] + } else { + return std::nullopt; + } + } + + std::array allocators_{ + {{SimpleAllocator::Ctor{}, 8}, + {SimpleAllocator::Ctor{}, 16}, + {SimpleAllocator::Ctor{}, 32}, + {SimpleAllocator::Ctor{}, 64}}}; +}; + +} // namespace + +void* coreMalloc(size_t size, size_t numStripes, size_t stripe) { + static folly::Indestructible + allocators[AccessSpreader<>::maxLocalityIndexValue()]; + auto index = AccessSpreader<>::localityIndexForStripe(numStripes, stripe); + return allocators[index]->allocate(size); +} + +void coreFree(void* ptr) { + Allocator::deallocate(ptr); +} + +namespace { +thread_local CoreAllocatorGuard* gCoreAllocatorGuard = nullptr; +} + +CoreAllocatorGuard::CoreAllocatorGuard(size_t numStripes, size_t stripe) + : numStripes_(numStripes), stripe_(stripe) { + CHECK(gCoreAllocatorGuard == nullptr) + << "CoreAllocator::Guard cannot be used recursively"; + gCoreAllocatorGuard = this; +} + +CoreAllocatorGuard::~CoreAllocatorGuard() { + gCoreAllocatorGuard = nullptr; +} + +namespace detail { + +void* coreMallocFromGuard(size_t size) { + CHECK(gCoreAllocatorGuard != nullptr) + << "CoreAllocator::allocator called without an active Guard"; + return coreMalloc( + size, gCoreAllocatorGuard->numStripes_, gCoreAllocatorGuard->stripe_); +} + +} // namespace detail + +} // namespace folly \ No newline at end of file diff --git a/vnext/Folly/TEMP_UntilFollyUpdate/dynamic-inl.h b/vnext/Folly/TEMP_UntilFollyUpdate/json/dynamic-inl.h similarity index 99% rename from vnext/Folly/TEMP_UntilFollyUpdate/dynamic-inl.h rename to vnext/Folly/TEMP_UntilFollyUpdate/json/dynamic-inl.h index 4495e2ac88b..ee000cdd7de 100644 --- a/vnext/Folly/TEMP_UntilFollyUpdate/dynamic-inl.h +++ b/vnext/Folly/TEMP_UntilFollyUpdate/json/dynamic-inl.h @@ -268,8 +268,6 @@ struct dynamic::const_item_iterator const_item_iterator() = default; /* implicit */ const_item_iterator(dynamic::ObjectImpl::const_iterator b) : Super(b) {} - /* implicit */ const_item_iterator(const_item_iterator const& i) - : Super(i.base()) {} /* implicit */ const_item_iterator(item_iterator i) : Super(i.base()) {} using object_type = dynamic::ObjectImpl const; @@ -389,7 +387,8 @@ dynamic::dynamic(T t) { } template -dynamic::dynamic(Iterator first, Iterator last) : type_(ARRAY) { +dynamic::dynamic(array_range_construct_t, Iterator first, Iterator last) + : type_(ARRAY) { new (&u_.array) Array(first, last); } @@ -1436,4 +1435,4 @@ class FormatValue> { } // namespace folly -#undef FB_DYNAMIC_APPLY +#undef FB_DYNAMIC_APPLY \ No newline at end of file diff --git a/vnext/Folly/TEMP_UntilFollyUpdate/json.cpp b/vnext/Folly/TEMP_UntilFollyUpdate/json/json.cpp similarity index 98% rename from vnext/Folly/TEMP_UntilFollyUpdate/json.cpp rename to vnext/Folly/TEMP_UntilFollyUpdate/json/json.cpp index e6a1718af01..b8696322386 100644 --- a/vnext/Folly/TEMP_UntilFollyUpdate/json.cpp +++ b/vnext/Folly/TEMP_UntilFollyUpdate/json/json.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include +#include #include #include @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -104,7 +103,7 @@ struct Printer { } void operator()(dynamic const& v, const Context* context) const { switch (v.type()) { - case dynamic::DOUBLE: + case dynamic::DOUBLE: { if (!opts_.allow_nan_inf) { if (std::isnan(v.asDouble())) { throw json::print_error( @@ -120,10 +119,11 @@ struct Printer { toAppend( v.asDouble(), &out_, - opts_.double_mode, + opts_.dtoa_mode, opts_.double_num_digits, - opts_.double_flags); + opts_.dtoa_flags); break; + } case dynamic::INT64: { auto intval = v.asInt(); if (opts_.javascript_safe) { @@ -409,9 +409,13 @@ struct Input { return range_.subpiece(0, 16 /* arbitrary */).toString(); } - [[noreturn]] dynamic error(char const* what) const { + [[noreturn]] void error(char const* what) const { throw json::make_parse_error(lineNum_, context(), what); } + template + R error(char const* what) const { + error(what); + } json::serialization_opts const& getOpts() { return opts_; } @@ -620,7 +624,7 @@ void decodeUnicodeEscape(Input& in, std::string& out) { c >= '0' && c <= '9' ? c - '0' : c >= 'a' && c <= 'f' ? c - 'a' + 10 : c >= 'A' && c <= 'F' ? c - 'A' + 10 : - (in.error("invalid hex digit"), 0)); + in.error("invalid hex digit")); // clang-format on }; @@ -737,7 +741,7 @@ dynamic parseValue(Input& in, json::metadata_map* map) { in.consume("NaN") ? (in.getOpts().parse_numbers_as_strings ? (dynamic)"NaN" : (dynamic)std::numeric_limits::quiet_NaN()) : - in.error("expected json value"); + in.error("expected json value"); // clang-format on } @@ -796,7 +800,7 @@ size_t firstEscapableInWord(T s, const serialization_opts& opts) { // times. However, for the case where 0 or a handful of bits are set, // looping will be minimal through use of findFirstSet. for (size_t i = 0, e = opts.extra_ascii_to_escape_bitmap.size(); i < e; - ++i) { + ++i) { const auto offset = i * 64; // Clear first 32 characters if this is the first index, since those are // always escaped. @@ -1111,4 +1115,4 @@ void PrintTo(const dynamic& dyn, std::ostream* os) { ////////////////////////////////////////////////////////////////////// -} // namespace folly +} // namespace folly \ No newline at end of file diff --git a/vnext/Folly/TEMP_UntilFollyUpdate/lang/SafeAssert.h b/vnext/Folly/TEMP_UntilFollyUpdate/lang/SafeAssert.h index d565dff810b..0d9ff21b55d 100644 --- a/vnext/Folly/TEMP_UntilFollyUpdate/lang/SafeAssert.h +++ b/vnext/Folly/TEMP_UntilFollyUpdate/lang/SafeAssert.h @@ -126,7 +126,7 @@ struct safe_assert_msg_types_one_fn { c operator()(char const*) const; c operator()(uint64_t) const; }; -FOLLY_INLINE_VARIABLE constexpr safe_assert_msg_types_one_fn +inline constexpr safe_assert_msg_types_one_fn safe_assert_msg_types_one{}; // a function object to prevent extensions template @@ -141,13 +141,6 @@ struct safe_assert_msg_types> { static constexpr value_type value = {{A..., safe_assert_msg_type::term}}; }; -#if FOLLY_CPLUSPLUS < 201703L -template -constexpr - typename safe_assert_msg_types>::value_type - safe_assert_msg_types>::value; -#endif - struct safe_assert_arg { char const* expr; char const* file; @@ -160,21 +153,21 @@ struct safe_assert_msg_cast_one_fn { FOLLY_ERASE auto operator()(char const* const a) const { return a; } FOLLY_ERASE auto operator()(uint64_t const a) const { return a; } }; -FOLLY_INLINE_VARIABLE constexpr safe_assert_msg_cast_one_fn +inline constexpr safe_assert_msg_cast_one_fn safe_assert_msg_cast_one{}; // a function object to prevent extensions template -[[noreturn]] FOLLY_COLD FOLLY_NOINLINE void safe_assert_terminate( +[[noreturn, FOLLY_ATTR_GNU_COLD]] FOLLY_NOINLINE void safe_assert_terminate( safe_assert_arg const* arg, ...) noexcept; // the true backing function // [Win - Fixes Error C2908 explicit specialization; 'void folly::detail::safe_assert_terminate(const folly::detail::safe_assert_arg *,...) noexcept' has already been instantiated template <> -[[noreturn]] FOLLY_COLD FOLLY_NOINLINE void safe_assert_terminate<0>( - safe_assert_arg const* arg, ...) noexcept; +[[noreturn, FOLLY_ATTR_GNU_COLD]] FOLLY_NOINLINE void safe_assert_terminate<0>( + safe_assert_arg const* arg, ...) noexcept; template <> -[[noreturn]] FOLLY_COLD FOLLY_NOINLINE void safe_assert_terminate<1>( - safe_assert_arg const* arg, ...) noexcept; +[[noreturn, FOLLY_ATTR_GNU_COLD]] FOLLY_NOINLINE void safe_assert_terminate<1>( + safe_assert_arg const* arg, ...) noexcept; // Win] template diff --git a/vnext/Folly/TEMP_UntilFollyUpdate/lang/ToAscii.h b/vnext/Folly/TEMP_UntilFollyUpdate/lang/ToAscii.h index bad7147bd1d..487a21a6e21 100644 --- a/vnext/Folly/TEMP_UntilFollyUpdate/lang/ToAscii.h +++ b/vnext/Folly/TEMP_UntilFollyUpdate/lang/ToAscii.h @@ -62,21 +62,21 @@ using to_ascii_alphabet_upper = to_ascii_alphabet; // and u8 at most 3. /* template -FOLLY_INLINE_VARIABLE constexpr size_t to_ascii_size_max = - detail::to_ascii_powers::size; +inline constexpr size_t to_ascii_size_max = + detail::to_ascii_powers::size; */ // to_ascii_size_max_decimal // // An alias to to_ascii_size_max<10>. template -FOLLY_INLINE_VARIABLE constexpr size_t to_ascii_size_max_decimal; +inline constexpr size_t to_ascii_size_max_decimal; template <> -FOLLY_INLINE_VARIABLE constexpr size_t to_ascii_size_max_decimal = 5; +inline constexpr size_t to_ascii_size_max_decimal = 5; template <> -FOLLY_INLINE_VARIABLE constexpr size_t to_ascii_size_max_decimal = 10; +inline constexpr size_t to_ascii_size_max_decimal = 10; template <> -FOLLY_INLINE_VARIABLE constexpr size_t to_ascii_size_max_decimal = 20; +inline constexpr size_t to_ascii_size_max_decimal = 20; namespace detail { diff --git a/vnext/Folly/cgmanifest.json b/vnext/Folly/cgmanifest.json index e87e1493ce2..10a2c5dd499 100644 --- a/vnext/Folly/cgmanifest.json +++ b/vnext/Folly/cgmanifest.json @@ -6,7 +6,17 @@ "Type": "git", "Git": { "RepositoryUrl": "https://github.com/facebook/folly", - "CommitHash": "234d39a36a43106747d10cc19efada72fd810dd3" + "CommitHash": "ad90720829db5ba0c3d0e44994856dcce33d7940" + } + }, + "DevelopmentDependency": false + }, + { + "Component": { + "Type": "git", + "Git": { + "RepositoryUrl": "https://github.com/fastfloat/fast_float", + "CommitHash": "ad90720829db5ba0c3d0e44994856dcce33d7940" } }, "DevelopmentDependency": false diff --git a/vnext/Microsoft.ReactNative/Base/FollyIncludes.h b/vnext/Microsoft.ReactNative/Base/FollyIncludes.h index 51d53c05e08..38daf68f03a 100644 --- a/vnext/Microsoft.ReactNative/Base/FollyIncludes.h +++ b/vnext/Microsoft.ReactNative/Base/FollyIncludes.h @@ -24,3 +24,4 @@ #include #include #include +#include diff --git a/vnext/Microsoft.ReactNative/ReactSupport.cpp b/vnext/Microsoft.ReactNative/ReactSupport.cpp index 7e0c7b39212..91d346e24d5 100644 --- a/vnext/Microsoft.ReactNative/ReactSupport.cpp +++ b/vnext/Microsoft.ReactNative/ReactSupport.cpp @@ -82,21 +82,30 @@ folly::dynamic ConvertToDynamic(IInspectable const &object) { case PropertyType::BooleanArray: { com_array tmpArray; propValue.GetBooleanArray(tmpArray); - folly::dynamic d(tmpArray.begin(), tmpArray.end()); + folly::dynamic d = folly::dynamic::array(); + for (bool b : tmpArray) { + d.push_back(folly::dynamic(b)); + } value = d; break; } case PropertyType::Char16Array: { com_array tmpArray; propValue.GetChar16Array(tmpArray); - folly::dynamic d(tmpArray.begin(), tmpArray.end()); + folly::dynamic d = folly::dynamic::array(); + for (char16_t b : tmpArray) { + d.push_back(folly::dynamic(b)); + } value = d; break; } case PropertyType::DoubleArray: { com_array tmpArray; propValue.GetDoubleArray(tmpArray); - folly::dynamic d(tmpArray.begin(), tmpArray.end()); + folly::dynamic d = folly::dynamic::array(); + for (double_t b : tmpArray) { + d.push_back(folly::dynamic(b)); + } value = d; break; } @@ -113,28 +122,40 @@ folly::dynamic ConvertToDynamic(IInspectable const &object) { case PropertyType::Int16Array: { com_array tmpArray; propValue.GetInt16Array(tmpArray); - folly::dynamic d(tmpArray.begin(), tmpArray.end()); + folly::dynamic d = folly::dynamic::array(); + for (int16_t b : tmpArray) { + d.push_back(folly::dynamic(b)); + } value = d; break; } case PropertyType::Int32Array: { com_array tmpArray; propValue.GetInt32Array(tmpArray); - folly::dynamic d(tmpArray.begin(), tmpArray.end()); + folly::dynamic d = folly::dynamic::array(); + for (int32_t b : tmpArray) { + d.push_back(folly::dynamic(b)); + } value = d; break; } case PropertyType::Int64Array: { com_array tmpArray; propValue.GetInt64Array(tmpArray); - folly::dynamic d(tmpArray.begin(), tmpArray.end()); + folly::dynamic d = folly::dynamic::array(); + for (int64_t b : tmpArray) { + d.push_back(folly::dynamic(b)); + } value = d; break; } case PropertyType::SingleArray: { com_array tmpArray; propValue.GetSingleArray(tmpArray); - folly::dynamic d(tmpArray.begin(), tmpArray.end()); + folly::dynamic d = folly::dynamic::array(); + for (float b : tmpArray) { + d.push_back(folly::dynamic(b)); + } value = d; break; } @@ -151,28 +172,40 @@ folly::dynamic ConvertToDynamic(IInspectable const &object) { case PropertyType::UInt8Array: { com_array tmpArray; propValue.GetUInt8Array(tmpArray); - folly::dynamic d(tmpArray.begin(), tmpArray.end()); + folly::dynamic d = folly::dynamic::array(); + for (uint8_t b : tmpArray) { + d.push_back(folly::dynamic(b)); + } value = d; break; } case PropertyType::UInt16Array: { com_array tmpArray; propValue.GetUInt16Array(tmpArray); - folly::dynamic d(tmpArray.begin(), tmpArray.end()); + folly::dynamic d = folly::dynamic::array(); + for (uint16_t b : tmpArray) { + d.push_back(folly::dynamic(b)); + } value = d; break; } case PropertyType::UInt32Array: { com_array tmpArray; propValue.GetUInt32Array(tmpArray); - folly::dynamic d(tmpArray.begin(), tmpArray.end()); + folly::dynamic d = folly::dynamic::array(); + for (uint32_t b : tmpArray) { + d.push_back(folly::dynamic(b)); + } value = d; break; } case PropertyType::UInt64Array: { com_array tmpArray; propValue.GetUInt64Array(tmpArray); - folly::dynamic d(tmpArray.begin(), tmpArray.end()); + folly::dynamic d = folly::dynamic::array(); + for (uint64_t b : tmpArray) { + d.push_back(folly::dynamic(b)); + } value = d; break; }