react-native-windows 0.78.4 → 0.78.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Directory.Build.props +6 -4
- package/Folly/Folly.vcxproj +46 -6
- package/Folly/Folly.vcxproj.filters +16 -4
- package/Folly/TEMP_UntilFollyUpdate/ConstexprMath.h +26 -18
- package/Folly/TEMP_UntilFollyUpdate/Conv.cpp +1205 -0
- package/Folly/TEMP_UntilFollyUpdate/chrono/Hardware.h +155 -0
- package/Folly/TEMP_UntilFollyUpdate/concurrency/CacheLocality.cpp +633 -0
- package/Folly/TEMP_UntilFollyUpdate/{dynamic-inl.h → json/dynamic-inl.h} +3 -4
- package/Folly/TEMP_UntilFollyUpdate/{json.cpp → json/json.cpp} +14 -10
- package/Folly/TEMP_UntilFollyUpdate/lang/SafeAssert.h +7 -14
- package/Folly/TEMP_UntilFollyUpdate/lang/ToAscii.h +6 -6
- package/Folly/ThreadNameStub.cpp +10 -0
- package/Folly/cgmanifest.json +11 -1
- package/Libraries/Components/View/View.windows.js +107 -56
- package/Libraries/Components/View/ViewAccessibility.d.ts +60 -1
- package/Libraries/Image/Image.windows.js +42 -21
- package/Libraries/Modal/Modal.d.ts +7 -0
- package/Libraries/Modal/Modal.windows.js +7 -1
- package/Libraries/NativeComponent/BaseViewConfig.windows.js +3 -0
- package/Libraries/Text/Text.d.ts +18 -0
- package/Microsoft.ReactNative/AsynchronousEventBeat.cpp +4 -25
- package/Microsoft.ReactNative/AsynchronousEventBeat.h +0 -3
- package/Microsoft.ReactNative/Base/FollyIncludes.h +1 -0
- package/Microsoft.ReactNative/CallInvoker.cpp +42 -0
- package/Microsoft.ReactNative/CallInvoker.h +34 -0
- package/Microsoft.ReactNative/{JSDispatcherWriter.cpp → CallInvokerWriter.cpp} +35 -47
- package/Microsoft.ReactNative/CallInvokerWriter.h +74 -0
- package/Microsoft.ReactNative/CompositionComponentView.idl +0 -5
- package/Microsoft.ReactNative/CompositionSwitcher.idl +7 -0
- package/Microsoft.ReactNative/Fabric/AbiViewProps.cpp +8 -10
- package/Microsoft.ReactNative/Fabric/ComponentView.cpp +4 -1
- package/Microsoft.ReactNative/Fabric/Composition/ActivityIndicatorComponentView.cpp +12 -2
- package/Microsoft.ReactNative/Fabric/Composition/ActivityIndicatorComponentView.h +2 -0
- package/Microsoft.ReactNative/Fabric/Composition/CompositionAnnotationProvider.cpp +100 -0
- package/Microsoft.ReactNative/Fabric/Composition/CompositionAnnotationProvider.h +31 -0
- package/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp +77 -11
- package/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp +43 -1
- package/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h +7 -0
- package/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp +86 -56
- package/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h +5 -1
- package/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp +0 -4
- package/Microsoft.ReactNative/Fabric/Composition/CompositionUIService.cpp +0 -2
- package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp +118 -63
- package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h +2 -0
- package/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp +133 -8
- package/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h +16 -2
- package/Microsoft.ReactNative/Fabric/Composition/FocusManager.cpp +4 -2
- package/Microsoft.ReactNative/Fabric/Composition/FocusManager.h +9 -1
- package/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp +34 -11
- package/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.h +3 -0
- package/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp +133 -135
- package/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp +9 -6
- package/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp +46 -49
- package/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h +6 -1
- package/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp +13 -8
- package/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h +5 -2
- package/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp +146 -25
- package/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.h +14 -0
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp +160 -12
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h +6 -0
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputEventEmitter.cpp +47 -0
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputEventEmitter.h +15 -1
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputProps.cpp +6 -2
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputProps.h +4 -1
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputShadowNode.cpp +7 -9
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputShadowNode.h +4 -1
- package/Microsoft.ReactNative/Fabric/Composition/Theme.cpp +5 -0
- package/Microsoft.ReactNative/Fabric/Composition/TooltipService.cpp +40 -36
- package/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.cpp +68 -0
- package/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.h +11 -0
- package/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp +70 -13
- package/Microsoft.ReactNative/Fabric/FabricUIManagerModule.h +10 -2
- package/Microsoft.ReactNative/Fabric/ImageManager.cpp +5 -5
- package/Microsoft.ReactNative/Fabric/ImageRequestParams.cpp +26 -0
- package/Microsoft.ReactNative/Fabric/WindowsImageManager.cpp +47 -8
- package/Microsoft.ReactNative/Fabric/WindowsImageManager.h +10 -1
- package/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/CompositionAccessibilityProps.h +67 -0
- package/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/HostPlatformViewEventEmitter.cpp +22 -4
- package/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/HostPlatformViewEventEmitter.h +15 -2
- package/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/HostPlatformViewProps.cpp +20 -0
- package/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/HostPlatformViewProps.h +5 -0
- package/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/MouseEvent.h +20 -0
- package/Microsoft.ReactNative/Fabric/platform/react/renderer/graphics/HostPlatformColor.h +5 -8
- package/Microsoft.ReactNative/Fabric/platform/react/renderer/graphics/PlatformColorParser.h +1 -2
- package/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/TextLayoutManager.cpp +247 -45
- package/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/TextLayoutManager.h +15 -0
- package/Microsoft.ReactNative/Fabric/platform/react/threading/MessageQueueThreadImpl.cpp +39 -0
- package/Microsoft.ReactNative/Fabric/platform/react/threading/MessageQueueThreadImpl.h +54 -0
- package/Microsoft.ReactNative/Fabric/platform/react/threading/TaskDispatchThread.cpp +126 -0
- package/Microsoft.ReactNative/Fabric/platform/react/threading/TaskDispatchThread.h +73 -0
- package/Microsoft.ReactNative/IReactContext.cpp +17 -0
- package/Microsoft.ReactNative/IReactContext.h +1 -0
- package/Microsoft.ReactNative/IReactContext.idl +18 -1
- package/Microsoft.ReactNative/IReactDispatcher.idl +1 -0
- package/Microsoft.ReactNative/IReactModuleBuilder.cpp +12 -0
- package/Microsoft.ReactNative/IReactModuleBuilder.h +2 -0
- package/Microsoft.ReactNative/IReactModuleBuilder.idl +8 -0
- package/Microsoft.ReactNative/JsiApi.cpp +10 -2
- package/Microsoft.ReactNative/JsiApi.h +1 -0
- package/Microsoft.ReactNative/JsiApi.idl +1 -0
- package/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj +0 -3
- package/Microsoft.ReactNative/Modules/AccessibilityInfoModule.cpp +2 -3
- package/Microsoft.ReactNative/Modules/AlertModule.cpp +7 -12
- package/Microsoft.ReactNative/Modules/Animated/AnimationDriver.cpp +2 -1
- package/Microsoft.ReactNative/Modules/Animated/NativeAnimatedModule.cpp +4 -8
- package/Microsoft.ReactNative/Modules/AppStateModule.cpp +2 -2
- package/Microsoft.ReactNative/Modules/ClipboardModule.cpp +6 -8
- package/Microsoft.ReactNative/Modules/ClipboardModule.h +1 -1
- package/Microsoft.ReactNative/Modules/ImageViewManagerModule.cpp +6 -15
- package/Microsoft.ReactNative/Modules/NativeUIManager.cpp +13 -24
- package/Microsoft.ReactNative/QuirkSettings.cpp +0 -16
- package/Microsoft.ReactNative/QuirkSettings.h +0 -3
- package/Microsoft.ReactNative/ReactHost/ReactHost.cpp +11 -1
- package/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp +78 -68
- package/Microsoft.ReactNative/ReactHost/ReactInstanceWin.h +1 -2
- package/Microsoft.ReactNative/ReactInstanceSettings.cpp +12 -0
- package/Microsoft.ReactNative/ReactInstanceSettings.h +2 -0
- package/Microsoft.ReactNative/ReactInstanceSettings.idl +6 -0
- package/Microsoft.ReactNative/ReactNativeIsland.idl +3 -0
- package/Microsoft.ReactNative/ReactSupport.cpp +44 -11
- package/Microsoft.ReactNative/RedBox.cpp +30 -1
- package/Microsoft.ReactNative/SchedulerSettings.cpp +4 -4
- package/Microsoft.ReactNative/SchedulerSettings.h +1 -1
- package/Microsoft.ReactNative/TurboModulesProvider.cpp +30 -12
- package/Microsoft.ReactNative/Utils/ImageUtils.h +1 -0
- package/Microsoft.ReactNative/Utils/LocalBundleReader.cpp +37 -31
- package/Microsoft.ReactNative.Cxx/ApiLoaders/JSRuntimeApi.cpp +1 -0
- package/Microsoft.ReactNative.Cxx/ApiLoaders/JSRuntimeApi.inc +2 -0
- package/Microsoft.ReactNative.Cxx/ApiLoaders/NodeApi_posix.cpp +1 -1
- package/Microsoft.ReactNative.Cxx/JSI/JsiAbiApi.cpp +94 -27
- package/Microsoft.ReactNative.Cxx/JSI/JsiAbiApi.h +27 -6
- package/Microsoft.ReactNative.Cxx/JSI/JsiApiContext.cpp +45 -11
- package/Microsoft.ReactNative.Cxx/JSI/JsiApiContext.h +6 -0
- package/Microsoft.ReactNative.Cxx/JSI/decorator.h +220 -0
- package/Microsoft.ReactNative.Cxx/JSI/instrumentation.h +28 -0
- package/Microsoft.ReactNative.Cxx/JSI/jsi-inl.h +6 -0
- package/Microsoft.ReactNative.Cxx/JSI/jsi.cpp +241 -4
- package/Microsoft.ReactNative.Cxx/JSI/jsi.h +207 -19
- package/Microsoft.ReactNative.Cxx/JSValue.cpp +19 -3
- package/Microsoft.ReactNative.Cxx/JSValue.h +15 -7
- package/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems +2 -2
- package/Microsoft.ReactNative.Cxx/NativeModules.h +60 -2
- package/Microsoft.ReactNative.Cxx/NodeApiJsiRuntime.cpp +1267 -614
- package/Microsoft.ReactNative.Cxx/NodeApiJsiRuntime.h +4 -2
- package/Microsoft.ReactNative.Cxx/ReactContext.h +7 -0
- package/Microsoft.ReactNative.Cxx/TurboModuleProvider.cpp +11 -13
- package/Microsoft.ReactNative.Cxx/TurboModuleProvider.h +2 -3
- package/Microsoft.ReactNative.Cxx/node-api/js_native_api.h +81 -20
- package/Microsoft.ReactNative.Cxx/node-api/js_native_api_types.h +47 -2
- package/Microsoft.ReactNative.Cxx/node-api/js_runtime_api.h +13 -0
- package/Microsoft.ReactNative.Cxx/stubs/glog/logging.h +1 -1
- package/Microsoft.ReactNative.Managed/ReactContext.cs +3 -1
- package/PropertySheets/Generated/PackageVersion.g.props +3 -3
- package/PropertySheets/JSEngine.props +1 -1
- package/PropertySheets/React.Cpp.props +2 -1
- package/PropertySheets/WebView2.props +1 -1
- package/PropertySheets/WinUI.props +2 -2
- package/ReactCommon/TEMP_UntilReactCommonUpdate/jserrorhandler/JsErrorHandler.cpp +429 -0
- package/ReactCommon/cgmanifest.json +1 -1
- package/Shared/HermesRuntimeHolder.cpp +6 -0
- package/Shared/JSI/ChakraRuntime.cpp +4 -0
- package/Shared/JSI/ChakraRuntime.h +2 -0
- package/Shared/Modules/BlobModule.cpp +14 -16
- package/Shared/Modules/BlobModule.h +3 -1
- package/Shared/Networking/WinRTWebSocketResource.cpp +82 -101
- package/Shared/Networking/WinRTWebSocketResource.h +91 -7
- package/Shared/Shared.vcxitems +11 -7
- package/Shared/Shared.vcxitems.filters +6 -1
- package/Shared/TurboModuleManager.cpp +0 -15
- package/codegen/react/components/rnwcore/ActivityIndicatorView.g.h +6 -6
- package/codegen/react/components/rnwcore/AndroidDrawerLayout.g.h +6 -6
- package/codegen/react/components/rnwcore/AndroidHorizontalScrollContentView.g.h +6 -6
- package/codegen/react/components/rnwcore/AndroidProgressBar.g.h +6 -6
- package/codegen/react/components/rnwcore/AndroidSwipeRefreshLayout.g.h +6 -6
- package/codegen/react/components/rnwcore/AndroidSwitch.g.h +6 -6
- package/codegen/react/components/rnwcore/DebuggingOverlay.g.h +6 -6
- package/codegen/react/components/rnwcore/InputAccessory.g.h +6 -6
- package/codegen/react/components/rnwcore/ModalHostView.g.h +11 -7
- package/codegen/react/components/rnwcore/Props.cpp +2 -1
- package/codegen/react/components/rnwcore/Props.h +1 -0
- package/codegen/react/components/rnwcore/PullToRefreshView.g.h +6 -6
- package/codegen/react/components/rnwcore/SafeAreaView.g.h +6 -6
- package/codegen/react/components/rnwcore/Switch.g.h +6 -6
- package/codegen/react/components/rnwcore/UnimplementedNativeView.g.h +6 -6
- package/index.windows.js +4 -2
- package/package.json +3 -4
- package/src/private/specs/components/RCTModalHostViewNativeComponent.js +8 -0
- package/stubs/glog/logging.h +1 -1
- package/Microsoft.ReactNative/JSDispatcherWriter.h +0 -47
- package/Microsoft.ReactNative/SynchronousEventBeat.cpp +0 -51
- package/Microsoft.ReactNative/SynchronousEventBeat.h +0 -31
|
@@ -0,0 +1,1205 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
#include <folly/Conv.h>
|
|
18
|
+
|
|
19
|
+
#include <array>
|
|
20
|
+
#include <istream>
|
|
21
|
+
|
|
22
|
+
#include <folly/lang/SafeAssert.h>
|
|
23
|
+
|
|
24
|
+
#include <fast_float/fast_float.h> // @manual=fbsource//third-party/fast_float:fast_float
|
|
25
|
+
|
|
26
|
+
namespace folly {
|
|
27
|
+
namespace detail {
|
|
28
|
+
|
|
29
|
+
namespace {
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Finds the first non-digit in a string. The number of digits
|
|
33
|
+
* searched depends on the precision of the Tgt integral. Assumes the
|
|
34
|
+
* string starts with NO whitespace and NO sign.
|
|
35
|
+
*
|
|
36
|
+
* The semantics of the routine is:
|
|
37
|
+
* for (;; ++b) {
|
|
38
|
+
* if (b >= e || !isdigit(*b)) return b;
|
|
39
|
+
* }
|
|
40
|
+
*
|
|
41
|
+
* Complete unrolling marks bottom-line (i.e. entire conversion)
|
|
42
|
+
* improvements of 20%.
|
|
43
|
+
*/
|
|
44
|
+
inline const char* findFirstNonDigit(const char* b, const char* e) {
|
|
45
|
+
for (; b < e; ++b) {
|
|
46
|
+
auto const c = static_cast<unsigned>(*b) - '0';
|
|
47
|
+
if (c >= 10) {
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return b;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Maximum value of number when represented as a string
|
|
55
|
+
template <class T>
|
|
56
|
+
struct MaxString {
|
|
57
|
+
static const char* const value;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
template <>
|
|
61
|
+
const char* const MaxString<uint8_t>::value = "255";
|
|
62
|
+
template <>
|
|
63
|
+
const char* const MaxString<uint16_t>::value = "65535";
|
|
64
|
+
template <>
|
|
65
|
+
const char* const MaxString<uint32_t>::value = "4294967295";
|
|
66
|
+
#if __SIZEOF_LONG__ == 4
|
|
67
|
+
template <>
|
|
68
|
+
const char* const MaxString<unsigned long>::value = "4294967295";
|
|
69
|
+
#else
|
|
70
|
+
template <>
|
|
71
|
+
const char* const MaxString<unsigned long>::value = "18446744073709551615";
|
|
72
|
+
#endif
|
|
73
|
+
static_assert(
|
|
74
|
+
sizeof(unsigned long) >= 4,
|
|
75
|
+
"Wrong value for MaxString<unsigned long>::value,"
|
|
76
|
+
" please update.");
|
|
77
|
+
template <>
|
|
78
|
+
const char* const MaxString<unsigned long long>::value = "18446744073709551615";
|
|
79
|
+
static_assert(
|
|
80
|
+
sizeof(unsigned long long) >= 8,
|
|
81
|
+
"Wrong value for MaxString<unsigned long long>::value"
|
|
82
|
+
", please update.");
|
|
83
|
+
|
|
84
|
+
#if FOLLY_HAVE_INT128_T
|
|
85
|
+
template <>
|
|
86
|
+
const char* const MaxString<__uint128_t>::value =
|
|
87
|
+
"340282366920938463463374607431768211455";
|
|
88
|
+
#endif
|
|
89
|
+
|
|
90
|
+
/*
|
|
91
|
+
* Lookup tables that converts from a decimal character value to an integral
|
|
92
|
+
* binary value, shifted by a decimal "shift" multiplier.
|
|
93
|
+
* For all character values in the range '0'..'9', the table at those
|
|
94
|
+
* index locations returns the actual decimal value shifted by the multiplier.
|
|
95
|
+
* For all other values, the lookup table returns an invalid OOR value.
|
|
96
|
+
*/
|
|
97
|
+
// Out-of-range flag value, larger than the largest value that can fit in
|
|
98
|
+
// four decimal bytes (9999), but four of these added up together should
|
|
99
|
+
// still not overflow uint16_t.
|
|
100
|
+
constexpr int32_t OOR = 10000;
|
|
101
|
+
|
|
102
|
+
alignas(16) constexpr uint16_t shift1[] = {
|
|
103
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9
|
|
104
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10
|
|
105
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20
|
|
106
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30
|
|
107
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, 1, // 40
|
|
108
|
+
2, 3, 4, 5, 6, 7, 8, 9, OOR, OOR,
|
|
109
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60
|
|
110
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70
|
|
111
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80
|
|
112
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90
|
|
113
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100
|
|
114
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110
|
|
115
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120
|
|
116
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130
|
|
117
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140
|
|
118
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150
|
|
119
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160
|
|
120
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170
|
|
121
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180
|
|
122
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190
|
|
123
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200
|
|
124
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210
|
|
125
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220
|
|
126
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230
|
|
127
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240
|
|
128
|
+
OOR, OOR, OOR, OOR, OOR, OOR // 250
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
alignas(16) constexpr uint16_t shift10[] = {
|
|
132
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9
|
|
133
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10
|
|
134
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20
|
|
135
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30
|
|
136
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, 10, // 40
|
|
137
|
+
20, 30, 40, 50, 60, 70, 80, 90, OOR, OOR,
|
|
138
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60
|
|
139
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70
|
|
140
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80
|
|
141
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90
|
|
142
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100
|
|
143
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110
|
|
144
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120
|
|
145
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130
|
|
146
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140
|
|
147
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150
|
|
148
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160
|
|
149
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170
|
|
150
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180
|
|
151
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190
|
|
152
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200
|
|
153
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210
|
|
154
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220
|
|
155
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230
|
|
156
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240
|
|
157
|
+
OOR, OOR, OOR, OOR, OOR, OOR // 250
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
alignas(16) constexpr uint16_t shift100[] = {
|
|
161
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9
|
|
162
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10
|
|
163
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20
|
|
164
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30
|
|
165
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, 100, // 40
|
|
166
|
+
200, 300, 400, 500, 600, 700, 800, 900, OOR, OOR,
|
|
167
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60
|
|
168
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70
|
|
169
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80
|
|
170
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90
|
|
171
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100
|
|
172
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110
|
|
173
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120
|
|
174
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130
|
|
175
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140
|
|
176
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150
|
|
177
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160
|
|
178
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170
|
|
179
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180
|
|
180
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190
|
|
181
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200
|
|
182
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210
|
|
183
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220
|
|
184
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230
|
|
185
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240
|
|
186
|
+
OOR, OOR, OOR, OOR, OOR, OOR // 250
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
alignas(16) constexpr uint16_t shift1000[] = {
|
|
190
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9
|
|
191
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10
|
|
192
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20
|
|
193
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30
|
|
194
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, 1000, // 40
|
|
195
|
+
2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, OOR, OOR,
|
|
196
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60
|
|
197
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70
|
|
198
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80
|
|
199
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90
|
|
200
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100
|
|
201
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110
|
|
202
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120
|
|
203
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130
|
|
204
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140
|
|
205
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150
|
|
206
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160
|
|
207
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170
|
|
208
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180
|
|
209
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190
|
|
210
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200
|
|
211
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210
|
|
212
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220
|
|
213
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230
|
|
214
|
+
OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240
|
|
215
|
+
OOR, OOR, OOR, OOR, OOR, OOR // 250
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
struct ErrorString {
|
|
219
|
+
const char* string;
|
|
220
|
+
bool quote;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// Keep this in sync with ConversionCode in Conv.h
|
|
224
|
+
constexpr const std::array<
|
|
225
|
+
ErrorString,
|
|
226
|
+
static_cast<std::size_t>(ConversionCode::NUM_ERROR_CODES)>
|
|
227
|
+
kErrorStrings{{
|
|
228
|
+
{"Success", true},
|
|
229
|
+
{"Empty input string", true},
|
|
230
|
+
{"No digits found in input string", true},
|
|
231
|
+
{"Integer overflow when parsing bool (must be 0 or 1)", true},
|
|
232
|
+
{"Invalid value for bool", true},
|
|
233
|
+
{"Non-digit character found", true},
|
|
234
|
+
{"Invalid leading character", true},
|
|
235
|
+
{"Overflow during conversion", true},
|
|
236
|
+
{"Negative overflow during conversion", true},
|
|
237
|
+
{"Unable to convert string to floating point value", true},
|
|
238
|
+
{"Non-whitespace character found after end of conversion", true},
|
|
239
|
+
{"Overflow during arithmetic conversion", false},
|
|
240
|
+
{"Negative overflow during arithmetic conversion", false},
|
|
241
|
+
{"Loss of precision during arithmetic conversion", false},
|
|
242
|
+
}};
|
|
243
|
+
|
|
244
|
+
// Check if ASCII is really ASCII
|
|
245
|
+
using IsAscii =
|
|
246
|
+
std::bool_constant<'A' == 65 && 'Z' == 90 && 'a' == 97 && 'z' == 122>;
|
|
247
|
+
|
|
248
|
+
// The code in this file that uses tolower() really only cares about
|
|
249
|
+
// 7-bit ASCII characters, so we can take a nice shortcut here.
|
|
250
|
+
inline char tolower_ascii(char in) {
|
|
251
|
+
return IsAscii::value ? in | 0x20 : char(std::tolower(in));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
inline bool bool_str_cmp(const char** b, size_t len, const char* value) {
|
|
255
|
+
// Can't use strncasecmp, since we want to ensure that the full value matches
|
|
256
|
+
const char* p = *b;
|
|
257
|
+
const char* e = *b + len;
|
|
258
|
+
const char* v = value;
|
|
259
|
+
while (*v != '\0') {
|
|
260
|
+
if (p == e || tolower_ascii(*p) != *v) { // value is already lowercase
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
++p;
|
|
264
|
+
++v;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
*b = p;
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
} // namespace
|
|
272
|
+
|
|
273
|
+
Expected<bool, ConversionCode> str_to_bool(StringPiece* src) noexcept {
|
|
274
|
+
auto b = src->begin(), e = src->end();
|
|
275
|
+
for (;; ++b) {
|
|
276
|
+
if (b >= e) {
|
|
277
|
+
return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING);
|
|
278
|
+
}
|
|
279
|
+
if ((*b < '\t' || *b > '\r') && *b != ' ') {
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
bool result;
|
|
285
|
+
auto len = size_t(e - b);
|
|
286
|
+
switch (*b) {
|
|
287
|
+
case '0':
|
|
288
|
+
case '1': {
|
|
289
|
+
result = false;
|
|
290
|
+
for (; b < e && isdigit(*b); ++b) {
|
|
291
|
+
if (result || (*b != '0' && *b != '1')) {
|
|
292
|
+
return makeUnexpected(ConversionCode::BOOL_OVERFLOW);
|
|
293
|
+
}
|
|
294
|
+
result = (*b == '1');
|
|
295
|
+
}
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
case 'y':
|
|
299
|
+
case 'Y':
|
|
300
|
+
result = true;
|
|
301
|
+
if (!bool_str_cmp(&b, len, "yes")) {
|
|
302
|
+
++b; // accept the single 'y' character
|
|
303
|
+
}
|
|
304
|
+
break;
|
|
305
|
+
case 'n':
|
|
306
|
+
case 'N':
|
|
307
|
+
result = false;
|
|
308
|
+
if (!bool_str_cmp(&b, len, "no")) {
|
|
309
|
+
++b;
|
|
310
|
+
}
|
|
311
|
+
break;
|
|
312
|
+
case 't':
|
|
313
|
+
case 'T':
|
|
314
|
+
result = true;
|
|
315
|
+
if (!bool_str_cmp(&b, len, "true")) {
|
|
316
|
+
++b;
|
|
317
|
+
}
|
|
318
|
+
break;
|
|
319
|
+
case 'f':
|
|
320
|
+
case 'F':
|
|
321
|
+
result = false;
|
|
322
|
+
if (!bool_str_cmp(&b, len, "false")) {
|
|
323
|
+
++b;
|
|
324
|
+
}
|
|
325
|
+
break;
|
|
326
|
+
case 'o':
|
|
327
|
+
case 'O':
|
|
328
|
+
if (bool_str_cmp(&b, len, "on")) {
|
|
329
|
+
result = true;
|
|
330
|
+
} else if (bool_str_cmp(&b, len, "off")) {
|
|
331
|
+
result = false;
|
|
332
|
+
} else {
|
|
333
|
+
return makeUnexpected(ConversionCode::BOOL_INVALID_VALUE);
|
|
334
|
+
}
|
|
335
|
+
break;
|
|
336
|
+
default:
|
|
337
|
+
return makeUnexpected(ConversionCode::BOOL_INVALID_VALUE);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
src->assign(b, e);
|
|
341
|
+
|
|
342
|
+
return result;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/// Uses `double_conversion` library to convert from string to a floating
|
|
346
|
+
/// point.
|
|
347
|
+
template <class Tgt>
|
|
348
|
+
Expected<Tgt, ConversionCode> str_to_floating_double_conversion(
|
|
349
|
+
StringPiece* src) noexcept {
|
|
350
|
+
using namespace double_conversion;
|
|
351
|
+
static StringToDoubleConverter conv(
|
|
352
|
+
StringToDoubleConverter::ALLOW_TRAILING_JUNK |
|
|
353
|
+
StringToDoubleConverter::ALLOW_LEADING_SPACES,
|
|
354
|
+
0.0,
|
|
355
|
+
// return this for junk input string
|
|
356
|
+
std::numeric_limits<Tgt>::quiet_NaN(),
|
|
357
|
+
nullptr,
|
|
358
|
+
nullptr);
|
|
359
|
+
|
|
360
|
+
if (src->empty()) {
|
|
361
|
+
return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
int length; // processed char count
|
|
365
|
+
auto result = std::is_same<Tgt, float>::value
|
|
366
|
+
? conv.StringToFloat(src->data(), static_cast<int>(src->size()), &length)
|
|
367
|
+
: static_cast<Tgt>(conv.StringToDouble(
|
|
368
|
+
src->data(), static_cast<int>(src->size()), &length));
|
|
369
|
+
|
|
370
|
+
if (!std::isnan(result)) {
|
|
371
|
+
// If we get here with length = 0, the input string is empty.
|
|
372
|
+
// If we get here with result = 0.0, it's either because the string
|
|
373
|
+
// contained only whitespace, or because we had an actual zero value
|
|
374
|
+
// (with potential trailing junk). If it was only whitespace, we
|
|
375
|
+
// want to raise an error; length will point past the last character
|
|
376
|
+
// that was processed, so we need to check if that character was
|
|
377
|
+
// whitespace or not.
|
|
378
|
+
if (length == 0 ||
|
|
379
|
+
(result == 0.0 && std::isspace((*src)[size_t(length) - 1]))) {
|
|
380
|
+
return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING);
|
|
381
|
+
}
|
|
382
|
+
if (length >= 2) {
|
|
383
|
+
const char* suffix = src->data() + length - 1;
|
|
384
|
+
// double_conversion doesn't update length correctly when there is an
|
|
385
|
+
// incomplete exponent specifier. Converting "12e-f-g" shouldn't consume
|
|
386
|
+
// any more than "12", but it will consume "12e-".
|
|
387
|
+
|
|
388
|
+
// "123-" should only parse "123"
|
|
389
|
+
if (*suffix == '-' || *suffix == '+') {
|
|
390
|
+
--suffix;
|
|
391
|
+
--length;
|
|
392
|
+
}
|
|
393
|
+
// "12e-f-g" or "12euro" should only parse "12"
|
|
394
|
+
if (*suffix == 'e' || *suffix == 'E') {
|
|
395
|
+
--length;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
src->advance(size_t(length));
|
|
399
|
+
return Tgt(result);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
auto* e = src->end();
|
|
403
|
+
auto* b = std::find_if_not(src->begin(), e, [](char c) {
|
|
404
|
+
return (c >= '\t' && c <= '\r') || c == ' ';
|
|
405
|
+
});
|
|
406
|
+
if (b == e) {
|
|
407
|
+
return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING);
|
|
408
|
+
}
|
|
409
|
+
auto size = size_t(e - b);
|
|
410
|
+
|
|
411
|
+
bool negative = false;
|
|
412
|
+
if (*b == '-') {
|
|
413
|
+
negative = true;
|
|
414
|
+
++b;
|
|
415
|
+
--size;
|
|
416
|
+
if (size == 0) {
|
|
417
|
+
return makeUnexpected(ConversionCode::STRING_TO_FLOAT_ERROR);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
assert(size > 0);
|
|
421
|
+
|
|
422
|
+
result = 0.0;
|
|
423
|
+
|
|
424
|
+
switch (tolower_ascii(*b)) {
|
|
425
|
+
case 'i':
|
|
426
|
+
if (size >= 3 && tolower_ascii(b[1]) == 'n' &&
|
|
427
|
+
tolower_ascii(b[2]) == 'f') {
|
|
428
|
+
if (size >= 8 && tolower_ascii(b[3]) == 'i' &&
|
|
429
|
+
tolower_ascii(b[4]) == 'n' && tolower_ascii(b[5]) == 'i' &&
|
|
430
|
+
tolower_ascii(b[6]) == 't' && tolower_ascii(b[7]) == 'y') {
|
|
431
|
+
b += 8;
|
|
432
|
+
} else {
|
|
433
|
+
b += 3;
|
|
434
|
+
}
|
|
435
|
+
result = std::numeric_limits<Tgt>::infinity();
|
|
436
|
+
}
|
|
437
|
+
break;
|
|
438
|
+
|
|
439
|
+
case 'n':
|
|
440
|
+
if (size >= 3 && tolower_ascii(b[1]) == 'a' &&
|
|
441
|
+
tolower_ascii(b[2]) == 'n') {
|
|
442
|
+
b += 3;
|
|
443
|
+
result = std::numeric_limits<Tgt>::quiet_NaN();
|
|
444
|
+
}
|
|
445
|
+
break;
|
|
446
|
+
|
|
447
|
+
default:
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (result == 0.0) {
|
|
452
|
+
// All bets are off
|
|
453
|
+
return makeUnexpected(ConversionCode::STRING_TO_FLOAT_ERROR);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (negative) {
|
|
457
|
+
result = -result;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
src->assign(b, e);
|
|
461
|
+
|
|
462
|
+
return Tgt(result);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/// Uses `fast_float::from_chars` to convert from string to an integer.
|
|
466
|
+
template <class Tgt>
|
|
467
|
+
Expected<Tgt, ConversionCode> str_to_floating_fast_float_from_chars(
|
|
468
|
+
StringPiece* src) noexcept {
|
|
469
|
+
if (src->empty()) {
|
|
470
|
+
return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// move through leading whitespace characters
|
|
474
|
+
auto* e = src->end();
|
|
475
|
+
auto* b = std::find_if_not(src->begin(), e, [](char c) {
|
|
476
|
+
return (c >= '\t' && c <= '\r') || c == ' ';
|
|
477
|
+
});
|
|
478
|
+
if (b == e) {
|
|
479
|
+
return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
Tgt result;
|
|
483
|
+
auto [ptr, ec] = fast_float::from_chars(b, e, result);
|
|
484
|
+
bool isOutOfRange{ec == std::errc::result_out_of_range};
|
|
485
|
+
bool isOk{ec == std::errc()};
|
|
486
|
+
if (!isOk && !isOutOfRange) {
|
|
487
|
+
return makeUnexpected(ConversionCode::STRING_TO_FLOAT_ERROR);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
auto numMatchedChars = ptr - src->data();
|
|
491
|
+
src->advance(numMatchedChars);
|
|
492
|
+
|
|
493
|
+
if (isOutOfRange) {
|
|
494
|
+
if (*b == '-') {
|
|
495
|
+
return -std::numeric_limits<Tgt>::infinity();
|
|
496
|
+
} else {
|
|
497
|
+
return std::numeric_limits<Tgt>::infinity();
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
return result;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
template Expected<float, ConversionCode>
|
|
505
|
+
str_to_floating_fast_float_from_chars<float>(StringPiece* src) noexcept;
|
|
506
|
+
template Expected<double, ConversionCode>
|
|
507
|
+
str_to_floating_fast_float_from_chars<double>(StringPiece* src) noexcept;
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* StringPiece to double, with progress information. Alters the
|
|
511
|
+
* StringPiece parameter to munch the already-parsed characters.
|
|
512
|
+
*/
|
|
513
|
+
template <class Tgt>
|
|
514
|
+
Expected<Tgt, ConversionCode> str_to_floating(StringPiece* src) noexcept {
|
|
515
|
+
#if defined(FOLLY_CONV_ATOD_MODE) && FOLLY_CONV_ATOD_MODE == 1
|
|
516
|
+
return detail::str_to_floating_fast_float_from_chars<Tgt>(src);
|
|
517
|
+
#else
|
|
518
|
+
return detail::str_to_floating_double_conversion<Tgt>(src);
|
|
519
|
+
#endif
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
template Expected<float, ConversionCode> str_to_floating<float>(
|
|
523
|
+
StringPiece* src) noexcept;
|
|
524
|
+
template Expected<double, ConversionCode> str_to_floating<double>(
|
|
525
|
+
StringPiece* src) noexcept;
|
|
526
|
+
|
|
527
|
+
namespace {
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* This class takes care of additional processing needed for signed values,
|
|
531
|
+
* like leading sign character and overflow checks.
|
|
532
|
+
*/
|
|
533
|
+
template <typename T, bool IsSigned = is_signed_v<T>>
|
|
534
|
+
class SignedValueHandler;
|
|
535
|
+
|
|
536
|
+
template <typename T>
|
|
537
|
+
class SignedValueHandler<T, true> {
|
|
538
|
+
public:
|
|
539
|
+
ConversionCode init(const char*& b) {
|
|
540
|
+
negative_ = false;
|
|
541
|
+
if (!std::isdigit(*b)) {
|
|
542
|
+
if (*b == '-') {
|
|
543
|
+
negative_ = true;
|
|
544
|
+
} else if (FOLLY_UNLIKELY(*b != '+')) {
|
|
545
|
+
return ConversionCode::INVALID_LEADING_CHAR;
|
|
546
|
+
}
|
|
547
|
+
++b;
|
|
548
|
+
}
|
|
549
|
+
return ConversionCode::SUCCESS;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
ConversionCode overflow() {
|
|
553
|
+
return negative_ ? ConversionCode::NEGATIVE_OVERFLOW
|
|
554
|
+
: ConversionCode::POSITIVE_OVERFLOW;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
template <typename U>
|
|
558
|
+
Expected<T, ConversionCode> finalize(U value) {
|
|
559
|
+
T rv;
|
|
560
|
+
if (negative_) {
|
|
561
|
+
FOLLY_PUSH_WARNING
|
|
562
|
+
FOLLY_MSVC_DISABLE_WARNING(4146)
|
|
563
|
+
|
|
564
|
+
// unary minus operator applied to unsigned type, result still unsigned
|
|
565
|
+
rv = T(-value);
|
|
566
|
+
|
|
567
|
+
FOLLY_POP_WARNING
|
|
568
|
+
|
|
569
|
+
if (FOLLY_UNLIKELY(rv > 0)) {
|
|
570
|
+
return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);
|
|
571
|
+
}
|
|
572
|
+
} else {
|
|
573
|
+
rv = T(value);
|
|
574
|
+
if (FOLLY_UNLIKELY(rv < 0)) {
|
|
575
|
+
return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return rv;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
private:
|
|
582
|
+
bool negative_;
|
|
583
|
+
};
|
|
584
|
+
|
|
585
|
+
// For unsigned types, we don't need any extra processing
|
|
586
|
+
template <typename T>
|
|
587
|
+
class SignedValueHandler<T, false> {
|
|
588
|
+
public:
|
|
589
|
+
ConversionCode init(const char*&) { return ConversionCode::SUCCESS; }
|
|
590
|
+
|
|
591
|
+
ConversionCode overflow() { return ConversionCode::POSITIVE_OVERFLOW; }
|
|
592
|
+
|
|
593
|
+
Expected<T, ConversionCode> finalize(T value) { return value; }
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
} // namespace
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* String represented as a pair of pointers to char to signed/unsigned
|
|
600
|
+
* integrals. Assumes NO whitespace before or after, and also that the
|
|
601
|
+
* string is composed entirely of digits (and an optional sign only for
|
|
602
|
+
* signed types). String may be empty, in which case digits_to returns
|
|
603
|
+
* an appropriate error.
|
|
604
|
+
*/
|
|
605
|
+
template <class Tgt>
|
|
606
|
+
inline Expected<Tgt, ConversionCode> digits_to(
|
|
607
|
+
const char* b, const char* const e) noexcept {
|
|
608
|
+
using UT = make_unsigned_t<Tgt>;
|
|
609
|
+
assert(b <= e);
|
|
610
|
+
|
|
611
|
+
SignedValueHandler<Tgt> sgn;
|
|
612
|
+
|
|
613
|
+
auto err = sgn.init(b);
|
|
614
|
+
if (FOLLY_UNLIKELY(err != ConversionCode::SUCCESS)) {
|
|
615
|
+
return makeUnexpected(err);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
auto size = size_t(e - b);
|
|
619
|
+
|
|
620
|
+
/* Although the string is entirely made of digits, we still need to
|
|
621
|
+
* check for overflow.
|
|
622
|
+
*/
|
|
623
|
+
if (size > std::numeric_limits<UT>::digits10) {
|
|
624
|
+
// Leading zeros?
|
|
625
|
+
if (b < e && *b == '0') {
|
|
626
|
+
for (++b;; ++b) {
|
|
627
|
+
if (b == e) {
|
|
628
|
+
return Tgt(0); // just zeros, e.g. "0000"
|
|
629
|
+
}
|
|
630
|
+
if (*b != '0') {
|
|
631
|
+
size = size_t(e - b);
|
|
632
|
+
break;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
if (size > std::numeric_limits<UT>::digits10 &&
|
|
637
|
+
(size != std::numeric_limits<UT>::digits10 + 1 ||
|
|
638
|
+
strncmp(b, MaxString<UT>::value, size) > 0)) {
|
|
639
|
+
return makeUnexpected(sgn.overflow());
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// Here we know that the number won't overflow when
|
|
644
|
+
// converted. Proceed without checks.
|
|
645
|
+
|
|
646
|
+
UT result = 0;
|
|
647
|
+
|
|
648
|
+
for (; e - b >= 4; b += 4) {
|
|
649
|
+
result *= UT(10000);
|
|
650
|
+
const int32_t r0 = shift1000[static_cast<size_t>(b[0])];
|
|
651
|
+
const int32_t r1 = shift100[static_cast<size_t>(b[1])];
|
|
652
|
+
const int32_t r2 = shift10[static_cast<size_t>(b[2])];
|
|
653
|
+
const int32_t r3 = shift1[static_cast<size_t>(b[3])];
|
|
654
|
+
const auto sum = r0 + r1 + r2 + r3;
|
|
655
|
+
if (sum >= OOR) {
|
|
656
|
+
goto outOfRange;
|
|
657
|
+
}
|
|
658
|
+
result += UT(sum);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
switch (e - b) {
|
|
662
|
+
case 3: {
|
|
663
|
+
const int32_t r0 = shift100[static_cast<size_t>(b[0])];
|
|
664
|
+
const int32_t r1 = shift10[static_cast<size_t>(b[1])];
|
|
665
|
+
const int32_t r2 = shift1[static_cast<size_t>(b[2])];
|
|
666
|
+
const auto sum = r0 + r1 + r2;
|
|
667
|
+
if (sum >= OOR) {
|
|
668
|
+
goto outOfRange;
|
|
669
|
+
}
|
|
670
|
+
result = UT(1000 * result + sum);
|
|
671
|
+
break;
|
|
672
|
+
}
|
|
673
|
+
case 2: {
|
|
674
|
+
const int32_t r0 = shift10[static_cast<size_t>(b[0])];
|
|
675
|
+
const int32_t r1 = shift1[static_cast<size_t>(b[1])];
|
|
676
|
+
const auto sum = r0 + r1;
|
|
677
|
+
if (sum >= OOR) {
|
|
678
|
+
goto outOfRange;
|
|
679
|
+
}
|
|
680
|
+
result = UT(100 * result + sum);
|
|
681
|
+
break;
|
|
682
|
+
}
|
|
683
|
+
case 1: {
|
|
684
|
+
const int32_t sum = shift1[static_cast<size_t>(b[0])];
|
|
685
|
+
if (sum >= OOR) {
|
|
686
|
+
goto outOfRange;
|
|
687
|
+
}
|
|
688
|
+
result = UT(10 * result + sum);
|
|
689
|
+
break;
|
|
690
|
+
}
|
|
691
|
+
default:
|
|
692
|
+
assert(b == e);
|
|
693
|
+
if (size == 0) {
|
|
694
|
+
return makeUnexpected(ConversionCode::NO_DIGITS);
|
|
695
|
+
}
|
|
696
|
+
break;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
return sgn.finalize(result);
|
|
700
|
+
|
|
701
|
+
outOfRange:
|
|
702
|
+
return makeUnexpected(ConversionCode::NON_DIGIT_CHAR);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
template Expected<char, ConversionCode> digits_to<char>(
|
|
706
|
+
const char*, const char*) noexcept;
|
|
707
|
+
template Expected<signed char, ConversionCode> digits_to<signed char>(
|
|
708
|
+
const char*, const char*) noexcept;
|
|
709
|
+
template Expected<unsigned char, ConversionCode> digits_to<unsigned char>(
|
|
710
|
+
const char*, const char*) noexcept;
|
|
711
|
+
|
|
712
|
+
template Expected<short, ConversionCode> digits_to<short>(
|
|
713
|
+
const char*, const char*) noexcept;
|
|
714
|
+
template Expected<unsigned short, ConversionCode> digits_to<unsigned short>(
|
|
715
|
+
const char*, const char*) noexcept;
|
|
716
|
+
|
|
717
|
+
template Expected<int, ConversionCode> digits_to<int>(
|
|
718
|
+
const char*, const char*) noexcept;
|
|
719
|
+
template Expected<unsigned int, ConversionCode> digits_to<unsigned int>(
|
|
720
|
+
const char*, const char*) noexcept;
|
|
721
|
+
|
|
722
|
+
template Expected<long, ConversionCode> digits_to<long>(
|
|
723
|
+
const char*, const char*) noexcept;
|
|
724
|
+
template Expected<unsigned long, ConversionCode> digits_to<unsigned long>(
|
|
725
|
+
const char*, const char*) noexcept;
|
|
726
|
+
|
|
727
|
+
template Expected<long long, ConversionCode> digits_to<long long>(
|
|
728
|
+
const char*, const char*) noexcept;
|
|
729
|
+
template Expected<unsigned long long, ConversionCode>
|
|
730
|
+
digits_to<unsigned long long>(const char*, const char*) noexcept;
|
|
731
|
+
|
|
732
|
+
#if FOLLY_HAVE_INT128_T
|
|
733
|
+
template Expected<__int128, ConversionCode> digits_to<__int128>(
|
|
734
|
+
const char*, const char*) noexcept;
|
|
735
|
+
template Expected<unsigned __int128, ConversionCode>
|
|
736
|
+
digits_to<unsigned __int128>(const char*, const char*) noexcept;
|
|
737
|
+
#endif
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* StringPiece to integrals, with progress information. Alters the
|
|
741
|
+
* StringPiece parameter to munch the already-parsed characters.
|
|
742
|
+
*/
|
|
743
|
+
template <class Tgt>
|
|
744
|
+
Expected<Tgt, ConversionCode> str_to_integral(StringPiece* src) noexcept {
|
|
745
|
+
using UT = make_unsigned_t<Tgt>;
|
|
746
|
+
|
|
747
|
+
auto b = src->data(), past = src->data() + src->size();
|
|
748
|
+
|
|
749
|
+
for (;; ++b) {
|
|
750
|
+
if (FOLLY_UNLIKELY(b >= past)) {
|
|
751
|
+
return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING);
|
|
752
|
+
}
|
|
753
|
+
if ((*b < '\t' || *b > '\r') && *b != ' ') {
|
|
754
|
+
break;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
SignedValueHandler<Tgt> sgn;
|
|
759
|
+
auto err = sgn.init(b);
|
|
760
|
+
|
|
761
|
+
if (FOLLY_UNLIKELY(err != ConversionCode::SUCCESS)) {
|
|
762
|
+
return makeUnexpected(err);
|
|
763
|
+
}
|
|
764
|
+
if (is_signed_v<Tgt> && FOLLY_UNLIKELY(b >= past)) {
|
|
765
|
+
return makeUnexpected(ConversionCode::NO_DIGITS);
|
|
766
|
+
}
|
|
767
|
+
if (FOLLY_UNLIKELY(!isdigit(*b))) {
|
|
768
|
+
return makeUnexpected(ConversionCode::NON_DIGIT_CHAR);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
auto m = findFirstNonDigit(b + 1, past);
|
|
772
|
+
|
|
773
|
+
auto tmp = digits_to<UT>(b, m);
|
|
774
|
+
|
|
775
|
+
if (FOLLY_UNLIKELY(!tmp.hasValue())) {
|
|
776
|
+
return makeUnexpected(
|
|
777
|
+
tmp.error() == ConversionCode::POSITIVE_OVERFLOW ? sgn.overflow()
|
|
778
|
+
: tmp.error());
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
auto res = sgn.finalize(tmp.value());
|
|
782
|
+
|
|
783
|
+
if (res.hasValue()) {
|
|
784
|
+
src->advance(size_t(m - src->data()));
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
return res;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
template Expected<char, ConversionCode> str_to_integral<char>(
|
|
791
|
+
StringPiece* src) noexcept;
|
|
792
|
+
template Expected<signed char, ConversionCode> str_to_integral<signed char>(
|
|
793
|
+
StringPiece* src) noexcept;
|
|
794
|
+
template Expected<unsigned char, ConversionCode> str_to_integral<unsigned char>(
|
|
795
|
+
StringPiece* src) noexcept;
|
|
796
|
+
|
|
797
|
+
template Expected<short, ConversionCode> str_to_integral<short>(
|
|
798
|
+
StringPiece* src) noexcept;
|
|
799
|
+
template Expected<unsigned short, ConversionCode>
|
|
800
|
+
str_to_integral<unsigned short>(StringPiece* src) noexcept;
|
|
801
|
+
|
|
802
|
+
template Expected<int, ConversionCode> str_to_integral<int>(
|
|
803
|
+
StringPiece* src) noexcept;
|
|
804
|
+
template Expected<unsigned int, ConversionCode> str_to_integral<unsigned int>(
|
|
805
|
+
StringPiece* src) noexcept;
|
|
806
|
+
|
|
807
|
+
template Expected<long, ConversionCode> str_to_integral<long>(
|
|
808
|
+
StringPiece* src) noexcept;
|
|
809
|
+
template Expected<unsigned long, ConversionCode> str_to_integral<unsigned long>(
|
|
810
|
+
StringPiece* src) noexcept;
|
|
811
|
+
|
|
812
|
+
template Expected<long long, ConversionCode> str_to_integral<long long>(
|
|
813
|
+
StringPiece* src) noexcept;
|
|
814
|
+
template Expected<unsigned long long, ConversionCode>
|
|
815
|
+
str_to_integral<unsigned long long>(StringPiece* src) noexcept;
|
|
816
|
+
|
|
817
|
+
#if FOLLY_HAVE_INT128_T
|
|
818
|
+
template Expected<__int128, ConversionCode> str_to_integral<__int128>(
|
|
819
|
+
StringPiece* src) noexcept;
|
|
820
|
+
template Expected<unsigned __int128, ConversionCode>
|
|
821
|
+
str_to_integral<unsigned __int128>(StringPiece* src) noexcept;
|
|
822
|
+
#endif
|
|
823
|
+
|
|
824
|
+
#if defined(FOLLY_CONV_AVALIABILITY_TO_CHARS_FLOATING_POINT) && \
|
|
825
|
+
FOLLY_CONV_AVALIABILITY_TO_CHARS_FLOATING_POINT == 1
|
|
826
|
+
DtoaFlagsSet::DtoaFlagsSet(DtoaFlags flags) : flags_(flags) {}
|
|
827
|
+
|
|
828
|
+
bool DtoaFlagsSet::isSet(DtoaFlags flag) const {
|
|
829
|
+
return (flags_ & flag) == flag;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
bool DtoaFlagsSet::emitPositiveExponentSign() const {
|
|
833
|
+
return isSet(DtoaFlags::EMIT_POSITIVE_EXPONENT_SIGN);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
bool DtoaFlagsSet::emitTrailingDecimalPoint() const {
|
|
837
|
+
return isSet(DtoaFlags::EMIT_TRAILING_DECIMAL_POINT);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
bool DtoaFlagsSet::emitTrailingZeroAfterPoint() const {
|
|
841
|
+
return isSet(DtoaFlags::EMIT_TRAILING_ZERO_AFTER_POINT);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
bool DtoaFlagsSet::uniqueZero() const {
|
|
845
|
+
return isSet(DtoaFlags::UNIQUE_ZERO);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
bool DtoaFlagsSet::noTrailingZero() const {
|
|
849
|
+
return isSet(DtoaFlags::NO_TRAILING_ZERO);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
int ParsedDecimal::numPrecisionFigures() const {
|
|
853
|
+
int numInts = 0;
|
|
854
|
+
|
|
855
|
+
bool intIsZero = true;
|
|
856
|
+
int numLeadingIntZeros = 0;
|
|
857
|
+
bool isLeadingIntZero = true;
|
|
858
|
+
for (char* p = integerBegin; p && p != integerEnd; p++) {
|
|
859
|
+
if (*p == '0') {
|
|
860
|
+
if (isLeadingIntZero) {
|
|
861
|
+
numLeadingIntZeros += 1;
|
|
862
|
+
} else {
|
|
863
|
+
numInts += 1;
|
|
864
|
+
}
|
|
865
|
+
} else if (std::isdigit(*p)) {
|
|
866
|
+
intIsZero = false;
|
|
867
|
+
isLeadingIntZero = false;
|
|
868
|
+
numInts += 1;
|
|
869
|
+
} else {
|
|
870
|
+
folly::throw_exception<std::runtime_error>("non-numeric int");
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
bool fractionalIsZero = true;
|
|
875
|
+
int numFractional = 0;
|
|
876
|
+
int numLeadingFractionalZeros = 0;
|
|
877
|
+
bool isLeadingFractionalZero = true;
|
|
878
|
+
for (char* p = fractionalBegin; p && p != fractionalEnd; p++) {
|
|
879
|
+
if (*p == '0') {
|
|
880
|
+
if (isLeadingFractionalZero) {
|
|
881
|
+
numLeadingFractionalZeros += 1;
|
|
882
|
+
} else {
|
|
883
|
+
numFractional += 1;
|
|
884
|
+
}
|
|
885
|
+
} else if (std::isdigit(*p)) {
|
|
886
|
+
fractionalIsZero = false;
|
|
887
|
+
isLeadingFractionalZero = false;
|
|
888
|
+
numFractional += 1;
|
|
889
|
+
} else {
|
|
890
|
+
folly::throw_exception<std::runtime_error>("non-numeric frac");
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
if (intIsZero && fractionalIsZero) {
|
|
895
|
+
return numLeadingIntZeros + numLeadingFractionalZeros;
|
|
896
|
+
} else if (intIsZero) {
|
|
897
|
+
return numLeadingFractionalZeros + numFractional;
|
|
898
|
+
} else if (fractionalIsZero) {
|
|
899
|
+
return numInts + numLeadingFractionalZeros + numFractional;
|
|
900
|
+
} else {
|
|
901
|
+
return numInts + numLeadingFractionalZeros + numFractional;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
std::optional<detail::ParsedDecimal::FractionalSuffix>
|
|
906
|
+
ParsedDecimal::fractionalSuffix() const {
|
|
907
|
+
if (exponentSymbol) {
|
|
908
|
+
if (exponentEnd) {
|
|
909
|
+
return std::make_pair(exponentSymbol, exponentEnd);
|
|
910
|
+
} else if (exponentSign) {
|
|
911
|
+
return std::make_pair(exponentSymbol, exponentSign);
|
|
912
|
+
} else {
|
|
913
|
+
return std::make_pair(exponentSymbol, exponentSymbol + 1);
|
|
914
|
+
}
|
|
915
|
+
} else if (exponentSign) {
|
|
916
|
+
if (exponentEnd) {
|
|
917
|
+
return std::make_pair(exponentSign, exponentEnd);
|
|
918
|
+
} else {
|
|
919
|
+
return std::make_pair(exponentSign, exponentSign + 1);
|
|
920
|
+
}
|
|
921
|
+
} else if (exponentBegin) {
|
|
922
|
+
if (exponentEnd) {
|
|
923
|
+
return std::make_pair(exponentEnd, exponentEnd);
|
|
924
|
+
} else {
|
|
925
|
+
return std::make_pair(exponentBegin, exponentSign + 1);
|
|
926
|
+
}
|
|
927
|
+
} else {
|
|
928
|
+
return std::nullopt;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
void ParsedDecimal::shiftFractionalSuffixPtrs(size_t amount) {
|
|
933
|
+
if (exponentSymbol) {
|
|
934
|
+
exponentSymbol += amount;
|
|
935
|
+
}
|
|
936
|
+
if (exponentSign) {
|
|
937
|
+
exponentSign += amount;
|
|
938
|
+
}
|
|
939
|
+
if (exponentBegin) {
|
|
940
|
+
exponentBegin += amount;
|
|
941
|
+
}
|
|
942
|
+
if (exponentEnd) {
|
|
943
|
+
exponentEnd += amount;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
namespace {
|
|
948
|
+
|
|
949
|
+
struct Stream : std::istream {
|
|
950
|
+
struct CharBuf : std::streambuf {
|
|
951
|
+
CharBuf(char* begin, char* end) { setg(begin, begin, end); }
|
|
952
|
+
|
|
953
|
+
char* pos() const { return gptr(); }
|
|
954
|
+
};
|
|
955
|
+
CharBuf& buf_;
|
|
956
|
+
|
|
957
|
+
explicit Stream(CharBuf& buf) : std::istream(&buf), buf_(buf) {}
|
|
958
|
+
|
|
959
|
+
char* pos() { return buf_.pos(); }
|
|
960
|
+
|
|
961
|
+
void advance() { get(); }
|
|
962
|
+
};
|
|
963
|
+
|
|
964
|
+
} // namespace
|
|
965
|
+
|
|
966
|
+
ParsedDecimal::ParsedDecimal(char* begin, char* end) {
|
|
967
|
+
if (!begin || !end || begin >= end) {
|
|
968
|
+
folly::throw_exception<std::invalid_argument>("invalid args");
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
Stream::CharBuf buf(begin, end);
|
|
972
|
+
Stream stream(buf);
|
|
973
|
+
if (stream.peek() == '-') {
|
|
974
|
+
negativeSign = stream.pos();
|
|
975
|
+
stream.advance();
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
if (char c = static_cast<unsigned char>(stream.peek()); std::isdigit(c)) { // [Windows - fix conversion from 'int' to 'char']
|
|
979
|
+
integerBegin = stream.pos();
|
|
980
|
+
|
|
981
|
+
while (!stream.eof() && std::isdigit(stream.peek())) {
|
|
982
|
+
stream.advance();
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
integerEnd = stream.pos();
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
if (stream.eof()) {
|
|
989
|
+
if (!integerBegin) {
|
|
990
|
+
folly::throw_exception<std::invalid_argument>("no int part");
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
if (stream.peek() == '.') {
|
|
997
|
+
decimalPoint = stream.pos();
|
|
998
|
+
stream.advance();
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
if (stream.eof()) {
|
|
1002
|
+
if (!integerBegin) {
|
|
1003
|
+
folly::throw_exception<std::invalid_argument>("no int part");
|
|
1004
|
+
}
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
if (char c = static_cast<unsigned char>(stream.peek()); std::isdigit(c)) { // [Windows - fix conversion from 'int' to 'char']
|
|
1009
|
+
fractionalBegin = stream.pos();
|
|
1010
|
+
|
|
1011
|
+
while (!stream.eof() && std::isdigit(stream.peek())) {
|
|
1012
|
+
stream.advance();
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
fractionalEnd = stream.pos();
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
if (!integerBegin && !fractionalBegin) {
|
|
1019
|
+
// there was no integer or fractional part.
|
|
1020
|
+
folly::throw_exception<std::invalid_argument>("no int or frac part");
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
if (stream.eof()) {
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
if (stream.peek() == 'e') {
|
|
1028
|
+
exponentSymbol = stream.pos();
|
|
1029
|
+
stream.advance();
|
|
1030
|
+
|
|
1031
|
+
if (stream.eof()) {
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
if (char c = static_cast<unsigned char>(stream.peek()); c == '-' || c == '+') { // [Windows - fix conversion from 'int' to 'char']
|
|
1036
|
+
exponentSign = stream.pos();
|
|
1037
|
+
stream.advance();
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
if (char c = static_cast<unsigned char>(stream.peek()); std::isdigit(c)) { // [Windows - fix conversion from 'int' to 'char']
|
|
1041
|
+
exponentBegin = stream.pos();
|
|
1042
|
+
while (!stream.eof() && std::isdigit(stream.peek())) {
|
|
1043
|
+
stream.advance();
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
exponentEnd = stream.pos();
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
while (!stream.eof()) {
|
|
1051
|
+
int c = stream.get();
|
|
1052
|
+
if (c != '\0' && !std::isspace(c)) {
|
|
1053
|
+
folly::throw_exception<std::invalid_argument>("unexpected chars");
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
std::pair<char*, char*> formatAsDoubleConversion(
|
|
1059
|
+
bool valueIsZero,
|
|
1060
|
+
DtoaMode mode,
|
|
1061
|
+
unsigned int numDigits,
|
|
1062
|
+
DtoaFlags flags,
|
|
1063
|
+
char* resultBegin,
|
|
1064
|
+
char* resultEnd,
|
|
1065
|
+
char* bufferEnd) {
|
|
1066
|
+
detail::ParsedDecimal parsedDecimal(resultBegin, resultEnd);
|
|
1067
|
+
detail::DtoaFlagsSet flagsSet{flags};
|
|
1068
|
+
if (parsedDecimal.negativeSign && flagsSet.uniqueZero() && valueIsZero) {
|
|
1069
|
+
// skip the negative sign (-) if it's a zero and UNIQUE_ZERO is set
|
|
1070
|
+
resultBegin += 1;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
unsigned int numTrailingZerosToAdd = 0;
|
|
1074
|
+
if (!flagsSet.noTrailingZero() && mode == DtoaMode::PRECISION) {
|
|
1075
|
+
// std::to_chars outputs no trailing zeros, so if it's not set, add
|
|
1076
|
+
// trailing zeros
|
|
1077
|
+
unsigned int numPrecisionFigures = parsedDecimal.numPrecisionFigures();
|
|
1078
|
+
if (numDigits > numPrecisionFigures) {
|
|
1079
|
+
numTrailingZerosToAdd = numDigits - numPrecisionFigures;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
bool insertDecimalPoint = false;
|
|
1084
|
+
char* insertionPoint;
|
|
1085
|
+
if (parsedDecimal.fractionalEnd) {
|
|
1086
|
+
insertionPoint = parsedDecimal.fractionalEnd;
|
|
1087
|
+
} else if (parsedDecimal.decimalPoint) {
|
|
1088
|
+
insertionPoint = parsedDecimal.decimalPoint + 1;
|
|
1089
|
+
} else {
|
|
1090
|
+
insertionPoint = parsedDecimal.integerEnd;
|
|
1091
|
+
if (flagsSet.emitTrailingDecimalPoint() || numTrailingZerosToAdd > 0) {
|
|
1092
|
+
insertDecimalPoint = true;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
if (flagsSet.emitTrailingZeroAfterPoint()) {
|
|
1096
|
+
numTrailingZerosToAdd += 1;
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
unsigned int numCharsToInsert =
|
|
1101
|
+
numTrailingZerosToAdd + (insertDecimalPoint ? 1 : 0);
|
|
1102
|
+
|
|
1103
|
+
if (numCharsToInsert > 0) {
|
|
1104
|
+
if (resultEnd + numCharsToInsert > bufferEnd) {
|
|
1105
|
+
folly::throw_exception<std::invalid_argument>("buffer too small");
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
std::optional<detail::ParsedDecimal::FractionalSuffix> fractionalsuffix =
|
|
1109
|
+
parsedDecimal.fractionalSuffix();
|
|
1110
|
+
if (fractionalsuffix.has_value()) {
|
|
1111
|
+
auto [fractionalSuffixBegin, fractionalSuffixEnd] = *fractionalsuffix;
|
|
1112
|
+
std::memmove(
|
|
1113
|
+
insertionPoint + numCharsToInsert,
|
|
1114
|
+
fractionalSuffixBegin,
|
|
1115
|
+
fractionalSuffixEnd - fractionalSuffixBegin);
|
|
1116
|
+
parsedDecimal.shiftFractionalSuffixPtrs(numCharsToInsert);
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
resultEnd += numCharsToInsert;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
if (insertDecimalPoint) {
|
|
1123
|
+
*insertionPoint++ = '.';
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
while (numTrailingZerosToAdd) {
|
|
1127
|
+
*insertionPoint++ = '0';
|
|
1128
|
+
numTrailingZerosToAdd -= 1;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
if (parsedDecimal.exponentSymbol) {
|
|
1132
|
+
// std::tochars outputs a lowercase e and it needs to be uppercase.
|
|
1133
|
+
*parsedDecimal.exponentSymbol = 'E';
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
size_t charsToRemove = 0;
|
|
1137
|
+
char* removalBegin = nullptr;
|
|
1138
|
+
if (!flagsSet.emitPositiveExponentSign() && parsedDecimal.exponentSign &&
|
|
1139
|
+
*parsedDecimal.exponentSign == '+') {
|
|
1140
|
+
// std::to_chars outputs a + sign, remove it if the flag wasn't set.
|
|
1141
|
+
// e.g., 1.23e+45 -> 1.23e45
|
|
1142
|
+
removalBegin = parsedDecimal.exponentSign;
|
|
1143
|
+
charsToRemove += 1;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
if (char* p = parsedDecimal.exponentBegin; p && *p == '0') {
|
|
1147
|
+
// std::to_chars outputs a leading zero, remove it to match
|
|
1148
|
+
// double_conversion formating. e.g., 1.23e+04 -> 1.23e4
|
|
1149
|
+
if (!removalBegin) {
|
|
1150
|
+
removalBegin = p;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
while (p != parsedDecimal.exponentEnd && *p == '0') {
|
|
1154
|
+
charsToRemove += 1;
|
|
1155
|
+
p += 1;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
if (p == parsedDecimal.exponentEnd) {
|
|
1159
|
+
// they all were 0 digits. keep a single 0.
|
|
1160
|
+
charsToRemove -= 1;
|
|
1161
|
+
p -= 1;
|
|
1162
|
+
if (p == removalBegin) {
|
|
1163
|
+
// there was only one 0, keep it.
|
|
1164
|
+
removalBegin = nullptr;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
if (charsToRemove && removalBegin) {
|
|
1170
|
+
size_t len = resultEnd - (removalBegin + charsToRemove);
|
|
1171
|
+
std::memmove(removalBegin, removalBegin + charsToRemove, len);
|
|
1172
|
+
resultEnd -= charsToRemove;
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
return std::pair{resultBegin, resultEnd};
|
|
1176
|
+
}
|
|
1177
|
+
#endif // FOLLY_CONV_AVALIABILITY_TO_CHARS_FLOATING_POINT
|
|
1178
|
+
} // namespace detail
|
|
1179
|
+
|
|
1180
|
+
ConversionError makeConversionError(ConversionCode code, StringPiece input) {
|
|
1181
|
+
using namespace detail;
|
|
1182
|
+
static_assert(
|
|
1183
|
+
std::is_unsigned<std::underlying_type<ConversionCode>::type>::value,
|
|
1184
|
+
"ConversionCode should be unsigned");
|
|
1185
|
+
auto index = static_cast<std::size_t>(code);
|
|
1186
|
+
FOLLY_SAFE_CHECK(index < kErrorStrings.size(), "code=", uint64_t(index));
|
|
1187
|
+
const ErrorString& err = kErrorStrings[index];
|
|
1188
|
+
if (code == ConversionCode::EMPTY_INPUT_STRING && input.empty()) {
|
|
1189
|
+
return {err.string, code};
|
|
1190
|
+
}
|
|
1191
|
+
std::string tmp(err.string);
|
|
1192
|
+
tmp.append(": ");
|
|
1193
|
+
if (err.quote) {
|
|
1194
|
+
tmp.append(1, '"');
|
|
1195
|
+
}
|
|
1196
|
+
if (!input.empty()) {
|
|
1197
|
+
tmp.append(input.data(), input.size());
|
|
1198
|
+
}
|
|
1199
|
+
if (err.quote) {
|
|
1200
|
+
tmp.append(1, '"');
|
|
1201
|
+
}
|
|
1202
|
+
return {tmp, code};
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
} // namespace folly
|