react-native-windows 0.73.20 → 0.73.22
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.
|
@@ -0,0 +1,1635 @@
|
|
|
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
|
+
/**
|
|
18
|
+
* Conv provides the ubiquitous method `to<TargetType>(source)`, along with
|
|
19
|
+
* a few other generic interfaces for converting objects to and from
|
|
20
|
+
* string-like types (std::string, fbstring, StringPiece), as well as
|
|
21
|
+
* range-checked conversions between numeric and enum types. The mechanisms are
|
|
22
|
+
* extensible, so that user-specified types can add folly::to support.
|
|
23
|
+
*
|
|
24
|
+
* folly::to<std::string>(123)
|
|
25
|
+
* // "123"
|
|
26
|
+
*
|
|
27
|
+
*******************************************************************************
|
|
28
|
+
* ## TYPE -> STRING CONVERSIONS
|
|
29
|
+
*******************************************************************************
|
|
30
|
+
* You can call the `to<std::string>` or `to<fbstring>`. These are variadic
|
|
31
|
+
* functions that convert their arguments to strings, and concatenate them to
|
|
32
|
+
* form a result. So, for example,
|
|
33
|
+
*
|
|
34
|
+
* auto str = to<std::string>(123, "456", 789);
|
|
35
|
+
*
|
|
36
|
+
* Sets str to `"123456789"`.
|
|
37
|
+
*
|
|
38
|
+
* In addition to just concatenating the arguments, related functions can
|
|
39
|
+
* delimit them with some string: `toDelim<std::string>(",", "123", 456, "789")`
|
|
40
|
+
* will return the string `"123,456,789"`.
|
|
41
|
+
*
|
|
42
|
+
* toAppend does not return a string; instead, it takes a pointer to a string as
|
|
43
|
+
* its last argument, and appends the result of the concatenation into it:
|
|
44
|
+
* std::string str = "123";
|
|
45
|
+
* toAppend(456, "789", &str); // Now str is "123456789".
|
|
46
|
+
*
|
|
47
|
+
* The toAppendFit function acts like toAppend, but it precalculates the size
|
|
48
|
+
* required to perform the append operation, and reserves that space in the
|
|
49
|
+
* output string before actually inserting its arguments. This can sometimes
|
|
50
|
+
* save on string expansion, but beware: appending to the same string many times
|
|
51
|
+
* with toAppendFit is likely a pessimization, since it will resize the string
|
|
52
|
+
* once per append.
|
|
53
|
+
*
|
|
54
|
+
* The combination of the append and delim variants also exist: toAppendDelim
|
|
55
|
+
* and toAppendDelimFit are defined, with the obvious semantics.
|
|
56
|
+
*
|
|
57
|
+
*******************************************************************************
|
|
58
|
+
* ## STRING -> TYPE CONVERSIONS
|
|
59
|
+
*******************************************************************************
|
|
60
|
+
* Going in the other direction, and parsing a string into a C++ type, is also
|
|
61
|
+
* supported:
|
|
62
|
+
* to<int>("123"); // Returns 123.
|
|
63
|
+
*
|
|
64
|
+
* Out of range (e.g. `to<std::uint8_t>("1000")`), or invalidly formatted (e.g.
|
|
65
|
+
* `to<int>("four")`) inputs will throw. If throw-on-error is undesirable (for
|
|
66
|
+
* instance: you're dealing with untrusted input, and want to protect yourself
|
|
67
|
+
* from users sending you down a very slow exception-throwing path), you can use
|
|
68
|
+
* `tryTo<T>`, which will return an `Expected<T, ConversionCode>`.
|
|
69
|
+
*
|
|
70
|
+
* There are overloads of to() and tryTo() that take a `StringPiece*`. These
|
|
71
|
+
* parse out a type from the beginning of a string, and modify the passed-in
|
|
72
|
+
* StringPiece to indicate the portion of the string not consumed.
|
|
73
|
+
*
|
|
74
|
+
*******************************************************************************
|
|
75
|
+
* ## NUMERIC / ENUM CONVERSIONS
|
|
76
|
+
*******************************************************************************
|
|
77
|
+
* Conv also supports a `to<T>(S)` overload, where T and S are numeric or enum
|
|
78
|
+
* types, that checks to see that the target type can represent its argument,
|
|
79
|
+
* and will throw if it cannot. This includes cases where a floating point to
|
|
80
|
+
* integral conversion is attempted on a value with a non-zero fractional
|
|
81
|
+
* component, and integral to floating point conversions that would lose
|
|
82
|
+
* precision. Enum conversions are range-checked for the underlying type of the
|
|
83
|
+
* enum, but there is no check that the input value is a valid choice of enum
|
|
84
|
+
* value.
|
|
85
|
+
*
|
|
86
|
+
*******************************************************************************
|
|
87
|
+
* ## CUSTOM TYPE CONVERSIONS
|
|
88
|
+
*******************************************************************************
|
|
89
|
+
* Users may customize the string conversion functionality for their own data
|
|
90
|
+
* types. The key functions you should implement are:
|
|
91
|
+
* // Two functions to allow conversion to your type from a string.
|
|
92
|
+
* Expected<StringPiece, ConversionCode> parseTo(folly::StringPiece in,
|
|
93
|
+
* YourType& out);
|
|
94
|
+
* YourErrorType makeConversionError(YourErrorType in, StringPiece in);
|
|
95
|
+
* // Two functions to allow conversion from your type to a string.
|
|
96
|
+
* template <class String>
|
|
97
|
+
* void toAppend(const YourType& in, String* out);
|
|
98
|
+
* size_t estimateSpaceNeeded(const YourType& in);
|
|
99
|
+
*
|
|
100
|
+
* These are documented below, inline.
|
|
101
|
+
*
|
|
102
|
+
* @file Conv.h
|
|
103
|
+
*/
|
|
104
|
+
|
|
105
|
+
#pragma once
|
|
106
|
+
|
|
107
|
+
#include <algorithm>
|
|
108
|
+
#include <cassert>
|
|
109
|
+
#include <cctype>
|
|
110
|
+
#include <climits>
|
|
111
|
+
#include <cmath>
|
|
112
|
+
#include <cstddef>
|
|
113
|
+
#include <limits>
|
|
114
|
+
#include <stdexcept>
|
|
115
|
+
#include <string>
|
|
116
|
+
#include <tuple>
|
|
117
|
+
#include <type_traits>
|
|
118
|
+
#include <utility>
|
|
119
|
+
|
|
120
|
+
#include <double-conversion/double-conversion.h> // V8 JavaScript implementation
|
|
121
|
+
|
|
122
|
+
#include <folly/CPortability.h>
|
|
123
|
+
#include <folly/Demangle.h>
|
|
124
|
+
#include <folly/Expected.h>
|
|
125
|
+
#include <folly/FBString.h>
|
|
126
|
+
#include <folly/Likely.h>
|
|
127
|
+
#include <folly/Portability.h>
|
|
128
|
+
#include <folly/Range.h>
|
|
129
|
+
#include <folly/Traits.h>
|
|
130
|
+
#include <folly/Unit.h>
|
|
131
|
+
#include <folly/Utility.h>
|
|
132
|
+
#include <folly/lang/Exception.h>
|
|
133
|
+
#include <folly/lang/Pretty.h>
|
|
134
|
+
#include <folly/lang/ToAscii.h>
|
|
135
|
+
#include <folly/portability/Math.h>
|
|
136
|
+
|
|
137
|
+
namespace folly {
|
|
138
|
+
|
|
139
|
+
// Keep this in sync with kErrorStrings in Conv.cpp
|
|
140
|
+
enum class ConversionCode : unsigned char {
|
|
141
|
+
SUCCESS,
|
|
142
|
+
EMPTY_INPUT_STRING,
|
|
143
|
+
NO_DIGITS,
|
|
144
|
+
BOOL_OVERFLOW,
|
|
145
|
+
BOOL_INVALID_VALUE,
|
|
146
|
+
NON_DIGIT_CHAR,
|
|
147
|
+
INVALID_LEADING_CHAR,
|
|
148
|
+
POSITIVE_OVERFLOW,
|
|
149
|
+
NEGATIVE_OVERFLOW,
|
|
150
|
+
STRING_TO_FLOAT_ERROR,
|
|
151
|
+
NON_WHITESPACE_AFTER_END,
|
|
152
|
+
ARITH_POSITIVE_OVERFLOW,
|
|
153
|
+
ARITH_NEGATIVE_OVERFLOW,
|
|
154
|
+
ARITH_LOSS_OF_PRECISION,
|
|
155
|
+
NUM_ERROR_CODES, // has to be the last entry
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
struct FOLLY_EXPORT ConversionErrorBase : std::range_error {
|
|
159
|
+
using std::range_error::range_error;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
class FOLLY_EXPORT ConversionError : public ConversionErrorBase {
|
|
163
|
+
public:
|
|
164
|
+
ConversionError(const std::string& str, ConversionCode code)
|
|
165
|
+
: ConversionErrorBase(str), code_(code) {}
|
|
166
|
+
|
|
167
|
+
ConversionError(const char* str, ConversionCode code)
|
|
168
|
+
: ConversionErrorBase(str), code_(code) {}
|
|
169
|
+
|
|
170
|
+
ConversionCode errorCode() const { return code_; }
|
|
171
|
+
|
|
172
|
+
private:
|
|
173
|
+
ConversionCode code_;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Custom Error Translation
|
|
178
|
+
*
|
|
179
|
+
* Your overloaded parseTo() function can return a custom error code on failure.
|
|
180
|
+
* ::folly::to() will call makeConversionError to translate that error code into
|
|
181
|
+
* an object to throw. makeConversionError is found by argument-dependent
|
|
182
|
+
* lookup. It should have this signature:
|
|
183
|
+
*
|
|
184
|
+
* namespace other_namespace {
|
|
185
|
+
* enum YourErrorCode { BAD_ERROR, WORSE_ERROR };
|
|
186
|
+
*
|
|
187
|
+
* struct YourConversionError : ConversionErrorBase {
|
|
188
|
+
* YourConversionError(const char* what) : ConversionErrorBase(what) {}
|
|
189
|
+
* };
|
|
190
|
+
*
|
|
191
|
+
* YourConversionError
|
|
192
|
+
* makeConversionError(YourErrorCode code, ::folly::StringPiece sp) {
|
|
193
|
+
* ...
|
|
194
|
+
* return YourConversionError(messageString);
|
|
195
|
+
* }
|
|
196
|
+
*/
|
|
197
|
+
ConversionError makeConversionError(ConversionCode code, StringPiece input);
|
|
198
|
+
|
|
199
|
+
namespace detail {
|
|
200
|
+
/**
|
|
201
|
+
* Enforce that the suffix following a number is made up only of whitespace.
|
|
202
|
+
*/
|
|
203
|
+
inline ConversionCode enforceWhitespaceErr(StringPiece sp) {
|
|
204
|
+
for (auto c : sp) {
|
|
205
|
+
if (UNLIKELY(!std::isspace(c))) {
|
|
206
|
+
return ConversionCode::NON_WHITESPACE_AFTER_END;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return ConversionCode::SUCCESS;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Keep this implementation around for prettyToDouble().
|
|
214
|
+
*/
|
|
215
|
+
inline void enforceWhitespace(StringPiece sp) {
|
|
216
|
+
auto err = enforceWhitespaceErr(sp);
|
|
217
|
+
if (err != ConversionCode::SUCCESS) {
|
|
218
|
+
throw_exception(makeConversionError(err, sp));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
} // namespace detail
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* @overloadbrief to, but return an Expected
|
|
225
|
+
*
|
|
226
|
+
* The identity conversion function.
|
|
227
|
+
* tryTo<T>(T) returns itself for all types T.
|
|
228
|
+
*/
|
|
229
|
+
template <class Tgt, class Src>
|
|
230
|
+
typename std::enable_if<
|
|
231
|
+
std::is_same<Tgt, typename std::decay<Src>::type>::value,
|
|
232
|
+
Expected<Tgt, ConversionCode>>::type
|
|
233
|
+
tryTo(Src&& value) {
|
|
234
|
+
return static_cast<Src&&>(value);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* @overloadbrief Convert from one type to another.
|
|
239
|
+
*/
|
|
240
|
+
template <class Tgt, class Src>
|
|
241
|
+
typename std::enable_if<
|
|
242
|
+
std::is_same<Tgt, typename std::decay<Src>::type>::value,
|
|
243
|
+
Tgt>::type
|
|
244
|
+
to(Src&& value) {
|
|
245
|
+
return static_cast<Src&&>(value);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Arithmetic to boolean
|
|
250
|
+
*/
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Unchecked conversion from arithmetic to boolean. This is different from the
|
|
254
|
+
* other arithmetic conversions because we use the C convention of treating any
|
|
255
|
+
* non-zero value as true, instead of range checking.
|
|
256
|
+
*/
|
|
257
|
+
template <class Tgt, class Src>
|
|
258
|
+
typename std::enable_if<
|
|
259
|
+
is_arithmetic_v<Src> && !std::is_same<Tgt, Src>::value &&
|
|
260
|
+
std::is_same<Tgt, bool>::value,
|
|
261
|
+
Expected<Tgt, ConversionCode>>::type
|
|
262
|
+
tryTo(const Src& value) {
|
|
263
|
+
return value != Src();
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
template <class Tgt, class Src>
|
|
267
|
+
typename std::enable_if<
|
|
268
|
+
is_arithmetic_v<Src> && !std::is_same<Tgt, Src>::value &&
|
|
269
|
+
std::is_same<Tgt, bool>::value,
|
|
270
|
+
Tgt>::type
|
|
271
|
+
to(const Src& value) {
|
|
272
|
+
return value != Src();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Anything to string
|
|
277
|
+
*/
|
|
278
|
+
|
|
279
|
+
namespace detail {
|
|
280
|
+
|
|
281
|
+
template <class... T>
|
|
282
|
+
using LastElement = type_pack_element_t<sizeof...(T) - 1, T...>;
|
|
283
|
+
|
|
284
|
+
#ifdef _MSC_VER
|
|
285
|
+
// MSVC can't quite figure out the LastElementImpl::call() stuff
|
|
286
|
+
// in the base implementation, so we have to use tuples instead,
|
|
287
|
+
// which result in significantly more templates being compiled,
|
|
288
|
+
// though the runtime performance is the same.
|
|
289
|
+
|
|
290
|
+
template <typename... Ts>
|
|
291
|
+
const LastElement<Ts...>& getLastElement(const Ts&... ts) {
|
|
292
|
+
return std::get<sizeof...(Ts) - 1>(std::forward_as_tuple(ts...));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
inline void getLastElement() {}
|
|
296
|
+
#else
|
|
297
|
+
template <typename...>
|
|
298
|
+
struct LastElementImpl;
|
|
299
|
+
template <>
|
|
300
|
+
struct LastElementImpl<> {
|
|
301
|
+
static void call() {}
|
|
302
|
+
};
|
|
303
|
+
template <typename Ign, typename... Igns>
|
|
304
|
+
struct LastElementImpl<Ign, Igns...> {
|
|
305
|
+
template <typename Last>
|
|
306
|
+
static const Last& call(Igns..., const Last& last) {
|
|
307
|
+
return last;
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
template <typename... Ts>
|
|
312
|
+
const LastElement<Ts...>& getLastElement(const Ts&... ts) {
|
|
313
|
+
return LastElementImpl<Ignored<Ts>...>::call(ts...);
|
|
314
|
+
}
|
|
315
|
+
#endif
|
|
316
|
+
|
|
317
|
+
} // namespace detail
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Conversions from integral types to string types.
|
|
321
|
+
*/
|
|
322
|
+
|
|
323
|
+
#if FOLLY_HAVE_INT128_T
|
|
324
|
+
namespace detail {
|
|
325
|
+
|
|
326
|
+
template <typename IntegerType>
|
|
327
|
+
constexpr unsigned int digitsEnough() {
|
|
328
|
+
// digits10 returns the number of decimal digits that this type can represent,
|
|
329
|
+
// not the number of characters required for the max value, so we need to add
|
|
330
|
+
// one. ex: char digits10 returns 2, because 256-999 cannot be represented,
|
|
331
|
+
// but we need 3.
|
|
332
|
+
auto const digits10 = std::numeric_limits<IntegerType>::digits10;
|
|
333
|
+
return static_cast<unsigned int>(digits10) + 1;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
inline size_t unsafeTelescope128(char* outb, char* oute, unsigned __int128 x) {
|
|
337
|
+
using Usrc = unsigned __int128;
|
|
338
|
+
|
|
339
|
+
// Decompose the input into at most 3 components using the largest power-of-10
|
|
340
|
+
// base that fits in a 64-bit unsigned integer, and then convert the
|
|
341
|
+
// components using 64-bit arithmetic and concatenate them.
|
|
342
|
+
constexpr static auto kBase = UINT64_C(10'000'000'000'000'000'000);
|
|
343
|
+
constexpr static size_t kBaseDigits = 19;
|
|
344
|
+
|
|
345
|
+
size_t p = 0;
|
|
346
|
+
const auto leading = [&](Usrc v) {
|
|
347
|
+
assert(v >> 64 == 0);
|
|
348
|
+
p = detail::to_ascii_with_route<10, to_ascii_alphabet_lower>(
|
|
349
|
+
outb, oute, static_cast<uint64_t>(v));
|
|
350
|
+
};
|
|
351
|
+
const auto append = [&](uint64_t v) {
|
|
352
|
+
assert(v < kBase);
|
|
353
|
+
assert(outb + p + kBaseDigits <= oute);
|
|
354
|
+
auto v64 = static_cast<uint64_t>(v);
|
|
355
|
+
detail::to_ascii_with_route<10, to_ascii_alphabet_lower>(
|
|
356
|
+
outb + p, kBaseDigits, v64);
|
|
357
|
+
p += kBaseDigits;
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
if (x >> 64 > 0) {
|
|
361
|
+
const auto rem = static_cast<uint64_t>(x % kBase);
|
|
362
|
+
x /= kBase;
|
|
363
|
+
|
|
364
|
+
if (x >> 64 > 0) {
|
|
365
|
+
const auto rem2 = static_cast<uint64_t>(x % kBase);
|
|
366
|
+
x /= kBase;
|
|
367
|
+
|
|
368
|
+
leading(x);
|
|
369
|
+
append(rem2);
|
|
370
|
+
append(rem);
|
|
371
|
+
return p;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
leading(x);
|
|
375
|
+
append(rem);
|
|
376
|
+
return p;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
leading(x);
|
|
380
|
+
return p;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
} // namespace detail
|
|
384
|
+
#endif
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* @overloadbrief Appends conversion to string.
|
|
388
|
+
*
|
|
389
|
+
* A single char gets appended.
|
|
390
|
+
*/
|
|
391
|
+
template <class Tgt>
|
|
392
|
+
void toAppend(char value, Tgt* result) {
|
|
393
|
+
*result += value;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* @overloadbrief Estimates the number of characters in a value's string
|
|
398
|
+
* representation.
|
|
399
|
+
*/
|
|
400
|
+
template <class T>
|
|
401
|
+
constexpr typename std::enable_if<std::is_same<T, char>::value, size_t>::type
|
|
402
|
+
estimateSpaceNeeded(T) {
|
|
403
|
+
return 1;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
template <size_t N>
|
|
407
|
+
constexpr size_t estimateSpaceNeeded(const char (&)[N]) {
|
|
408
|
+
return N;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Everything implicitly convertible to const char* gets appended.
|
|
413
|
+
*/
|
|
414
|
+
template <class Tgt, class Src>
|
|
415
|
+
typename std::enable_if<
|
|
416
|
+
std::is_convertible<Src, const char*>::value &&
|
|
417
|
+
IsSomeString<Tgt>::value>::type
|
|
418
|
+
toAppend(Src value, Tgt* result) {
|
|
419
|
+
// Treat null pointers like an empty string, as in:
|
|
420
|
+
// operator<<(std::ostream&, const char*).
|
|
421
|
+
const char* c = value;
|
|
422
|
+
if (c) {
|
|
423
|
+
result->append(value);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
template <class Src>
|
|
428
|
+
typename std::enable_if<std::is_convertible<Src, const char*>::value, size_t>::
|
|
429
|
+
type
|
|
430
|
+
estimateSpaceNeeded(Src value) {
|
|
431
|
+
const char* c = value;
|
|
432
|
+
if (c) {
|
|
433
|
+
return folly::StringPiece(value).size();
|
|
434
|
+
};
|
|
435
|
+
return 0;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
template <class Src>
|
|
439
|
+
typename std::enable_if<IsSomeString<Src>::value, size_t>::type
|
|
440
|
+
estimateSpaceNeeded(Src const& value) {
|
|
441
|
+
return value.size();
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
template <class Src>
|
|
445
|
+
typename std::enable_if<
|
|
446
|
+
std::is_convertible<Src, folly::StringPiece>::value &&
|
|
447
|
+
!IsSomeString<Src>::value &&
|
|
448
|
+
!std::is_convertible<Src, const char*>::value,
|
|
449
|
+
size_t>::type
|
|
450
|
+
estimateSpaceNeeded(Src value) {
|
|
451
|
+
return folly::StringPiece(value).size();
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
template <>
|
|
455
|
+
inline size_t estimateSpaceNeeded(std::nullptr_t /* value */) {
|
|
456
|
+
return 0;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
template <class Src>
|
|
460
|
+
typename std::enable_if<
|
|
461
|
+
std::is_pointer<Src>::value &&
|
|
462
|
+
IsSomeString<std::remove_pointer<Src>>::value,
|
|
463
|
+
size_t>::type
|
|
464
|
+
estimateSpaceNeeded(Src value) {
|
|
465
|
+
return value->size();
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Strings get appended, too.
|
|
470
|
+
*/
|
|
471
|
+
template <class Tgt, class Src>
|
|
472
|
+
typename std::enable_if<
|
|
473
|
+
IsSomeString<Src>::value && IsSomeString<Tgt>::value>::type
|
|
474
|
+
toAppend(const Src& value, Tgt* result) {
|
|
475
|
+
result->append(value);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* and StringPiece objects too
|
|
480
|
+
*/
|
|
481
|
+
template <class Tgt>
|
|
482
|
+
typename std::enable_if<IsSomeString<Tgt>::value>::type toAppend(
|
|
483
|
+
StringPiece value, Tgt* result) {
|
|
484
|
+
result->append(value.data(), value.size());
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* There's no implicit conversion from fbstring to other string types,
|
|
489
|
+
* so make a specialization.
|
|
490
|
+
*/
|
|
491
|
+
template <class Tgt>
|
|
492
|
+
typename std::enable_if<IsSomeString<Tgt>::value>::type toAppend(
|
|
493
|
+
const fbstring& value, Tgt* result) {
|
|
494
|
+
result->append(value.data(), value.size());
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
#if FOLLY_HAVE_INT128_T
|
|
498
|
+
/**
|
|
499
|
+
* Special handling for 128 bit integers.
|
|
500
|
+
*/
|
|
501
|
+
|
|
502
|
+
template <class Tgt>
|
|
503
|
+
void toAppend(__int128 value, Tgt* result) {
|
|
504
|
+
typedef unsigned __int128 Usrc;
|
|
505
|
+
char buffer[detail::digitsEnough<unsigned __int128>() + 1];
|
|
506
|
+
const auto oute = buffer + sizeof(buffer);
|
|
507
|
+
size_t p;
|
|
508
|
+
|
|
509
|
+
if (value < 0) {
|
|
510
|
+
buffer[0] = '-';
|
|
511
|
+
p = 1 + detail::unsafeTelescope128(buffer + 1, oute, -Usrc(value));
|
|
512
|
+
} else {
|
|
513
|
+
p = detail::unsafeTelescope128(buffer, oute, value);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
result->append(buffer, p);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
template <class Tgt>
|
|
520
|
+
void toAppend(unsigned __int128 value, Tgt* result) {
|
|
521
|
+
char buffer[detail::digitsEnough<unsigned __int128>()];
|
|
522
|
+
size_t p = detail::unsafeTelescope128(buffer, buffer + sizeof(buffer), value);
|
|
523
|
+
result->append(buffer, p);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
template <class T>
|
|
527
|
+
constexpr
|
|
528
|
+
typename std::enable_if<std::is_same<T, __int128>::value, size_t>::type
|
|
529
|
+
estimateSpaceNeeded(T) {
|
|
530
|
+
return detail::digitsEnough<__int128>();
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
template <class T>
|
|
534
|
+
constexpr typename std::
|
|
535
|
+
enable_if<std::is_same<T, unsigned __int128>::value, size_t>::type
|
|
536
|
+
estimateSpaceNeeded(T) {
|
|
537
|
+
return detail::digitsEnough<unsigned __int128>();
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
#endif
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* int32_t and int64_t to string (by appending) go through here. The
|
|
544
|
+
* result is APPENDED to a preexisting string passed as the second
|
|
545
|
+
* parameter. This should be efficient with fbstring because fbstring
|
|
546
|
+
* incurs no dynamic allocation below 23 bytes and no number has more
|
|
547
|
+
* than 22 bytes in its textual representation (20 for digits, one for
|
|
548
|
+
* sign, one for the terminating 0).
|
|
549
|
+
*/
|
|
550
|
+
template <class Tgt, class Src>
|
|
551
|
+
typename std::enable_if<
|
|
552
|
+
is_integral_v<Src> && is_signed_v<Src> && IsSomeString<Tgt>::value &&
|
|
553
|
+
sizeof(Src) >= 4>::type
|
|
554
|
+
toAppend(Src value, Tgt* result) {
|
|
555
|
+
char buffer[to_ascii_size_max_decimal<uint64_t>];
|
|
556
|
+
auto uvalue = value < 0 ? ~static_cast<uint64_t>(value) + 1
|
|
557
|
+
: static_cast<uint64_t>(value);
|
|
558
|
+
if (value < 0) {
|
|
559
|
+
result->push_back('-');
|
|
560
|
+
}
|
|
561
|
+
result->append(buffer, to_ascii_decimal(buffer, uvalue));
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
template <class Src>
|
|
565
|
+
typename std::enable_if<
|
|
566
|
+
is_integral_v<Src> && is_signed_v<Src> && sizeof(Src) >= 4 &&
|
|
567
|
+
sizeof(Src) < 16,
|
|
568
|
+
size_t>::type
|
|
569
|
+
estimateSpaceNeeded(Src value) {
|
|
570
|
+
auto uvalue = value < 0 ? ~static_cast<uint64_t>(value) + 1
|
|
571
|
+
: static_cast<uint64_t>(value);
|
|
572
|
+
return size_t(value < 0) + to_ascii_size_decimal(uvalue);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* As above, but for uint32_t and uint64_t.
|
|
577
|
+
*/
|
|
578
|
+
template <class Tgt, class Src>
|
|
579
|
+
typename std::enable_if<
|
|
580
|
+
is_integral_v<Src> && !is_signed_v<Src> && IsSomeString<Tgt>::value &&
|
|
581
|
+
sizeof(Src) >= 4>::type
|
|
582
|
+
toAppend(Src value, Tgt* result) {
|
|
583
|
+
char buffer[to_ascii_size_max_decimal<uint64_t>];
|
|
584
|
+
result->append(buffer, to_ascii_decimal(buffer, value));
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
template <class Src>
|
|
588
|
+
typename std::enable_if<
|
|
589
|
+
is_integral_v<Src> && !is_signed_v<Src> && sizeof(Src) >= 4 &&
|
|
590
|
+
sizeof(Src) < 16,
|
|
591
|
+
size_t>::type
|
|
592
|
+
estimateSpaceNeeded(Src value) {
|
|
593
|
+
return to_ascii_size_decimal(value);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* All small signed and unsigned integers to string go through 32-bit
|
|
598
|
+
* types int32_t and uint32_t, respectively.
|
|
599
|
+
*/
|
|
600
|
+
template <class Tgt, class Src>
|
|
601
|
+
typename std::enable_if<
|
|
602
|
+
is_integral_v<Src> && IsSomeString<Tgt>::value && sizeof(Src) < 4>::type
|
|
603
|
+
toAppend(Src value, Tgt* result) {
|
|
604
|
+
typedef typename std::conditional<is_signed_v<Src>, int64_t, uint64_t>::type
|
|
605
|
+
Intermediate;
|
|
606
|
+
toAppend<Tgt>(static_cast<Intermediate>(value), result);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
template <class Src>
|
|
610
|
+
typename std::enable_if<
|
|
611
|
+
is_integral_v<Src> && sizeof(Src) < 4 && !std::is_same<Src, char>::value,
|
|
612
|
+
size_t>::type
|
|
613
|
+
estimateSpaceNeeded(Src value) {
|
|
614
|
+
typedef typename std::conditional<is_signed_v<Src>, int64_t, uint64_t>::type
|
|
615
|
+
Intermediate;
|
|
616
|
+
return estimateSpaceNeeded(static_cast<Intermediate>(value));
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Enumerated values get appended as integers.
|
|
621
|
+
*/
|
|
622
|
+
template <class Tgt, class Src>
|
|
623
|
+
typename std::enable_if<
|
|
624
|
+
std::is_enum<Src>::value && IsSomeString<Tgt>::value>::type
|
|
625
|
+
toAppend(Src value, Tgt* result) {
|
|
626
|
+
toAppend(to_underlying(value), result);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
template <class Src>
|
|
630
|
+
typename std::enable_if<std::is_enum<Src>::value, size_t>::type
|
|
631
|
+
estimateSpaceNeeded(Src value) {
|
|
632
|
+
return estimateSpaceNeeded(to_underlying(value));
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Conversions from floating-point types to string types.
|
|
637
|
+
*/
|
|
638
|
+
|
|
639
|
+
namespace detail {
|
|
640
|
+
constexpr int kConvMaxDecimalInShortestLow = -6;
|
|
641
|
+
constexpr int kConvMaxDecimalInShortestHigh = 21;
|
|
642
|
+
} // namespace detail
|
|
643
|
+
|
|
644
|
+
/** Wrapper around DoubleToStringConverter */
|
|
645
|
+
template <class Tgt, class Src>
|
|
646
|
+
typename std::enable_if<
|
|
647
|
+
std::is_floating_point<Src>::value && IsSomeString<Tgt>::value>::type
|
|
648
|
+
toAppend(
|
|
649
|
+
Src value,
|
|
650
|
+
Tgt* result,
|
|
651
|
+
double_conversion::DoubleToStringConverter::DtoaMode mode,
|
|
652
|
+
unsigned int numDigits,
|
|
653
|
+
double_conversion::DoubleToStringConverter::Flags flags =
|
|
654
|
+
double_conversion::DoubleToStringConverter::NO_FLAGS) {
|
|
655
|
+
using namespace double_conversion;
|
|
656
|
+
DoubleToStringConverter conv(
|
|
657
|
+
flags,
|
|
658
|
+
"Infinity",
|
|
659
|
+
"NaN",
|
|
660
|
+
'E',
|
|
661
|
+
detail::kConvMaxDecimalInShortestLow,
|
|
662
|
+
detail::kConvMaxDecimalInShortestHigh,
|
|
663
|
+
6, // max leading padding zeros
|
|
664
|
+
1); // max trailing padding zeros
|
|
665
|
+
char buffer[256];
|
|
666
|
+
StringBuilder builder(buffer, sizeof(buffer));
|
|
667
|
+
FOLLY_PUSH_WARNING
|
|
668
|
+
FOLLY_CLANG_DISABLE_WARNING("-Wcovered-switch-default")
|
|
669
|
+
switch (mode) {
|
|
670
|
+
case DoubleToStringConverter::SHORTEST:
|
|
671
|
+
conv.ToShortest(value, &builder);
|
|
672
|
+
break;
|
|
673
|
+
case DoubleToStringConverter::SHORTEST_SINGLE:
|
|
674
|
+
conv.ToShortestSingle(static_cast<float>(value), &builder);
|
|
675
|
+
break;
|
|
676
|
+
case DoubleToStringConverter::FIXED:
|
|
677
|
+
conv.ToFixed(value, int(numDigits), &builder);
|
|
678
|
+
break;
|
|
679
|
+
case DoubleToStringConverter::PRECISION:
|
|
680
|
+
default:
|
|
681
|
+
assert(mode == DoubleToStringConverter::PRECISION);
|
|
682
|
+
conv.ToPrecision(value, int(numDigits), &builder);
|
|
683
|
+
break;
|
|
684
|
+
}
|
|
685
|
+
FOLLY_POP_WARNING
|
|
686
|
+
const size_t length = size_t(builder.position());
|
|
687
|
+
builder.Finalize();
|
|
688
|
+
result->append(buffer, length);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* As above, but for floating point
|
|
693
|
+
*/
|
|
694
|
+
template <class Tgt, class Src>
|
|
695
|
+
typename std::enable_if<
|
|
696
|
+
std::is_floating_point<Src>::value && IsSomeString<Tgt>::value>::type
|
|
697
|
+
toAppend(Src value, Tgt* result) {
|
|
698
|
+
toAppend(
|
|
699
|
+
value, result, double_conversion::DoubleToStringConverter::SHORTEST, 0);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* Upper bound of the length of the output from
|
|
704
|
+
* DoubleToStringConverter::ToShortest(double, StringBuilder*),
|
|
705
|
+
* as used in toAppend(double, string*).
|
|
706
|
+
*/
|
|
707
|
+
template <class Src>
|
|
708
|
+
typename std::enable_if<std::is_floating_point<Src>::value, size_t>::type
|
|
709
|
+
estimateSpaceNeeded(Src value) {
|
|
710
|
+
// kBase10MaximalLength is 17. We add 1 for decimal point,
|
|
711
|
+
// e.g. 10.0/9 is 17 digits and 18 characters, including the decimal point.
|
|
712
|
+
constexpr int kMaxMantissaSpace =
|
|
713
|
+
double_conversion::DoubleToStringConverter::kBase10MaximalLength + 1;
|
|
714
|
+
// strlen("E-") + digits10(numeric_limits<double>::max_exponent10)
|
|
715
|
+
constexpr int kMaxExponentSpace = 2 + 3;
|
|
716
|
+
static const int kMaxPositiveSpace = std::max({
|
|
717
|
+
// E.g. 1.1111111111111111E-100.
|
|
718
|
+
kMaxMantissaSpace + kMaxExponentSpace,
|
|
719
|
+
// E.g. 0.000001.1111111111111111, if kConvMaxDecimalInShortestLow is -6.
|
|
720
|
+
kMaxMantissaSpace - detail::kConvMaxDecimalInShortestLow,
|
|
721
|
+
// If kConvMaxDecimalInShortestHigh is 21, then 1e21 is the smallest
|
|
722
|
+
// number > 1 which ToShortest outputs in exponential notation,
|
|
723
|
+
// so 21 is the longest non-exponential number > 1.
|
|
724
|
+
detail::kConvMaxDecimalInShortestHigh,
|
|
725
|
+
});
|
|
726
|
+
return size_t(
|
|
727
|
+
kMaxPositiveSpace +
|
|
728
|
+
(value < 0 ? 1 : 0)); // +1 for minus sign, if negative
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* This can be specialized, together with adding specialization
|
|
733
|
+
* for estimateSpaceNeed for your type, so that we allocate
|
|
734
|
+
* as much as you need instead of the default
|
|
735
|
+
*/
|
|
736
|
+
template <class Src>
|
|
737
|
+
struct HasLengthEstimator : std::false_type {};
|
|
738
|
+
|
|
739
|
+
template <class Src>
|
|
740
|
+
constexpr typename std::enable_if<
|
|
741
|
+
!std::is_fundamental<Src>::value &&
|
|
742
|
+
#if FOLLY_HAVE_INT128_T
|
|
743
|
+
// On OSX 10.10, is_fundamental<__int128> is false :-O
|
|
744
|
+
!std::is_same<__int128, Src>::value &&
|
|
745
|
+
!std::is_same<unsigned __int128, Src>::value &&
|
|
746
|
+
#endif
|
|
747
|
+
!IsSomeString<Src>::value &&
|
|
748
|
+
!std::is_convertible<Src, const char*>::value &&
|
|
749
|
+
!std::is_convertible<Src, StringPiece>::value &&
|
|
750
|
+
!std::is_enum<Src>::value && !HasLengthEstimator<Src>::value,
|
|
751
|
+
size_t>::type
|
|
752
|
+
estimateSpaceNeeded(const Src&) {
|
|
753
|
+
return sizeof(Src) + 1; // dumbest best effort ever?
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
#ifndef DOXYGEN_SHOULD_SKIP_THIS
|
|
757
|
+
namespace detail {
|
|
758
|
+
|
|
759
|
+
FOLLY_ERASE constexpr size_t estimateSpaceToReserveOne(std::false_type, void*) {
|
|
760
|
+
return 0;
|
|
761
|
+
}
|
|
762
|
+
template <typename T>
|
|
763
|
+
FOLLY_ERASE constexpr size_t estimateSpaceToReserveOne(
|
|
764
|
+
std::true_type, const T& v) {
|
|
765
|
+
return estimateSpaceNeeded(v);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
template <typename>
|
|
769
|
+
struct EstimateSpaceToReserveAll;
|
|
770
|
+
template <size_t... I>
|
|
771
|
+
struct EstimateSpaceToReserveAll<std::index_sequence<I...>> {
|
|
772
|
+
// [MSVC] error C3523: 'sizeof...' requires as its argument an unexpanded parameter pack
|
|
773
|
+
// [MSVC] Replaced sizeof...(I) with std::index_sequence<I...>::size()
|
|
774
|
+
template <size_t J, size_t N = std::index_sequence<I...>::size()>
|
|
775
|
+
using tag = bool_constant<J + 1 < N>;
|
|
776
|
+
template <class... T>
|
|
777
|
+
static size_t call(const T&... v) {
|
|
778
|
+
const size_t sizes[] = {estimateSpaceToReserveOne(tag<I>{}, v)...};
|
|
779
|
+
size_t size = 0;
|
|
780
|
+
for (const auto s : sizes) {
|
|
781
|
+
size += s;
|
|
782
|
+
}
|
|
783
|
+
return size;
|
|
784
|
+
}
|
|
785
|
+
};
|
|
786
|
+
|
|
787
|
+
template <class O>
|
|
788
|
+
void reserveInTarget(const O& o) {
|
|
789
|
+
(void)o;
|
|
790
|
+
}
|
|
791
|
+
template <class T, class O>
|
|
792
|
+
void reserveInTarget(const T& v, const O& o) {
|
|
793
|
+
o->reserve(estimateSpaceNeeded(v));
|
|
794
|
+
}
|
|
795
|
+
template <class T0, class T1, class... Ts>
|
|
796
|
+
void reserveInTarget(const T0& v0, const T1& v1, const Ts&... vs) {
|
|
797
|
+
using seq = std::index_sequence_for<T0, T1, Ts...>;
|
|
798
|
+
getLastElement(vs...)->reserve(
|
|
799
|
+
EstimateSpaceToReserveAll<seq>::call(v0, v1, vs...));
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
template <class Delimiter, class... Ts>
|
|
803
|
+
void reserveInTargetDelim(const Delimiter& d, const Ts&... vs) {
|
|
804
|
+
static_assert(sizeof...(vs) >= 2, "Needs at least 2 args");
|
|
805
|
+
using seq = std::index_sequence_for<Ts...>;
|
|
806
|
+
size_t fordelim = (sizeof...(vs) - 2) * estimateSpaceNeeded(d);
|
|
807
|
+
getLastElement(vs...)->reserve(
|
|
808
|
+
fordelim + EstimateSpaceToReserveAll<seq>::call(vs...));
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
template <class T>
|
|
812
|
+
FOLLY_ERASE constexpr int toAppendStrImplOne(
|
|
813
|
+
std::false_type, const T& v, void*) {
|
|
814
|
+
(void)v;
|
|
815
|
+
return 0;
|
|
816
|
+
}
|
|
817
|
+
template <class T, class Tgt>
|
|
818
|
+
FOLLY_ERASE int toAppendStrImplOne(std::true_type, const T& v, Tgt result) {
|
|
819
|
+
return toAppend(v, result), 0;
|
|
820
|
+
}
|
|
821
|
+
template <typename>
|
|
822
|
+
struct ToAppendStrImplAll;
|
|
823
|
+
template <size_t... I>
|
|
824
|
+
struct ToAppendStrImplAll<std::index_sequence<I...>> {
|
|
825
|
+
template <class... T>
|
|
826
|
+
static void call(const T&... v) {
|
|
827
|
+
using _ = int[];
|
|
828
|
+
auto r = getLastElement(v...);
|
|
829
|
+
void(_{toAppendStrImplOne(bool_constant<I + 1 < sizeof...(T)>{}, v, r)...});
|
|
830
|
+
}
|
|
831
|
+
};
|
|
832
|
+
|
|
833
|
+
template <class Delimiter, class T>
|
|
834
|
+
FOLLY_ERASE constexpr int toAppendDelimStrImplOne(
|
|
835
|
+
index_constant<0>, const Delimiter& d, const T& v, void*) {
|
|
836
|
+
(void)d;
|
|
837
|
+
(void)v;
|
|
838
|
+
return 0;
|
|
839
|
+
}
|
|
840
|
+
template <class Delimiter, class T, class Tgt>
|
|
841
|
+
FOLLY_ERASE int toAppendDelimStrImplOne(
|
|
842
|
+
index_constant<1>, const Delimiter& d, const T& v, Tgt result) {
|
|
843
|
+
(void)d;
|
|
844
|
+
toAppend(v, result);
|
|
845
|
+
return 0;
|
|
846
|
+
}
|
|
847
|
+
template <class Delimiter, class T, class Tgt>
|
|
848
|
+
FOLLY_ERASE int toAppendDelimStrImplOne(
|
|
849
|
+
index_constant<2>, const Delimiter& d, const T& v, Tgt result) {
|
|
850
|
+
toAppend(v, result);
|
|
851
|
+
toAppend(d, result);
|
|
852
|
+
return 0;
|
|
853
|
+
}
|
|
854
|
+
template <typename>
|
|
855
|
+
struct ToAppendDelimStrImplAll;
|
|
856
|
+
template <size_t... I>
|
|
857
|
+
struct ToAppendDelimStrImplAll<std::index_sequence<I...>> {
|
|
858
|
+
template <size_t J, size_t N = sizeof...(I), size_t K = N - J - 1>
|
|
859
|
+
using tag = index_constant<(K < 2 ? K : 2)>;
|
|
860
|
+
template <class Delimiter, class... T>
|
|
861
|
+
static void call(const Delimiter& d, const T&... v) {
|
|
862
|
+
using _ = int[];
|
|
863
|
+
auto r = detail::getLastElement(v...);
|
|
864
|
+
void(_{toAppendDelimStrImplOne(tag<I>{}, d, v, r)...});
|
|
865
|
+
}
|
|
866
|
+
};
|
|
867
|
+
template <class Delimiter, class T, class... Ts>
|
|
868
|
+
typename std::enable_if<
|
|
869
|
+
sizeof...(Ts) >= 2 &&
|
|
870
|
+
IsSomeString<typename std::remove_pointer<
|
|
871
|
+
detail::LastElement<Ts...>>::type>::value>::type
|
|
872
|
+
toAppendDelimStrImpl(const Delimiter& delim, const T& v, const Ts&... vs) {
|
|
873
|
+
using seq = std::index_sequence_for<T, Ts...>;
|
|
874
|
+
ToAppendDelimStrImplAll<seq>::call(delim, v, vs...);
|
|
875
|
+
}
|
|
876
|
+
} // namespace detail
|
|
877
|
+
#endif
|
|
878
|
+
|
|
879
|
+
/**
|
|
880
|
+
* Variadic conversion to string. Appends each element in turn.
|
|
881
|
+
* If we have two or more things to append, we will not reserve
|
|
882
|
+
* the space for them and will depend on strings exponential growth.
|
|
883
|
+
* If you just append once consider using toAppendFit which reserves
|
|
884
|
+
* the space needed (but does not have exponential as a result).
|
|
885
|
+
*
|
|
886
|
+
* Custom implementations of toAppend() can be provided in the same namespace as
|
|
887
|
+
* the type to customize printing. estimateSpaceNeed() may also be provided to
|
|
888
|
+
* avoid reallocations in toAppendFit():
|
|
889
|
+
*
|
|
890
|
+
* namespace other_namespace {
|
|
891
|
+
*
|
|
892
|
+
* template <class String>
|
|
893
|
+
* void toAppend(const OtherType&, String* out);
|
|
894
|
+
*
|
|
895
|
+
* // optional
|
|
896
|
+
* size_t estimateSpaceNeeded(const OtherType&);
|
|
897
|
+
*
|
|
898
|
+
* }
|
|
899
|
+
*/
|
|
900
|
+
template <class... Ts>
|
|
901
|
+
typename std::enable_if<
|
|
902
|
+
sizeof...(Ts) >= 3 &&
|
|
903
|
+
IsSomeString<typename std::remove_pointer<
|
|
904
|
+
detail::LastElement<Ts...>>::type>::value>::type
|
|
905
|
+
toAppend(const Ts&... vs) {
|
|
906
|
+
using seq = std::index_sequence_for<Ts...>;
|
|
907
|
+
detail::ToAppendStrImplAll<seq>::call(vs...);
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
#ifdef _MSC_VER
|
|
911
|
+
// Special case pid_t on MSVC, because it's a void* rather than an
|
|
912
|
+
// integral type. We can't do a global special case because this is already
|
|
913
|
+
// dangerous enough (as most pointers will implicitly convert to a void*)
|
|
914
|
+
// just doing it for MSVC.
|
|
915
|
+
template <class Tgt>
|
|
916
|
+
void toAppend(const pid_t a, Tgt* res) {
|
|
917
|
+
toAppend(uint64_t(a), res);
|
|
918
|
+
}
|
|
919
|
+
#endif
|
|
920
|
+
|
|
921
|
+
/**
|
|
922
|
+
* @overloadbrief toAppend, but pre-allocate the exact amount of space required.
|
|
923
|
+
*
|
|
924
|
+
* Special version of the call that preallocates exactly as much memory
|
|
925
|
+
* as need for arguments to be stored in target. This means we are
|
|
926
|
+
* not doing exponential growth when we append. If you are using it
|
|
927
|
+
* in a loop you are aiming at your foot with a big perf-destroying
|
|
928
|
+
* bazooka.
|
|
929
|
+
* On the other hand if you are appending to a string once, this
|
|
930
|
+
* will probably save a few calls to malloc.
|
|
931
|
+
*/
|
|
932
|
+
template <class... Ts>
|
|
933
|
+
typename std::enable_if<IsSomeString<typename std::remove_pointer<
|
|
934
|
+
detail::LastElement<Ts...>>::type>::value>::type
|
|
935
|
+
toAppendFit(const Ts&... vs) {
|
|
936
|
+
::folly::detail::reserveInTarget(vs...);
|
|
937
|
+
toAppend(vs...);
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
template <class Ts>
|
|
941
|
+
void toAppendFit(const Ts&) {}
|
|
942
|
+
|
|
943
|
+
/**
|
|
944
|
+
* Variadic base case: do nothing.
|
|
945
|
+
*/
|
|
946
|
+
template <class Tgt>
|
|
947
|
+
typename std::enable_if<IsSomeString<Tgt>::value>::type toAppend(
|
|
948
|
+
Tgt* /* result */) {}
|
|
949
|
+
|
|
950
|
+
/**
|
|
951
|
+
* @overloadbrief Use a specified delimiter between appendees.
|
|
952
|
+
*
|
|
953
|
+
* Variadic base case: do nothing.
|
|
954
|
+
*/
|
|
955
|
+
template <class Delimiter, class Tgt>
|
|
956
|
+
typename std::enable_if<IsSomeString<Tgt>::value>::type toAppendDelim(
|
|
957
|
+
const Delimiter& /* delim */, Tgt* /* result */) {}
|
|
958
|
+
|
|
959
|
+
/**
|
|
960
|
+
* 1 element: same as toAppend.
|
|
961
|
+
*/
|
|
962
|
+
template <class Delimiter, class T, class Tgt>
|
|
963
|
+
typename std::enable_if<IsSomeString<Tgt>::value>::type toAppendDelim(
|
|
964
|
+
const Delimiter& /* delim */, const T& v, Tgt* tgt) {
|
|
965
|
+
toAppend(v, tgt);
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/**
|
|
969
|
+
* Append to string with a delimiter in between elements. Check out
|
|
970
|
+
* comments for toAppend for details about memory allocation.
|
|
971
|
+
*/
|
|
972
|
+
template <class Delimiter, class... Ts>
|
|
973
|
+
typename std::enable_if<
|
|
974
|
+
sizeof...(Ts) >= 3 &&
|
|
975
|
+
IsSomeString<typename std::remove_pointer<
|
|
976
|
+
detail::LastElement<Ts...>>::type>::value>::type
|
|
977
|
+
toAppendDelim(const Delimiter& delim, const Ts&... vs) {
|
|
978
|
+
detail::toAppendDelimStrImpl(delim, vs...);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
/**
|
|
982
|
+
* @overloadbrief toAppend with custom delimiter and exact pre-allocation.
|
|
983
|
+
*
|
|
984
|
+
* Detail in comment for toAppendFit
|
|
985
|
+
*/
|
|
986
|
+
template <class Delimiter, class... Ts>
|
|
987
|
+
typename std::enable_if<IsSomeString<typename std::remove_pointer<
|
|
988
|
+
detail::LastElement<Ts...>>::type>::value>::type
|
|
989
|
+
toAppendDelimFit(const Delimiter& delim, const Ts&... vs) {
|
|
990
|
+
detail::reserveInTargetDelim(delim, vs...);
|
|
991
|
+
toAppendDelim(delim, vs...);
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
template <class De, class Ts>
|
|
995
|
+
void toAppendDelimFit(const De&, const Ts&) {}
|
|
996
|
+
|
|
997
|
+
/**
|
|
998
|
+
* to<SomeString>(v1, v2, ...) uses toAppend() (see below) as back-end
|
|
999
|
+
* for all types.
|
|
1000
|
+
*/
|
|
1001
|
+
template <class Tgt, class... Ts>
|
|
1002
|
+
typename std::enable_if<
|
|
1003
|
+
IsSomeString<Tgt>::value &&
|
|
1004
|
+
(sizeof...(Ts) != 1 ||
|
|
1005
|
+
!std::is_same<Tgt, detail::LastElement<void, Ts...>>::value),
|
|
1006
|
+
Tgt>::type
|
|
1007
|
+
to(const Ts&... vs) {
|
|
1008
|
+
Tgt result;
|
|
1009
|
+
toAppendFit(vs..., &result);
|
|
1010
|
+
return result;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
/**
|
|
1014
|
+
* Special version of to<SomeString> for floating point. When calling
|
|
1015
|
+
* folly::to<SomeString>(double), generic implementation above will
|
|
1016
|
+
* firstly reserve 24 (or 25 when negative value) bytes. This will
|
|
1017
|
+
* introduce a malloc call for most mainstream string implementations.
|
|
1018
|
+
*
|
|
1019
|
+
* But for most cases, a floating point doesn't need 24 (or 25) bytes to
|
|
1020
|
+
* be converted as a string.
|
|
1021
|
+
*
|
|
1022
|
+
* This special version will not do string reserve.
|
|
1023
|
+
*/
|
|
1024
|
+
template <class Tgt, class Src>
|
|
1025
|
+
typename std::enable_if<
|
|
1026
|
+
IsSomeString<Tgt>::value && std::is_floating_point<Src>::value,
|
|
1027
|
+
Tgt>::type
|
|
1028
|
+
to(Src value) {
|
|
1029
|
+
Tgt result;
|
|
1030
|
+
toAppend(value, &result);
|
|
1031
|
+
return result;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
/**
|
|
1035
|
+
* @overloadbrief Like `to`, but uses a custom delimiter.
|
|
1036
|
+
*
|
|
1037
|
+
* toDelim<SomeString>(SomeString str) returns itself.
|
|
1038
|
+
*/
|
|
1039
|
+
template <class Tgt, class Delim, class Src>
|
|
1040
|
+
typename std::enable_if<
|
|
1041
|
+
IsSomeString<Tgt>::value &&
|
|
1042
|
+
std::is_same<Tgt, typename std::decay<Src>::type>::value,
|
|
1043
|
+
Tgt>::type
|
|
1044
|
+
toDelim(const Delim& /* delim */, Src&& value) {
|
|
1045
|
+
return static_cast<Src&&>(value);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
/**
|
|
1049
|
+
* toDelim<SomeString>(delim, v1, v2, ...) uses toAppendDelim() as
|
|
1050
|
+
* back-end for all types.
|
|
1051
|
+
*/
|
|
1052
|
+
template <class Tgt, class Delim, class... Ts>
|
|
1053
|
+
typename std::enable_if<
|
|
1054
|
+
IsSomeString<Tgt>::value &&
|
|
1055
|
+
(sizeof...(Ts) != 1 ||
|
|
1056
|
+
!std::is_same<Tgt, detail::LastElement<void, Ts...>>::value),
|
|
1057
|
+
Tgt>::type
|
|
1058
|
+
toDelim(const Delim& delim, const Ts&... vs) {
|
|
1059
|
+
Tgt result;
|
|
1060
|
+
toAppendDelimFit(delim, vs..., &result);
|
|
1061
|
+
return result;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
/**
|
|
1065
|
+
* Conversions from string types to integral types.
|
|
1066
|
+
*/
|
|
1067
|
+
|
|
1068
|
+
namespace detail {
|
|
1069
|
+
|
|
1070
|
+
Expected<bool, ConversionCode> str_to_bool(StringPiece* src) noexcept;
|
|
1071
|
+
|
|
1072
|
+
template <typename T>
|
|
1073
|
+
Expected<T, ConversionCode> str_to_floating(StringPiece* src) noexcept;
|
|
1074
|
+
|
|
1075
|
+
extern template Expected<float, ConversionCode> str_to_floating<float>(
|
|
1076
|
+
StringPiece* src) noexcept;
|
|
1077
|
+
extern template Expected<double, ConversionCode> str_to_floating<double>(
|
|
1078
|
+
StringPiece* src) noexcept;
|
|
1079
|
+
|
|
1080
|
+
template <class Tgt>
|
|
1081
|
+
Expected<Tgt, ConversionCode> digits_to(const char* b, const char* e) noexcept;
|
|
1082
|
+
|
|
1083
|
+
extern template Expected<char, ConversionCode> digits_to<char>(
|
|
1084
|
+
const char*, const char*) noexcept;
|
|
1085
|
+
extern template Expected<signed char, ConversionCode> digits_to<signed char>(
|
|
1086
|
+
const char*, const char*) noexcept;
|
|
1087
|
+
extern template Expected<unsigned char, ConversionCode>
|
|
1088
|
+
digits_to<unsigned char>(const char*, const char*) noexcept;
|
|
1089
|
+
|
|
1090
|
+
extern template Expected<short, ConversionCode> digits_to<short>(
|
|
1091
|
+
const char*, const char*) noexcept;
|
|
1092
|
+
extern template Expected<unsigned short, ConversionCode>
|
|
1093
|
+
digits_to<unsigned short>(const char*, const char*) noexcept;
|
|
1094
|
+
|
|
1095
|
+
extern template Expected<int, ConversionCode> digits_to<int>(
|
|
1096
|
+
const char*, const char*) noexcept;
|
|
1097
|
+
extern template Expected<unsigned int, ConversionCode> digits_to<unsigned int>(
|
|
1098
|
+
const char*, const char*) noexcept;
|
|
1099
|
+
|
|
1100
|
+
extern template Expected<long, ConversionCode> digits_to<long>(
|
|
1101
|
+
const char*, const char*) noexcept;
|
|
1102
|
+
extern template Expected<unsigned long, ConversionCode>
|
|
1103
|
+
digits_to<unsigned long>(const char*, const char*) noexcept;
|
|
1104
|
+
|
|
1105
|
+
extern template Expected<long long, ConversionCode> digits_to<long long>(
|
|
1106
|
+
const char*, const char*) noexcept;
|
|
1107
|
+
extern template Expected<unsigned long long, ConversionCode>
|
|
1108
|
+
digits_to<unsigned long long>(const char*, const char*) noexcept;
|
|
1109
|
+
|
|
1110
|
+
#if FOLLY_HAVE_INT128_T
|
|
1111
|
+
extern template Expected<__int128, ConversionCode> digits_to<__int128>(
|
|
1112
|
+
const char*, const char*) noexcept;
|
|
1113
|
+
extern template Expected<unsigned __int128, ConversionCode>
|
|
1114
|
+
digits_to<unsigned __int128>(const char*, const char*) noexcept;
|
|
1115
|
+
#endif
|
|
1116
|
+
|
|
1117
|
+
template <class T>
|
|
1118
|
+
Expected<T, ConversionCode> str_to_integral(StringPiece* src) noexcept;
|
|
1119
|
+
|
|
1120
|
+
extern template Expected<char, ConversionCode> str_to_integral<char>(
|
|
1121
|
+
StringPiece* src) noexcept;
|
|
1122
|
+
extern template Expected<signed char, ConversionCode>
|
|
1123
|
+
str_to_integral<signed char>(StringPiece* src) noexcept;
|
|
1124
|
+
extern template Expected<unsigned char, ConversionCode>
|
|
1125
|
+
str_to_integral<unsigned char>(StringPiece* src) noexcept;
|
|
1126
|
+
|
|
1127
|
+
extern template Expected<short, ConversionCode> str_to_integral<short>(
|
|
1128
|
+
StringPiece* src) noexcept;
|
|
1129
|
+
extern template Expected<unsigned short, ConversionCode>
|
|
1130
|
+
str_to_integral<unsigned short>(StringPiece* src) noexcept;
|
|
1131
|
+
|
|
1132
|
+
extern template Expected<int, ConversionCode> str_to_integral<int>(
|
|
1133
|
+
StringPiece* src) noexcept;
|
|
1134
|
+
extern template Expected<unsigned int, ConversionCode>
|
|
1135
|
+
str_to_integral<unsigned int>(StringPiece* src) noexcept;
|
|
1136
|
+
|
|
1137
|
+
extern template Expected<long, ConversionCode> str_to_integral<long>(
|
|
1138
|
+
StringPiece* src) noexcept;
|
|
1139
|
+
extern template Expected<unsigned long, ConversionCode>
|
|
1140
|
+
str_to_integral<unsigned long>(StringPiece* src) noexcept;
|
|
1141
|
+
|
|
1142
|
+
extern template Expected<long long, ConversionCode> str_to_integral<long long>(
|
|
1143
|
+
StringPiece* src) noexcept;
|
|
1144
|
+
extern template Expected<unsigned long long, ConversionCode>
|
|
1145
|
+
str_to_integral<unsigned long long>(StringPiece* src) noexcept;
|
|
1146
|
+
|
|
1147
|
+
#if FOLLY_HAVE_INT128_T
|
|
1148
|
+
extern template Expected<__int128, ConversionCode> str_to_integral<__int128>(
|
|
1149
|
+
StringPiece* src) noexcept;
|
|
1150
|
+
extern template Expected<unsigned __int128, ConversionCode>
|
|
1151
|
+
str_to_integral<unsigned __int128>(StringPiece* src) noexcept;
|
|
1152
|
+
#endif
|
|
1153
|
+
|
|
1154
|
+
template <typename T>
|
|
1155
|
+
typename std::
|
|
1156
|
+
enable_if<std::is_same<T, bool>::value, Expected<T, ConversionCode>>::type
|
|
1157
|
+
convertTo(StringPiece* src) noexcept {
|
|
1158
|
+
return str_to_bool(src);
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
template <typename T>
|
|
1162
|
+
typename std::enable_if<
|
|
1163
|
+
std::is_floating_point<T>::value,
|
|
1164
|
+
Expected<T, ConversionCode>>::type
|
|
1165
|
+
convertTo(StringPiece* src) noexcept {
|
|
1166
|
+
return str_to_floating<T>(src);
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
template <typename T>
|
|
1170
|
+
typename std::enable_if<
|
|
1171
|
+
is_integral_v<T> && !std::is_same<T, bool>::value,
|
|
1172
|
+
Expected<T, ConversionCode>>::type
|
|
1173
|
+
convertTo(StringPiece* src) noexcept {
|
|
1174
|
+
return str_to_integral<T>(src);
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
} // namespace detail
|
|
1178
|
+
|
|
1179
|
+
/**
|
|
1180
|
+
* String represented as a pair of pointers to char to unsigned
|
|
1181
|
+
* integrals. Assumes NO whitespace before or after.
|
|
1182
|
+
*/
|
|
1183
|
+
template <typename Tgt>
|
|
1184
|
+
typename std::enable_if<
|
|
1185
|
+
is_integral_v<Tgt> && !std::is_same<Tgt, bool>::value,
|
|
1186
|
+
Expected<Tgt, ConversionCode>>::type
|
|
1187
|
+
tryTo(const char* b, const char* e) {
|
|
1188
|
+
return detail::digits_to<Tgt>(b, e);
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
template <typename Tgt>
|
|
1192
|
+
typename std::enable_if< //
|
|
1193
|
+
is_integral_v<Tgt> && !std::is_same<Tgt, bool>::value,
|
|
1194
|
+
Tgt>::type
|
|
1195
|
+
to(const char* b, const char* e) {
|
|
1196
|
+
return tryTo<Tgt>(b, e).thenOrThrow(identity, [=](ConversionCode code) {
|
|
1197
|
+
return makeConversionError(code, StringPiece(b, e));
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
/**
|
|
1202
|
+
* Conversions from string types to arithmetic types.
|
|
1203
|
+
*/
|
|
1204
|
+
|
|
1205
|
+
/**
|
|
1206
|
+
* Parsing strings to numeric types.
|
|
1207
|
+
*/
|
|
1208
|
+
template <typename Tgt>
|
|
1209
|
+
FOLLY_NODISCARD inline typename std::enable_if< //
|
|
1210
|
+
is_arithmetic_v<Tgt>,
|
|
1211
|
+
Expected<StringPiece, ConversionCode>>::type
|
|
1212
|
+
parseTo(StringPiece src, Tgt& out) {
|
|
1213
|
+
return detail::convertTo<Tgt>(&src).then(
|
|
1214
|
+
[&](Tgt res) { return void(out = res), src; });
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
/**
|
|
1218
|
+
* Integral / Floating Point to integral / Floating Point
|
|
1219
|
+
*/
|
|
1220
|
+
|
|
1221
|
+
namespace detail {
|
|
1222
|
+
|
|
1223
|
+
/**
|
|
1224
|
+
* Bool to integral/float doesn't need any special checks, and this
|
|
1225
|
+
* overload means we aren't trying to see if a bool is less than
|
|
1226
|
+
* an integer.
|
|
1227
|
+
*/
|
|
1228
|
+
template <class Tgt>
|
|
1229
|
+
typename std::enable_if<
|
|
1230
|
+
!std::is_same<Tgt, bool>::value &&
|
|
1231
|
+
(is_integral_v<Tgt> || std::is_floating_point<Tgt>::value),
|
|
1232
|
+
Expected<Tgt, ConversionCode>>::type
|
|
1233
|
+
convertTo(const bool& value) noexcept {
|
|
1234
|
+
return static_cast<Tgt>(value ? 1 : 0);
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
/**
|
|
1238
|
+
* Checked conversion from integral to integral. The checks are only
|
|
1239
|
+
* performed when meaningful, e.g. conversion from int to long goes
|
|
1240
|
+
* unchecked.
|
|
1241
|
+
*/
|
|
1242
|
+
template <class Tgt, class Src>
|
|
1243
|
+
typename std::enable_if<
|
|
1244
|
+
is_integral_v<Src> && !std::is_same<Tgt, Src>::value &&
|
|
1245
|
+
!std::is_same<Tgt, bool>::value && is_integral_v<Tgt>,
|
|
1246
|
+
Expected<Tgt, ConversionCode>>::type
|
|
1247
|
+
convertTo(const Src& value) noexcept {
|
|
1248
|
+
if /* constexpr */ (
|
|
1249
|
+
make_unsigned_t<Tgt>(std::numeric_limits<Tgt>::max()) <
|
|
1250
|
+
make_unsigned_t<Src>(std::numeric_limits<Src>::max())) {
|
|
1251
|
+
if (greater_than<Tgt, std::numeric_limits<Tgt>::max()>(value)) {
|
|
1252
|
+
return makeUnexpected(ConversionCode::ARITH_POSITIVE_OVERFLOW);
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
if /* constexpr */ (
|
|
1256
|
+
is_signed_v<Src> && (!is_signed_v<Tgt> || sizeof(Src) > sizeof(Tgt))) {
|
|
1257
|
+
if (less_than<Tgt, std::numeric_limits<Tgt>::min()>(value)) {
|
|
1258
|
+
return makeUnexpected(ConversionCode::ARITH_NEGATIVE_OVERFLOW);
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
return static_cast<Tgt>(value);
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
/**
|
|
1265
|
+
* Checked conversion from floating to floating. The checks are only
|
|
1266
|
+
* performed when meaningful, e.g. conversion from float to double goes
|
|
1267
|
+
* unchecked.
|
|
1268
|
+
*/
|
|
1269
|
+
template <class Tgt, class Src>
|
|
1270
|
+
typename std::enable_if<
|
|
1271
|
+
std::is_floating_point<Tgt>::value && std::is_floating_point<Src>::value &&
|
|
1272
|
+
!std::is_same<Tgt, Src>::value,
|
|
1273
|
+
Expected<Tgt, ConversionCode>>::type
|
|
1274
|
+
convertTo(const Src& value) noexcept {
|
|
1275
|
+
if (FOLLY_UNLIKELY(std::isinf(value))) {
|
|
1276
|
+
return static_cast<Tgt>(value);
|
|
1277
|
+
}
|
|
1278
|
+
if /* constexpr */ (
|
|
1279
|
+
std::numeric_limits<Tgt>::max() < std::numeric_limits<Src>::max()) {
|
|
1280
|
+
if (value > std::numeric_limits<Tgt>::max()) {
|
|
1281
|
+
return makeUnexpected(ConversionCode::ARITH_POSITIVE_OVERFLOW);
|
|
1282
|
+
}
|
|
1283
|
+
if (value < std::numeric_limits<Tgt>::lowest()) {
|
|
1284
|
+
return makeUnexpected(ConversionCode::ARITH_NEGATIVE_OVERFLOW);
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
return static_cast<Tgt>(value);
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
/**
|
|
1291
|
+
* Check if a floating point value can safely be converted to an
|
|
1292
|
+
* integer value without triggering undefined behaviour.
|
|
1293
|
+
*/
|
|
1294
|
+
template <typename Tgt, typename Src>
|
|
1295
|
+
inline typename std::enable_if<
|
|
1296
|
+
std::is_floating_point<Src>::value && is_integral_v<Tgt> &&
|
|
1297
|
+
!std::is_same<Tgt, bool>::value,
|
|
1298
|
+
bool>::type
|
|
1299
|
+
checkConversion(const Src& value) {
|
|
1300
|
+
constexpr Src tgtMaxAsSrc = static_cast<Src>(std::numeric_limits<Tgt>::max());
|
|
1301
|
+
constexpr Src tgtMinAsSrc = static_cast<Src>(std::numeric_limits<Tgt>::min());
|
|
1302
|
+
// NOTE: The following two comparisons also handle the case where value is
|
|
1303
|
+
// NaN, as all comparisons with NaN are false.
|
|
1304
|
+
if (!(value < tgtMaxAsSrc)) {
|
|
1305
|
+
if (!(value <= tgtMaxAsSrc)) {
|
|
1306
|
+
return false;
|
|
1307
|
+
}
|
|
1308
|
+
const Src mmax = folly::nextafter(tgtMaxAsSrc, Src());
|
|
1309
|
+
if (static_cast<Tgt>(value - mmax) >
|
|
1310
|
+
std::numeric_limits<Tgt>::max() - static_cast<Tgt>(mmax)) {
|
|
1311
|
+
return false;
|
|
1312
|
+
}
|
|
1313
|
+
} else if (value <= tgtMinAsSrc) {
|
|
1314
|
+
if (value < tgtMinAsSrc) {
|
|
1315
|
+
return false;
|
|
1316
|
+
}
|
|
1317
|
+
const Src mmin = folly::nextafter(tgtMinAsSrc, Src());
|
|
1318
|
+
if (static_cast<Tgt>(value - mmin) <
|
|
1319
|
+
std::numeric_limits<Tgt>::min() - static_cast<Tgt>(mmin)) {
|
|
1320
|
+
return false;
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
return true;
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
// Integers can always safely be converted to floating point values
|
|
1327
|
+
template <typename Tgt, typename Src>
|
|
1328
|
+
constexpr typename std::enable_if<
|
|
1329
|
+
is_integral_v<Src> && std::is_floating_point<Tgt>::value,
|
|
1330
|
+
bool>::type
|
|
1331
|
+
checkConversion(const Src&) {
|
|
1332
|
+
return true;
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
// Also, floating point values can always be safely converted to bool
|
|
1336
|
+
// Per the standard, any floating point value that is not zero will yield true
|
|
1337
|
+
template <typename Tgt, typename Src>
|
|
1338
|
+
constexpr typename std::enable_if<
|
|
1339
|
+
std::is_floating_point<Src>::value && std::is_same<Tgt, bool>::value,
|
|
1340
|
+
bool>::type
|
|
1341
|
+
checkConversion(const Src&) {
|
|
1342
|
+
return true;
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
/**
|
|
1346
|
+
* Checked conversion from integral to floating point and back. The
|
|
1347
|
+
* result must be convertible back to the source type without loss of
|
|
1348
|
+
* precision. This seems Draconian but sometimes is what's needed, and
|
|
1349
|
+
* complements existing routines nicely. For various rounding
|
|
1350
|
+
* routines, see <math>.
|
|
1351
|
+
*/
|
|
1352
|
+
template <typename Tgt, typename Src>
|
|
1353
|
+
typename std::enable_if<
|
|
1354
|
+
(is_integral_v<Src> && std::is_floating_point<Tgt>::value) ||
|
|
1355
|
+
(std::is_floating_point<Src>::value && is_integral_v<Tgt>),
|
|
1356
|
+
Expected<Tgt, ConversionCode>>::type
|
|
1357
|
+
convertTo(const Src& value) noexcept {
|
|
1358
|
+
if (LIKELY(checkConversion<Tgt>(value))) {
|
|
1359
|
+
Tgt result = static_cast<Tgt>(value);
|
|
1360
|
+
if (LIKELY(checkConversion<Src>(result))) {
|
|
1361
|
+
Src witness = static_cast<Src>(result);
|
|
1362
|
+
if (LIKELY(value == witness)) {
|
|
1363
|
+
return result;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
return makeUnexpected(ConversionCode::ARITH_LOSS_OF_PRECISION);
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
template <typename Tgt, typename Src>
|
|
1371
|
+
inline std::string errorValue(const Src& value) {
|
|
1372
|
+
return to<std::string>("(", pretty_name<Tgt>(), ") ", value);
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
template <typename Tgt, typename Src>
|
|
1376
|
+
using IsArithToArith = bool_constant<
|
|
1377
|
+
!std::is_same<Tgt, Src>::value && !std::is_same<Tgt, bool>::value &&
|
|
1378
|
+
is_arithmetic_v<Src> && is_arithmetic_v<Tgt>>;
|
|
1379
|
+
|
|
1380
|
+
} // namespace detail
|
|
1381
|
+
|
|
1382
|
+
template <typename Tgt, typename Src>
|
|
1383
|
+
typename std::enable_if<
|
|
1384
|
+
detail::IsArithToArith<Tgt, Src>::value,
|
|
1385
|
+
Expected<Tgt, ConversionCode>>::type
|
|
1386
|
+
tryTo(const Src& value) noexcept {
|
|
1387
|
+
return detail::convertTo<Tgt>(value);
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
template <typename Tgt, typename Src>
|
|
1391
|
+
typename std::enable_if<detail::IsArithToArith<Tgt, Src>::value, Tgt>::type to(
|
|
1392
|
+
const Src& value) {
|
|
1393
|
+
return tryTo<Tgt>(value).thenOrThrow(identity, [&](ConversionCode e) {
|
|
1394
|
+
return makeConversionError(e, detail::errorValue<Tgt>(value));
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
/**
|
|
1399
|
+
* Custom Conversions
|
|
1400
|
+
*
|
|
1401
|
+
* Any type can be used with folly::to by implementing parseTo. The
|
|
1402
|
+
* implementation should be provided in the namespace of the type to facilitate
|
|
1403
|
+
* argument-dependent lookup:
|
|
1404
|
+
*
|
|
1405
|
+
* namespace other_namespace {
|
|
1406
|
+
* ::folly::Expected<::folly::StringPiece, SomeErrorCode>
|
|
1407
|
+
* parseTo(::folly::StringPiece, OtherType&) noexcept;
|
|
1408
|
+
* }
|
|
1409
|
+
*/
|
|
1410
|
+
template <class T>
|
|
1411
|
+
FOLLY_NODISCARD typename std::enable_if<
|
|
1412
|
+
std::is_enum<T>::value,
|
|
1413
|
+
Expected<StringPiece, ConversionCode>>::type
|
|
1414
|
+
parseTo(StringPiece in, T& out) noexcept {
|
|
1415
|
+
typename std::underlying_type<T>::type tmp{};
|
|
1416
|
+
auto restOrError = parseTo(in, tmp);
|
|
1417
|
+
out = static_cast<T>(tmp); // Harmless if parseTo fails
|
|
1418
|
+
return restOrError;
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
FOLLY_NODISCARD
|
|
1422
|
+
inline Expected<StringPiece, ConversionCode> parseTo(
|
|
1423
|
+
StringPiece in, StringPiece& out) noexcept {
|
|
1424
|
+
out = in;
|
|
1425
|
+
return StringPiece{in.end(), in.end()};
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
namespace detail {
|
|
1429
|
+
|
|
1430
|
+
template <class Str>
|
|
1431
|
+
FOLLY_ERASE Expected<StringPiece, ConversionCode> parseToStr(
|
|
1432
|
+
StringPiece in, Str& out) {
|
|
1433
|
+
out.clear();
|
|
1434
|
+
out.append(in.data(), in.size()); // TODO try/catch?
|
|
1435
|
+
return StringPiece{in.end(), in.end()};
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
} // namespace detail
|
|
1439
|
+
|
|
1440
|
+
FOLLY_NODISCARD
|
|
1441
|
+
inline Expected<StringPiece, ConversionCode> parseTo(
|
|
1442
|
+
StringPiece in, std::string& out) {
|
|
1443
|
+
return detail::parseToStr(in, out);
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
#if FOLLY_HAS_STRING_VIEW
|
|
1447
|
+
FOLLY_NODISCARD
|
|
1448
|
+
inline Expected<StringPiece, ConversionCode> parseTo(
|
|
1449
|
+
StringPiece in, std::string_view& out) {
|
|
1450
|
+
out = std::string_view(in.data(), in.size());
|
|
1451
|
+
return StringPiece{in.end(), in.end()};
|
|
1452
|
+
}
|
|
1453
|
+
#endif
|
|
1454
|
+
|
|
1455
|
+
FOLLY_NODISCARD
|
|
1456
|
+
inline Expected<StringPiece, ConversionCode> parseTo(
|
|
1457
|
+
StringPiece in, fbstring& out) {
|
|
1458
|
+
return detail::parseToStr(in, out);
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
template <class Str>
|
|
1462
|
+
FOLLY_NODISCARD inline typename std::enable_if<
|
|
1463
|
+
IsSomeString<Str>::value,
|
|
1464
|
+
Expected<StringPiece, ConversionCode>>::type
|
|
1465
|
+
parseTo(StringPiece in, Str& out) {
|
|
1466
|
+
return detail::parseToStr(in, out);
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
namespace detail {
|
|
1470
|
+
template <typename Tgt>
|
|
1471
|
+
using ParseToResult = decltype(parseTo(StringPiece{}, std::declval<Tgt&>()));
|
|
1472
|
+
|
|
1473
|
+
struct CheckTrailingSpace {
|
|
1474
|
+
Expected<Unit, ConversionCode> operator()(StringPiece sp) const {
|
|
1475
|
+
auto e = enforceWhitespaceErr(sp);
|
|
1476
|
+
if (UNLIKELY(e != ConversionCode::SUCCESS)) {
|
|
1477
|
+
return makeUnexpected(e);
|
|
1478
|
+
}
|
|
1479
|
+
return unit;
|
|
1480
|
+
}
|
|
1481
|
+
};
|
|
1482
|
+
|
|
1483
|
+
template <class Error>
|
|
1484
|
+
struct ReturnUnit {
|
|
1485
|
+
template <class T>
|
|
1486
|
+
constexpr Expected<Unit, Error> operator()(T&&) const {
|
|
1487
|
+
return unit;
|
|
1488
|
+
}
|
|
1489
|
+
};
|
|
1490
|
+
|
|
1491
|
+
// Older versions of the parseTo customization point threw on error and
|
|
1492
|
+
// returned void. Handle that.
|
|
1493
|
+
template <class Tgt>
|
|
1494
|
+
inline typename std::enable_if<
|
|
1495
|
+
std::is_void<ParseToResult<Tgt>>::value,
|
|
1496
|
+
Expected<StringPiece, ConversionCode>>::type
|
|
1497
|
+
parseToWrap(StringPiece sp, Tgt& out) {
|
|
1498
|
+
parseTo(sp, out);
|
|
1499
|
+
return StringPiece(sp.end(), sp.end());
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
template <class Tgt>
|
|
1503
|
+
inline typename std::enable_if<
|
|
1504
|
+
!std::is_void<ParseToResult<Tgt>>::value,
|
|
1505
|
+
ParseToResult<Tgt>>::type
|
|
1506
|
+
parseToWrap(StringPiece sp, Tgt& out) {
|
|
1507
|
+
return parseTo(sp, out);
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
template <typename Tgt>
|
|
1511
|
+
using ParseToError = ExpectedErrorType<decltype(detail::parseToWrap(
|
|
1512
|
+
StringPiece{}, std::declval<Tgt&>()))>;
|
|
1513
|
+
|
|
1514
|
+
} // namespace detail
|
|
1515
|
+
|
|
1516
|
+
/**
|
|
1517
|
+
* String or StringPiece to target conversion. Accepts leading and trailing
|
|
1518
|
+
* whitespace, but no non-space trailing characters.
|
|
1519
|
+
*/
|
|
1520
|
+
|
|
1521
|
+
template <class Tgt>
|
|
1522
|
+
inline typename std::enable_if<
|
|
1523
|
+
!std::is_same<StringPiece, Tgt>::value,
|
|
1524
|
+
Expected<Tgt, detail::ParseToError<Tgt>>>::type
|
|
1525
|
+
tryTo(StringPiece src) {
|
|
1526
|
+
Tgt result{};
|
|
1527
|
+
using Error = detail::ParseToError<Tgt>;
|
|
1528
|
+
using Check = typename std::conditional<
|
|
1529
|
+
is_arithmetic_v<Tgt>,
|
|
1530
|
+
detail::CheckTrailingSpace,
|
|
1531
|
+
detail::ReturnUnit<Error>>::type;
|
|
1532
|
+
return parseTo(src, result).then(Check(), [&](Unit) {
|
|
1533
|
+
return std::move(result);
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
template <class Tgt, class Src>
|
|
1538
|
+
inline typename std::enable_if<
|
|
1539
|
+
IsSomeString<Src>::value && !std::is_same<StringPiece, Tgt>::value,
|
|
1540
|
+
Tgt>::type
|
|
1541
|
+
to(Src const& src) {
|
|
1542
|
+
return to<Tgt>(StringPiece(src.data(), src.size()));
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
template <class Tgt>
|
|
1546
|
+
inline
|
|
1547
|
+
typename std::enable_if<!std::is_same<StringPiece, Tgt>::value, Tgt>::type
|
|
1548
|
+
to(StringPiece src) {
|
|
1549
|
+
Tgt result{};
|
|
1550
|
+
using Error = detail::ParseToError<Tgt>;
|
|
1551
|
+
using Check = typename std::conditional<
|
|
1552
|
+
is_arithmetic_v<Tgt>,
|
|
1553
|
+
detail::CheckTrailingSpace,
|
|
1554
|
+
detail::ReturnUnit<Error>>::type;
|
|
1555
|
+
auto tmp = detail::parseToWrap(src, result);
|
|
1556
|
+
return tmp
|
|
1557
|
+
.thenOrThrow(
|
|
1558
|
+
Check(),
|
|
1559
|
+
[&](Error e) { throw_exception(makeConversionError(e, src)); })
|
|
1560
|
+
.thenOrThrow(
|
|
1561
|
+
[&](Unit) { return std::move(result); },
|
|
1562
|
+
[&](Error e) {
|
|
1563
|
+
throw_exception(makeConversionError(e, tmp.value()));
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
/**
|
|
1568
|
+
* tryTo/to that take the strings by pointer so the caller gets information
|
|
1569
|
+
* about how much of the string was consumed by the conversion. These do not
|
|
1570
|
+
* check for trailing whitespace.
|
|
1571
|
+
*/
|
|
1572
|
+
template <class Tgt>
|
|
1573
|
+
Expected<Tgt, detail::ParseToError<Tgt>> tryTo(StringPiece* src) {
|
|
1574
|
+
Tgt result;
|
|
1575
|
+
return parseTo(*src, result).then([&, src](StringPiece sp) -> Tgt {
|
|
1576
|
+
*src = sp;
|
|
1577
|
+
return std::move(result);
|
|
1578
|
+
});
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
template <class Tgt>
|
|
1582
|
+
Tgt to(StringPiece* src) {
|
|
1583
|
+
Tgt result{};
|
|
1584
|
+
using Error = detail::ParseToError<Tgt>;
|
|
1585
|
+
return parseTo(*src, result)
|
|
1586
|
+
.thenOrThrow(
|
|
1587
|
+
[&, src](StringPiece sp) -> Tgt {
|
|
1588
|
+
*src = sp;
|
|
1589
|
+
return std::move(result);
|
|
1590
|
+
},
|
|
1591
|
+
[=](Error e) { return makeConversionError(e, *src); });
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
/**
|
|
1595
|
+
* Enum to anything and back
|
|
1596
|
+
*/
|
|
1597
|
+
|
|
1598
|
+
template <class Tgt, class Src>
|
|
1599
|
+
typename std::enable_if<
|
|
1600
|
+
std::is_enum<Src>::value && !std::is_same<Src, Tgt>::value &&
|
|
1601
|
+
!std::is_convertible<Tgt, StringPiece>::value,
|
|
1602
|
+
Expected<Tgt, ConversionCode>>::type
|
|
1603
|
+
tryTo(const Src& value) {
|
|
1604
|
+
return tryTo<Tgt>(to_underlying(value));
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
template <class Tgt, class Src>
|
|
1608
|
+
typename std::enable_if<
|
|
1609
|
+
!std::is_convertible<Src, StringPiece>::value && std::is_enum<Tgt>::value &&
|
|
1610
|
+
!std::is_same<Src, Tgt>::value,
|
|
1611
|
+
Expected<Tgt, ConversionCode>>::type
|
|
1612
|
+
tryTo(const Src& value) {
|
|
1613
|
+
using I = typename std::underlying_type<Tgt>::type;
|
|
1614
|
+
return tryTo<I>(value).then([](I i) { return static_cast<Tgt>(i); });
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
template <class Tgt, class Src>
|
|
1618
|
+
typename std::enable_if<
|
|
1619
|
+
std::is_enum<Src>::value && !std::is_same<Src, Tgt>::value &&
|
|
1620
|
+
!std::is_convertible<Tgt, StringPiece>::value,
|
|
1621
|
+
Tgt>::type
|
|
1622
|
+
to(const Src& value) {
|
|
1623
|
+
return to<Tgt>(to_underlying(value));
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
template <class Tgt, class Src>
|
|
1627
|
+
typename std::enable_if<
|
|
1628
|
+
!std::is_convertible<Src, StringPiece>::value && std::is_enum<Tgt>::value &&
|
|
1629
|
+
!std::is_same<Src, Tgt>::value,
|
|
1630
|
+
Tgt>::type
|
|
1631
|
+
to(const Src& value) {
|
|
1632
|
+
return static_cast<Tgt>(to<typename std::underlying_type<Tgt>::type>(value));
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
} // namespace folly
|