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