react-native-windows 0.81.35 → 0.81.36
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/Microsoft.ReactNative/Fabric/Composition/TooltipService.cpp +48 -2
- package/Microsoft.ReactNative/Fabric/WindowsImageManager.cpp +40 -13
- package/PropertySheets/Generated/PackageVersion.g.props +3 -3
- package/Scripts/creaternwlib.cmd +2 -2
- package/external/fast-float/.syncignore +0 -1
- package/external/fast-float/README.md +545 -0
- package/external/fmt/.syncignore +0 -2
- package/external/fmt/README.md +484 -0
- package/external/fmt/doc/ChangeLog-old.md +3290 -0
- package/external/fmt/doc/api.md +650 -0
- package/external/fmt/doc/fmt.css +61 -0
- package/external/fmt/doc/fmt.js +4 -0
- package/external/fmt/doc/get-started.md +222 -0
- package/external/fmt/doc/index.md +151 -0
- package/external/fmt/doc/perf.svg +1 -0
- package/external/fmt/doc/python-license.txt +290 -0
- package/external/fmt/doc/syntax.md +887 -0
- package/external/folly/.syncignore +2 -3
- package/external/folly/README.md +263 -0
- package/external/folly/folly/docs/AtomicHashMap.md +134 -0
- package/external/folly/folly/docs/Benchmark.md +310 -0
- package/external/folly/folly/docs/Conv.md +291 -0
- package/external/folly/folly/docs/Dynamic.md +220 -0
- package/external/folly/folly/docs/DynamicConverter.md +71 -0
- package/external/folly/folly/docs/Executors.md +63 -0
- package/external/folly/folly/docs/FBString.md +46 -0
- package/external/folly/folly/docs/FBVector.md +200 -0
- package/external/folly/folly/docs/Fbvector--graphical_solutions.png +0 -0
- package/external/folly/folly/docs/Format.md +177 -0
- package/external/folly/folly/docs/Function.md +92 -0
- package/external/folly/folly/docs/Futures.md +263 -0
- package/external/folly/folly/docs/GroupVarint.md +46 -0
- package/external/folly/folly/docs/Histogram.md +104 -0
- package/external/folly/folly/docs/MapUtils.md +80 -0
- package/external/folly/folly/docs/Overview.md +263 -0
- package/external/folly/folly/docs/PackedSyncPtr.md +77 -0
- package/external/folly/folly/docs/Poly.md +500 -0
- package/external/folly/folly/docs/ProducerConsumerQueue.md +82 -0
- package/external/folly/folly/docs/Rcu.md +218 -0
- package/external/folly/folly/docs/SmallLocks.md +24 -0
- package/external/folly/folly/docs/Synchronized.md +793 -0
- package/external/folly/folly/docs/ThreadCachedInt.md +98 -0
- package/external/folly/folly/docs/ThreadLocal.md +105 -0
- package/external/folly/folly/docs/Traits.md +161 -0
- package/external/folly/folly/docs/small_vector.md +64 -0
- package/external/folly/folly/docs/style.css +23 -0
- package/external/folly/static/logo.svg +1 -0
- package/package.json +2 -2
- package/templates/cpp-lib/example/metro.config.js +31 -6
- package/Scripts/Warm-RnwFeedCache.ps1 +0 -413
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
`folly/Benchmark.h`
|
|
2
|
+
-----------------
|
|
3
|
+
|
|
4
|
+
`folly/Benchmark.h` provides a simple framework for writing and
|
|
5
|
+
executing benchmarks. Currently the framework targets only
|
|
6
|
+
single-threaded testing (though you can internally use fork-join
|
|
7
|
+
parallelism and measure total run time).
|
|
8
|
+
|
|
9
|
+
### Overview
|
|
10
|
+
***
|
|
11
|
+
|
|
12
|
+
Using `folly/Benchmark.h` is very simple. Here's an example:
|
|
13
|
+
|
|
14
|
+
``` Cpp
|
|
15
|
+
#include <folly/Benchmark.h>
|
|
16
|
+
#include <vector>
|
|
17
|
+
using namespace std;
|
|
18
|
+
using namespace folly;
|
|
19
|
+
BENCHMARK(insertFrontVector) {
|
|
20
|
+
// Let's insert 100 elements at the front of a vector
|
|
21
|
+
vector<int> v;
|
|
22
|
+
for (unsigned int i = 0; i < 100; ++i) {
|
|
23
|
+
v.insert(v.begin(), i);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
BENCHMARK(insertBackVector) {
|
|
27
|
+
// Let's insert 100 elements at the back of a vector
|
|
28
|
+
vector<int> v;
|
|
29
|
+
for (unsigned int i = 0; i < 100; ++i) {
|
|
30
|
+
v.insert(v.end(), i);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
int main() {
|
|
34
|
+
runBenchmarks();
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Compiling and running this code produces to the standard output:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
===============================================================================
|
|
42
|
+
test.cpp relative ns/iter iters/s
|
|
43
|
+
===============================================================================
|
|
44
|
+
insertFrontVector 3.84K 260.38K
|
|
45
|
+
insertBackVector 1.61K 622.75K
|
|
46
|
+
===============================================================================
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Let's worry about the empty column "relative" later. The table
|
|
50
|
+
contains, for each benchmark, the time spent per call and the converse
|
|
51
|
+
number of calls per second. Numbers are represented in metric notation
|
|
52
|
+
(K for thousands, M for millions etc). As expected, in this example
|
|
53
|
+
the second function is much faster (fewer ns/iter and more iters/s).
|
|
54
|
+
|
|
55
|
+
The macro `BENCHMARK` introduces a function and also adds it to an
|
|
56
|
+
internal array containing all benchmarks in the system. The defined
|
|
57
|
+
function takes no arguments and returns `void`.
|
|
58
|
+
|
|
59
|
+
The framework calls the function many times to collect statistics
|
|
60
|
+
about it. Sometimes the function itself would want to do that
|
|
61
|
+
iteration---for example how about inserting `n` elements instead of
|
|
62
|
+
100 elements? To do the iteration internally, use `BENCHMARK` with two
|
|
63
|
+
parameters. The second parameter is the number of iterations and is
|
|
64
|
+
passed by the framework down to the function. The type of the count is
|
|
65
|
+
implicitly `unsigned`. Consider a slightly reworked example:
|
|
66
|
+
|
|
67
|
+
``` Cpp
|
|
68
|
+
#include <folly/Benchmark.h>
|
|
69
|
+
#include <folly/container/Foreach.h>
|
|
70
|
+
#include <vector>
|
|
71
|
+
using namespace std;
|
|
72
|
+
using namespace folly;
|
|
73
|
+
BENCHMARK(insertFrontVector, n) {
|
|
74
|
+
vector<int> v;
|
|
75
|
+
for (unsigned int i = 0; i < n; ++i) {
|
|
76
|
+
v.insert(v.begin(), i);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
BENCHMARK(insertBackVector, n) {
|
|
80
|
+
vector<int> v;
|
|
81
|
+
for (unsigned int i = 0; i < n; ++i) {
|
|
82
|
+
v.insert(v.end(), i);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
int main() {
|
|
86
|
+
runBenchmarks();
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The produced numbers are substantially different:
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
===============================================================================
|
|
94
|
+
Benchmark relative ns/iter iters/s
|
|
95
|
+
===============================================================================
|
|
96
|
+
insertFrontVector 39.92 25.05M
|
|
97
|
+
insertBackVector 3.46 288.89M
|
|
98
|
+
===============================================================================
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Now the numbers indicate the speed of one single insertion because the
|
|
102
|
+
framework assumed the user-defined function used internal iteration
|
|
103
|
+
(which it does). So inserting at the back of a vector is more than 10
|
|
104
|
+
times faster than inserting at the front! Speaking of comparisons...
|
|
105
|
+
|
|
106
|
+
### Baselines
|
|
107
|
+
***
|
|
108
|
+
|
|
109
|
+
Choosing one or more good baselines is a crucial activity in any
|
|
110
|
+
measurement. Without a baseline there is little information to derive
|
|
111
|
+
from the sheer numbers. If, for example, you do experimentation with
|
|
112
|
+
algorithms, a good baseline is often an established approach (e.g. the
|
|
113
|
+
built-in `std::sort` for sorting). Essentially all experimental
|
|
114
|
+
numbers should be compared against some baseline.
|
|
115
|
+
|
|
116
|
+
To support baseline-driven measurements, `folly/Benchmark.h` defines
|
|
117
|
+
`BENCHMARK_RELATIVE`, which works much like `BENCHMARK`, except it
|
|
118
|
+
considers the most recent lexically-occurring `BENCHMARK` a baseline,
|
|
119
|
+
and fills the "relative" column. Say, for example, we want to use
|
|
120
|
+
front insertion for a vector as a baseline and see how back insertion
|
|
121
|
+
compares with it:
|
|
122
|
+
|
|
123
|
+
``` Cpp
|
|
124
|
+
#include <folly/Benchmark.h>
|
|
125
|
+
#include <folly/container/Foreach.h>
|
|
126
|
+
#include <vector>
|
|
127
|
+
using namespace std;
|
|
128
|
+
using namespace folly;
|
|
129
|
+
BENCHMARK(insertFrontVector, n) {
|
|
130
|
+
vector<int> v;
|
|
131
|
+
for (unsigned int i = 0; i < n; ++i) {
|
|
132
|
+
v.insert(v.begin(), i);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
BENCHMARK_RELATIVE(insertBackVector, n) {
|
|
136
|
+
vector<int> v;
|
|
137
|
+
for (unsigned int i = 0; i < n; ++i) {
|
|
138
|
+
v.insert(v.end(), i);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
int main() {
|
|
142
|
+
runBenchmarks();
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
This program prints something like:
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
===============================================================================
|
|
150
|
+
Benchmark relative ns/iter iters/s
|
|
151
|
+
===============================================================================
|
|
152
|
+
insertFrontVector 42.65 23.45M
|
|
153
|
+
insertBackVector 1208.24% 3.53 283.30M
|
|
154
|
+
===============================================================================
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
showing the 1208.24% relative speed advantage of inserting at the back
|
|
158
|
+
compared to front. The scale is chosen in such a way that 100% means
|
|
159
|
+
identical speed, numbers smaller than 100% indicate the benchmark is
|
|
160
|
+
slower than the baseline, and numbers greater than 100% indicate the
|
|
161
|
+
benchmark is faster. For example, if you see 42% that means the speed
|
|
162
|
+
of the benchmark is 0.42 of the baseline speed. If you see 123%, it
|
|
163
|
+
means the benchmark is 23% or 1.23 times faster.
|
|
164
|
+
|
|
165
|
+
To close the current benchmark group and start another, simply use
|
|
166
|
+
`BENCHMARK` again.
|
|
167
|
+
|
|
168
|
+
### Ars Gratia Artis
|
|
169
|
+
***
|
|
170
|
+
|
|
171
|
+
If you want to draw a horizontal line of dashes (e.g. at the end of a
|
|
172
|
+
group or for whatever reason), use `BENCHMARK_DRAW_LINE()`. The line
|
|
173
|
+
fulfills a purely aesthetic role; it doesn't interact with
|
|
174
|
+
measurements in any way.
|
|
175
|
+
|
|
176
|
+
``` Cpp
|
|
177
|
+
BENCHMARK(foo) {
|
|
178
|
+
Foo foo;
|
|
179
|
+
foo.doSomething();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
BENCHMARK_DRAW_LINE();
|
|
183
|
+
|
|
184
|
+
BENCHMARK(bar) {
|
|
185
|
+
Bar bar;
|
|
186
|
+
bar.doSomething();
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
If you want to print arbitrary text (e.g. a comment or for whatever
|
|
191
|
+
reason), use `BENCHMARK_DRAW_TEXT("your text here")`. The output will
|
|
192
|
+
be the literal value so escape codes like `\n` or `\t` will not work.
|
|
193
|
+
To add multiple lines of output use multiple `BENCHMARK_DRAW_TEXT`
|
|
194
|
+
calls.
|
|
195
|
+
|
|
196
|
+
``` Cpp
|
|
197
|
+
BENCHMARK(foo) {
|
|
198
|
+
Foo foo;
|
|
199
|
+
foo.doSomething();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
BENCHMARK_DRAW_TEXT("Note: bar is expected to be 2x more expensive.");
|
|
203
|
+
|
|
204
|
+
BENCHMARK(bar) {
|
|
205
|
+
Bar bar;
|
|
206
|
+
bar.doSomething();
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Suspending a benchmark
|
|
211
|
+
***
|
|
212
|
+
|
|
213
|
+
Sometimes benchmarking code must do some preparation work that is
|
|
214
|
+
physically inside the benchmark function, but should not take part to
|
|
215
|
+
its time budget. To temporarily suspend the benchmark, use the
|
|
216
|
+
pseudo-statement `BENCHMARK_SUSPEND` as follows:
|
|
217
|
+
|
|
218
|
+
``` Cpp
|
|
219
|
+
BENCHMARK(insertBackVector, n) {
|
|
220
|
+
vector<int> v;
|
|
221
|
+
BENCHMARK_SUSPEND {
|
|
222
|
+
v.reserve(n);
|
|
223
|
+
}
|
|
224
|
+
for (unsigned int i = 0; i < n; ++i) {
|
|
225
|
+
v.insert(v.end(), i);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
The preallocation effected with `v.reserve(n)` will not count toward
|
|
231
|
+
the total run time of the benchmark.
|
|
232
|
+
|
|
233
|
+
Only the main thread should call `BENCHMARK_SUSPEND` (and of course it
|
|
234
|
+
should not call it while other threads are doing actual work). This is
|
|
235
|
+
because the timer is application-global.
|
|
236
|
+
|
|
237
|
+
If the scope introduced by `BENCHMARK_SUSPEND` is not desired, you may
|
|
238
|
+
want to "manually" use the `BenchmarkSuspender` type. Constructing
|
|
239
|
+
such an object suspends time measurement, and destroying it resumes
|
|
240
|
+
the measurement. If you want to resume time measurement before the
|
|
241
|
+
destructor, call `dismiss` against the `BenchmarkSuspender`
|
|
242
|
+
object. The previous example could have been written like this:
|
|
243
|
+
|
|
244
|
+
``` Cpp
|
|
245
|
+
BENCHMARK(insertBackVector, n) {
|
|
246
|
+
BenchmarkSuspender braces;
|
|
247
|
+
vector<int> v;
|
|
248
|
+
v.reserve(n);
|
|
249
|
+
braces.dismiss();
|
|
250
|
+
for (unsigned int i = 0; i < n; ++i) {
|
|
251
|
+
v.insert(v.end(), i);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### `doNotOptimizeAway`
|
|
257
|
+
***
|
|
258
|
+
|
|
259
|
+
Finally, the small utility function `doNotOptimizeAway` prevents
|
|
260
|
+
compiler optimizations that may interfere with benchmarking . Call
|
|
261
|
+
doNotOptimizeAway(var) against variables that you use for
|
|
262
|
+
benchmarking but otherwise are useless. The compiler tends to do a
|
|
263
|
+
good job at eliminating unused variables, and this function fools it
|
|
264
|
+
into thinking a variable is in fact needed. Example:
|
|
265
|
+
|
|
266
|
+
``` Cpp
|
|
267
|
+
BENCHMARK(fpOps, n) {
|
|
268
|
+
double d = 1;
|
|
269
|
+
FOR_EACH_RANGE (i, 1, n) {
|
|
270
|
+
d += i;
|
|
271
|
+
d -= i;
|
|
272
|
+
d *= i;
|
|
273
|
+
d /= i;
|
|
274
|
+
}
|
|
275
|
+
doNotOptimizeAway(d);
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### A look under the hood
|
|
280
|
+
***
|
|
281
|
+
|
|
282
|
+
`folly/Benchmark.h` has a simple, systematic approach to collecting
|
|
283
|
+
timings.
|
|
284
|
+
|
|
285
|
+
First, it organizes measurements in several large epochs, and takes
|
|
286
|
+
the minimum over all epochs. Taking the minimum gives the closest
|
|
287
|
+
result to the real runtime. Benchmark timings are not a regular random
|
|
288
|
+
variable that fluctuates around an average. Instead, the real time
|
|
289
|
+
we're looking for is one to which there's a variety of additive noise
|
|
290
|
+
(i.e. there is no noise that could actually shorten the benchmark time
|
|
291
|
+
below its real value). In theory, taking an infinite amount of samples
|
|
292
|
+
and keeping the minimum is the actual time that needs
|
|
293
|
+
measuring. That's why the accuracy of benchmarking increases with the
|
|
294
|
+
number of epochs.
|
|
295
|
+
|
|
296
|
+
Clearly, in real functioning there will also be noise and a variety of
|
|
297
|
+
effects caused by the running context. But the noise during the
|
|
298
|
+
benchmark (straight setup, simple looping) is a poor model for the
|
|
299
|
+
noise in the real application. So taking the minimum across several
|
|
300
|
+
epochs is the most informative result.
|
|
301
|
+
|
|
302
|
+
Inside each epoch, the function measured is iterated an increasing
|
|
303
|
+
number of times until the total runtime is large enough to make noise
|
|
304
|
+
negligible. At that point the time is collected, and the time per
|
|
305
|
+
iteration is computed. As mentioned, the minimum time per iteration
|
|
306
|
+
over all epochs is the final result.
|
|
307
|
+
|
|
308
|
+
The timer function used is `clock_gettime` with the `CLOCK_REALTIME`
|
|
309
|
+
clock id. Note that you must use a recent Linux kernel (2.6.38 or
|
|
310
|
+
newer), otherwise the resolution of `CLOCK_REALTIME` is inadequate.
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
`folly/Conv.h`
|
|
2
|
+
-------------
|
|
3
|
+
|
|
4
|
+
`folly/Conv.h` is a one-stop-shop for converting values across
|
|
5
|
+
types. Its main features are simplicity of the API (only the
|
|
6
|
+
names `to` and `toAppend` must be memorized), speed
|
|
7
|
+
(folly is significantly faster, sometimes by an order of magnitude,
|
|
8
|
+
than comparable APIs), and correctness.
|
|
9
|
+
|
|
10
|
+
### Synopsis
|
|
11
|
+
***
|
|
12
|
+
|
|
13
|
+
All examples below are assume to have included `folly/Conv.h`
|
|
14
|
+
and issued `using namespace folly;` You will need:
|
|
15
|
+
|
|
16
|
+
``` Cpp
|
|
17
|
+
// To format as text and append to a string, use toAppend.
|
|
18
|
+
fbstring str;
|
|
19
|
+
toAppend(2.5, &str);
|
|
20
|
+
CHECK_EQ(str, "2.5");
|
|
21
|
+
|
|
22
|
+
// Multiple arguments are okay, too. Just put the pointer to string at the end.
|
|
23
|
+
toAppend(" is ", 2, " point ", 5, &str);
|
|
24
|
+
CHECK_EQ(str, "2.5 is 2 point 5");
|
|
25
|
+
|
|
26
|
+
// You don't need to use fbstring (although it's much faster for conversions and in general).
|
|
27
|
+
std::string stdStr;
|
|
28
|
+
toAppend("Pi is about ", 22.0 / 7, &stdStr);
|
|
29
|
+
// In general, just use to<TargetType>(sourceValue). It returns its result by value.
|
|
30
|
+
stdStr = to<std::string>("Variadic ", "arguments also accepted.");
|
|
31
|
+
|
|
32
|
+
// to<fbstring> is 2.5x faster than to<std::string> for typical workloads.
|
|
33
|
+
str = to<fbstring>("Variadic ", "arguments also accepted.");
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Integral-to-integral conversion
|
|
37
|
+
***
|
|
38
|
+
|
|
39
|
+
Using `to<Target>(value)` to convert one integral type to another
|
|
40
|
+
will behave as follows:
|
|
41
|
+
|
|
42
|
+
* If the target type can accommodate all possible values of the
|
|
43
|
+
source value, the value is implicitly converted. No further
|
|
44
|
+
action is taken. Example:
|
|
45
|
+
|
|
46
|
+
``` Cpp
|
|
47
|
+
short x;
|
|
48
|
+
unsigned short y;
|
|
49
|
+
...
|
|
50
|
+
auto a = to<int>(x); // zero overhead conversion
|
|
51
|
+
auto b = to<int>(y); // zero overhead conversion
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
* Otherwise, `to` inserts bounds checks and throws
|
|
55
|
+
`std::range_error` if the target type cannot accommodate the
|
|
56
|
+
source value. Example:
|
|
57
|
+
|
|
58
|
+
``` Cpp
|
|
59
|
+
short x;
|
|
60
|
+
unsigned short y;
|
|
61
|
+
long z;
|
|
62
|
+
...
|
|
63
|
+
x = 123;
|
|
64
|
+
auto a = to<unsigned short>(x); // fine
|
|
65
|
+
x = -1;
|
|
66
|
+
a = to<unsigned short>(x); // THROWS
|
|
67
|
+
z = 2000000000;
|
|
68
|
+
auto b = to<int>(z); // fine
|
|
69
|
+
z += 1000000000;
|
|
70
|
+
b = to<int>(z); // THROWS
|
|
71
|
+
auto b = to<unsigned int>(z); // fine
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Anything-to-string conversion
|
|
75
|
+
***
|
|
76
|
+
|
|
77
|
+
As mentioned, there are two primitives for converting anything to
|
|
78
|
+
string: `to` and `toAppend`. They support the same set of source
|
|
79
|
+
types, literally by definition (`to` is implemented in terms of
|
|
80
|
+
`toAppend` for all types). The call `toAppend(value, &str)`
|
|
81
|
+
formats and appends `value` to `str` whereas
|
|
82
|
+
`to<StringType>(value)` formats `value` as a `StringType` and
|
|
83
|
+
returns the result by value. Currently, the supported
|
|
84
|
+
`StringType`s are `std::string` and `fbstring`
|
|
85
|
+
|
|
86
|
+
Both `toAppend` and `to` with a string type as a target support
|
|
87
|
+
variadic arguments. Each argument is converted in turn. For
|
|
88
|
+
`toAppend` the last argument in a variadic list must be the
|
|
89
|
+
address of a supported string type (no need to specify the string
|
|
90
|
+
type as a template argument).
|
|
91
|
+
|
|
92
|
+
#### Integral-to-string conversion
|
|
93
|
+
|
|
94
|
+
Nothing special here - integrals are converted to strings in
|
|
95
|
+
decimal format, with a '-' prefix for negative values. Example:
|
|
96
|
+
|
|
97
|
+
``` Cpp
|
|
98
|
+
auto a = to<fbstring>(123);
|
|
99
|
+
assert(a == "123");
|
|
100
|
+
a = to<fbstring>(-456);
|
|
101
|
+
assert(a == "-456");
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The conversion implementation is aggressively optimized. It
|
|
105
|
+
converts two digits at a time assisted by fixed-size tables.
|
|
106
|
+
Converting a `long` to an `fbstring` is 3.6x faster than using
|
|
107
|
+
`boost::lexical_cast` and 2.5x faster than using `sprintf` even
|
|
108
|
+
though the latter is used in conjunction with a stack-allocated
|
|
109
|
+
constant-size buffer.
|
|
110
|
+
|
|
111
|
+
Note that converting integral types to `fbstring` has a
|
|
112
|
+
particular advantage compared to converting to `std::string`
|
|
113
|
+
No integral type (<= 64 bits) has more than 20 decimal digits
|
|
114
|
+
including sign. Since `fbstring` employs the small string
|
|
115
|
+
optimization for up to 23 characters, converting an integral
|
|
116
|
+
to `fbstring` is guaranteed to not allocate memory, resulting
|
|
117
|
+
in significant speed and memory locality gains. Benchmarks
|
|
118
|
+
reveal a 2x gain on a typical workload.
|
|
119
|
+
|
|
120
|
+
#### `char` to string conversion
|
|
121
|
+
|
|
122
|
+
Although `char` is technically an integral type, most of the time
|
|
123
|
+
you want the string representation of `'a'` to be `"a"`, not `96`
|
|
124
|
+
That's why `folly/Conv.h` handles `char` as a special case that
|
|
125
|
+
does the expected thing. Note that `signed char` and `unsigned
|
|
126
|
+
char` are still considered integral types.
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
#### Floating point to string conversion
|
|
130
|
+
|
|
131
|
+
`folly/Conv.h` uses [V8's double conversion](http://code.google.com/p/double-conversion/)
|
|
132
|
+
routines. They are accurate and fast; on typical workloads,
|
|
133
|
+
`to<fbstring>(doubleValue)` is 1.9x faster than `sprintf` and
|
|
134
|
+
5.5x faster than `boost::lexical_cast` (It is also 1.3x faster
|
|
135
|
+
than `to<std::string>(doubleValue)`
|
|
136
|
+
|
|
137
|
+
#### `const char*` to string conversion
|
|
138
|
+
|
|
139
|
+
For completeness, `folly/Conv.h` supports `const char*` including
|
|
140
|
+
i.e. string literals. The "conversion" consists, of course, of
|
|
141
|
+
the string itself. Example:
|
|
142
|
+
|
|
143
|
+
``` Cpp
|
|
144
|
+
auto s = to<fbstring>("Hello, world");
|
|
145
|
+
assert(s == "Hello, world");
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### Anything from string conversion (i.e. parsing)
|
|
149
|
+
***
|
|
150
|
+
|
|
151
|
+
`folly/Conv.h` includes three kinds of parsing routines:
|
|
152
|
+
|
|
153
|
+
* `to<Type>(const char* begin, const char* end)` rigidly
|
|
154
|
+
converts the range [begin, end) to `Type` These routines have
|
|
155
|
+
drastic restrictions (e.g. allow no leading or trailing
|
|
156
|
+
whitespace) and are intended as an efficient back-end for more
|
|
157
|
+
tolerant routines.
|
|
158
|
+
* `to<Type>(stringy)` converts `stringy` to `Type` Value
|
|
159
|
+
`stringy` may be of type `const char*`, `StringPiece`,
|
|
160
|
+
`std::string`, or `fbstring` (Technically, the requirement is
|
|
161
|
+
that `stringy` implicitly converts to a `StringPiece`
|
|
162
|
+
* `to<Type>(&stringPiece)` parses with progress information:
|
|
163
|
+
given `stringPiece` of type `StringPiece` it parses as much
|
|
164
|
+
as possible from it as type `Type` and alters `stringPiece`
|
|
165
|
+
to remove the munched characters. This is easiest clarified
|
|
166
|
+
by an example:
|
|
167
|
+
|
|
168
|
+
``` Cpp
|
|
169
|
+
fbstring s = " 1234 angels on a pin";
|
|
170
|
+
StringPiece pc(s);
|
|
171
|
+
auto x = to<int>(&pc);
|
|
172
|
+
assert(x == 1234);
|
|
173
|
+
assert(pc == " angels on a pin";
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Note how the routine ate the leading space but not the trailing one.
|
|
177
|
+
|
|
178
|
+
#### Parsing integral types
|
|
179
|
+
|
|
180
|
+
Parsing integral types is unremarkable - decimal format is
|
|
181
|
+
expected, optional `'+'` or `'-'` sign for signed types, but no
|
|
182
|
+
optional `'+'` is allowed for unsigned types. The one remarkable
|
|
183
|
+
element is speed - parsing typical `long` values is 6x faster than
|
|
184
|
+
`sscanf`. `folly/Conv.h` uses aggressive loop unrolling and
|
|
185
|
+
table-assisted SIMD-style code arrangement that avoids integral
|
|
186
|
+
division (slow) and data dependencies across operations
|
|
187
|
+
(ILP-unfriendly). Example:
|
|
188
|
+
|
|
189
|
+
``` Cpp
|
|
190
|
+
fbstring str = " 12345 ";
|
|
191
|
+
assert(to<int>(str) == 12345);
|
|
192
|
+
str = " 12345six seven eight";
|
|
193
|
+
StringPiece pc(str);
|
|
194
|
+
assert(to<int>(&pc) == 12345);
|
|
195
|
+
assert(str == "six seven eight");
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
#### Parsing floating-point types
|
|
199
|
+
|
|
200
|
+
`folly/Conv.h` uses, again, [V8's double-conversion](http://code.google.com/p/double-conversion/)
|
|
201
|
+
routines as back-end. The speed is 3x faster than `sscanf` and
|
|
202
|
+
1.7x faster than in-home routines such as `parse<double>` But
|
|
203
|
+
the more important detail is accuracy - even if you do code a
|
|
204
|
+
routine that works faster than `to<double>` chances are it is
|
|
205
|
+
incorrect and will fail in a variety of corner cases. Using
|
|
206
|
+
`to<double>` is strongly recommended.
|
|
207
|
+
|
|
208
|
+
Note that if the string "NaN" (with any capitalization) is passed to
|
|
209
|
+
`to<double>` then `NaN` is returned, which can be tested for as follows:
|
|
210
|
+
|
|
211
|
+
``` Cpp
|
|
212
|
+
fbstring str = "nan"; // "NaN", "NAN", etc.
|
|
213
|
+
double d = to<double>(str);
|
|
214
|
+
if (std::isnan(d)) {
|
|
215
|
+
// string was a valid representation of the double value NaN
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Note that passing "-NaN" (with any capitalization) to `to<double>` also returns
|
|
220
|
+
`NaN`.
|
|
221
|
+
|
|
222
|
+
Note that if the strings "inf" or "infinity" (with any capitalization) are
|
|
223
|
+
passed to `to<double>` then `infinity` is returned, which can be tested for
|
|
224
|
+
as follows:
|
|
225
|
+
|
|
226
|
+
``` Cpp
|
|
227
|
+
fbstring str = "inf"; // "Inf", "INF", "infinity", "Infinity", etc.
|
|
228
|
+
double d = to<double>(str);
|
|
229
|
+
if (std::isinf(d)) {
|
|
230
|
+
// string was a valid representation of one of the double values +Infinity
|
|
231
|
+
// or -Infinity
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Note that passing "-inf" or "-infinity" (with any capitalization) to
|
|
236
|
+
`to<double>` returns `-infinity` rather than `+infinity`. The sign of the
|
|
237
|
+
`infinity` can be tested for as follows:
|
|
238
|
+
|
|
239
|
+
``` Cpp
|
|
240
|
+
fbstring str = "-inf"; // or "inf", "-Infinity", "+Infinity", etc.
|
|
241
|
+
double d = to<double>(str);
|
|
242
|
+
if (d == std::numeric_limits<double>::infinity()) {
|
|
243
|
+
// string was a valid representation of the double value +Infinity
|
|
244
|
+
} else if (d == -std::numeric_limits<double>::infinity()) {
|
|
245
|
+
// string was a valid representation of the double value -Infinity
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Note that if an unparseable string is passed to `to<double>` then an exception
|
|
250
|
+
is thrown, rather than `NaN` being returned. This can be tested for as follows:
|
|
251
|
+
|
|
252
|
+
``` Cpp
|
|
253
|
+
fbstring str = "not-a-double"; // Or "1.1.1", "", "$500.00", etc.
|
|
254
|
+
double d;
|
|
255
|
+
try {
|
|
256
|
+
d = to<double>(str);
|
|
257
|
+
} catch (const std::range_error &) {
|
|
258
|
+
// string could not be parsed
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Note that the empty string (`""`) is an unparseable value, and will cause
|
|
263
|
+
`to<double>` to throw an exception.
|
|
264
|
+
|
|
265
|
+
#### Non-throwing interfaces
|
|
266
|
+
|
|
267
|
+
`tryTo<T>` is the non-throwing variant of `to<T>`. It returns
|
|
268
|
+
an `Expected<T, ConversionCode>`. You can think of `Expected`
|
|
269
|
+
as like an `Optional<T>`, but if the conversion failed, `Expected`
|
|
270
|
+
stores an error code instead of a `T`.
|
|
271
|
+
|
|
272
|
+
`tryTo<T>` has similar performance as `to<T>` when the
|
|
273
|
+
conversion is successful. On the error path, you can expect
|
|
274
|
+
`tryTo<T>` to be roughly three orders of magnitude faster than
|
|
275
|
+
the throwing `to<T>` and to completely avoid any lock contention
|
|
276
|
+
arising from stack unwinding.
|
|
277
|
+
|
|
278
|
+
Here is how to use non-throwing conversions:
|
|
279
|
+
|
|
280
|
+
``` Cpp
|
|
281
|
+
auto t1 = tryTo<int>(str);
|
|
282
|
+
if (t1.hasValue()) {
|
|
283
|
+
use(t1.value());
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
`Expected` has a composability feature to make the above pattern simpler.
|
|
288
|
+
|
|
289
|
+
``` Cpp
|
|
290
|
+
tryTo<int>(str).then([](int i) { use(i); });
|
|
291
|
+
```
|