1 | /* |
2 | * Copyright 2012-present Facebook, Inc. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #include <folly/String.h> |
18 | |
19 | #include <cctype> |
20 | #include <cerrno> |
21 | #include <cstdarg> |
22 | #include <cstring> |
23 | #include <iterator> |
24 | #include <sstream> |
25 | #include <stdexcept> |
26 | |
27 | #include <glog/logging.h> |
28 | |
29 | #include <folly/Portability.h> |
30 | #include <folly/ScopeGuard.h> |
31 | #include <folly/container/Array.h> |
32 | |
33 | namespace folly { |
34 | |
35 | static_assert(IsConvertible<float>::value, "" ); |
36 | static_assert(IsConvertible<int>::value, "" ); |
37 | static_assert(IsConvertible<bool>::value, "" ); |
38 | static_assert(IsConvertible<int>::value, "" ); |
39 | static_assert(!IsConvertible<std::vector<int>>::value, "" ); |
40 | |
41 | namespace detail { |
42 | |
43 | struct string_table_c_escape_make_item { |
44 | constexpr char operator()(std::size_t index) const { |
45 | // clang-format off |
46 | return |
47 | index == '"' ? '"' : |
48 | index == '\\' ? '\\' : |
49 | index == '?' ? '?' : |
50 | index == '\n' ? 'n' : |
51 | index == '\r' ? 'r' : |
52 | index == '\t' ? 't' : |
53 | index < 32 || index > 126 ? 'O' : // octal |
54 | 'P'; // printable |
55 | // clang-format on |
56 | } |
57 | }; |
58 | |
59 | struct string_table_c_unescape_make_item { |
60 | constexpr char operator()(std::size_t index) const { |
61 | // clang-format off |
62 | return |
63 | index == '\'' ? '\'' : |
64 | index == '?' ? '?' : |
65 | index == '\\' ? '\\' : |
66 | index == '"' ? '"' : |
67 | index == 'a' ? '\a' : |
68 | index == 'b' ? '\b' : |
69 | index == 'f' ? '\f' : |
70 | index == 'n' ? '\n' : |
71 | index == 'r' ? '\r' : |
72 | index == 't' ? '\t' : |
73 | index == 'v' ? '\v' : |
74 | index >= '0' && index <= '7' ? 'O' : // octal |
75 | index == 'x' ? 'X' : // hex |
76 | 'I'; // invalid |
77 | // clang-format on |
78 | } |
79 | }; |
80 | |
81 | struct string_table_hex_make_item { |
82 | constexpr unsigned char operator()(std::size_t index) const { |
83 | // clang-format off |
84 | return |
85 | index >= '0' && index <= '9' ? index - '0' : |
86 | index >= 'a' && index <= 'f' ? index - 'a' + 10 : |
87 | index >= 'A' && index <= 'F' ? index - 'A' + 10 : |
88 | 16; |
89 | // clang-format on |
90 | } |
91 | }; |
92 | |
93 | struct string_table_uri_escape_make_item { |
94 | // 0 = passthrough |
95 | // 1 = unused |
96 | // 2 = safe in path (/) |
97 | // 3 = space (replace with '+' in query) |
98 | // 4 = always percent-encode |
99 | constexpr unsigned char operator()(std::size_t index) const { |
100 | // clang-format off |
101 | return |
102 | index >= '0' && index <= '9' ? 0 : |
103 | index >= 'A' && index <= 'Z' ? 0 : |
104 | index >= 'a' && index <= 'z' ? 0 : |
105 | index == '-' ? 0 : |
106 | index == '_' ? 0 : |
107 | index == '.' ? 0 : |
108 | index == '~' ? 0 : |
109 | index == '/' ? 2 : |
110 | index == ' ' ? 3 : |
111 | 4; |
112 | // clang-format on |
113 | } |
114 | }; |
115 | |
116 | FOLLY_STORAGE_CONSTEXPR decltype(cEscapeTable) cEscapeTable = |
117 | make_array_with<256>(string_table_c_escape_make_item{}); |
118 | FOLLY_STORAGE_CONSTEXPR decltype(cUnescapeTable) cUnescapeTable = |
119 | make_array_with<256>(string_table_c_unescape_make_item{}); |
120 | FOLLY_STORAGE_CONSTEXPR decltype(hexTable) hexTable = |
121 | make_array_with<256>(string_table_hex_make_item{}); |
122 | FOLLY_STORAGE_CONSTEXPR decltype(uriEscapeTable) uriEscapeTable = |
123 | make_array_with<256>(string_table_uri_escape_make_item{}); |
124 | |
125 | } // namespace detail |
126 | |
127 | static inline bool is_oddspace(char c) { |
128 | return c == '\n' || c == '\t' || c == '\r'; |
129 | } |
130 | |
131 | StringPiece ltrimWhitespace(StringPiece sp) { |
132 | // Spaces other than ' ' characters are less common but should be |
133 | // checked. This configuration where we loop on the ' ' |
134 | // separately from oddspaces was empirically fastest. |
135 | |
136 | while (true) { |
137 | while (!sp.empty() && sp.front() == ' ') { |
138 | sp.pop_front(); |
139 | } |
140 | if (!sp.empty() && is_oddspace(sp.front())) { |
141 | sp.pop_front(); |
142 | continue; |
143 | } |
144 | |
145 | return sp; |
146 | } |
147 | } |
148 | |
149 | StringPiece rtrimWhitespace(StringPiece sp) { |
150 | // Spaces other than ' ' characters are less common but should be |
151 | // checked. This configuration where we loop on the ' ' |
152 | // separately from oddspaces was empirically fastest. |
153 | |
154 | while (true) { |
155 | while (!sp.empty() && sp.back() == ' ') { |
156 | sp.pop_back(); |
157 | } |
158 | if (!sp.empty() && is_oddspace(sp.back())) { |
159 | sp.pop_back(); |
160 | continue; |
161 | } |
162 | |
163 | return sp; |
164 | } |
165 | } |
166 | |
167 | namespace { |
168 | |
169 | int stringAppendfImplHelper( |
170 | char* buf, |
171 | size_t bufsize, |
172 | const char* format, |
173 | va_list args) { |
174 | va_list args_copy; |
175 | va_copy(args_copy, args); |
176 | int bytes_used = vsnprintf(buf, bufsize, format, args_copy); |
177 | va_end(args_copy); |
178 | return bytes_used; |
179 | } |
180 | |
181 | void stringAppendfImpl(std::string& output, const char* format, va_list args) { |
182 | // Very simple; first, try to avoid an allocation by using an inline |
183 | // buffer. If that fails to hold the output string, allocate one on |
184 | // the heap, use it instead. |
185 | // |
186 | // It is hard to guess the proper size of this buffer; some |
187 | // heuristics could be based on the number of format characters, or |
188 | // static analysis of a codebase. Or, we can just pick a number |
189 | // that seems big enough for simple cases (say, one line of text on |
190 | // a terminal) without being large enough to be concerning as a |
191 | // stack variable. |
192 | std::array<char, 128> inline_buffer; |
193 | |
194 | int bytes_used = stringAppendfImplHelper( |
195 | inline_buffer.data(), inline_buffer.size(), format, args); |
196 | if (bytes_used < 0) { |
197 | throw std::runtime_error(to<std::string>( |
198 | "Invalid format string; snprintf returned negative " |
199 | "with format string: " , |
200 | format)); |
201 | } |
202 | |
203 | if (static_cast<size_t>(bytes_used) < inline_buffer.size()) { |
204 | output.append(inline_buffer.data(), size_t(bytes_used)); |
205 | return; |
206 | } |
207 | |
208 | // Couldn't fit. Heap allocate a buffer, oh well. |
209 | std::unique_ptr<char[]> heap_buffer(new char[size_t(bytes_used + 1)]); |
210 | int final_bytes_used = stringAppendfImplHelper( |
211 | heap_buffer.get(), size_t(bytes_used + 1), format, args); |
212 | // The second call can take fewer bytes if, for example, we were printing a |
213 | // string buffer with null-terminating char using a width specifier - |
214 | // vsnprintf("%.*s", buf.size(), buf) |
215 | CHECK(bytes_used >= final_bytes_used); |
216 | |
217 | // We don't keep the trailing '\0' in our output string |
218 | output.append(heap_buffer.get(), size_t(final_bytes_used)); |
219 | } |
220 | |
221 | } // namespace |
222 | |
223 | std::string stringPrintf(const char* format, ...) { |
224 | va_list ap; |
225 | va_start(ap, format); |
226 | SCOPE_EXIT { |
227 | va_end(ap); |
228 | }; |
229 | return stringVPrintf(format, ap); |
230 | } |
231 | |
232 | std::string stringVPrintf(const char* format, va_list ap) { |
233 | std::string ret; |
234 | stringAppendfImpl(ret, format, ap); |
235 | return ret; |
236 | } |
237 | |
238 | // Basic declarations; allow for parameters of strings and string |
239 | // pieces to be specified. |
240 | std::string& stringAppendf(std::string* output, const char* format, ...) { |
241 | va_list ap; |
242 | va_start(ap, format); |
243 | SCOPE_EXIT { |
244 | va_end(ap); |
245 | }; |
246 | return stringVAppendf(output, format, ap); |
247 | } |
248 | |
249 | std::string& |
250 | stringVAppendf(std::string* output, const char* format, va_list ap) { |
251 | stringAppendfImpl(*output, format, ap); |
252 | return *output; |
253 | } |
254 | |
255 | void stringPrintf(std::string* output, const char* format, ...) { |
256 | va_list ap; |
257 | va_start(ap, format); |
258 | SCOPE_EXIT { |
259 | va_end(ap); |
260 | }; |
261 | return stringVPrintf(output, format, ap); |
262 | } |
263 | |
264 | void stringVPrintf(std::string* output, const char* format, va_list ap) { |
265 | output->clear(); |
266 | stringAppendfImpl(*output, format, ap); |
267 | } |
268 | |
269 | namespace { |
270 | |
271 | struct PrettySuffix { |
272 | const char* suffix; |
273 | double val; |
274 | }; |
275 | |
276 | const PrettySuffix kPrettyTimeSuffixes[] = { |
277 | {"s " , 1e0L}, |
278 | {"ms" , 1e-3L}, |
279 | {"us" , 1e-6L}, |
280 | {"ns" , 1e-9L}, |
281 | {"ps" , 1e-12L}, |
282 | {"s " , 0}, |
283 | {nullptr, 0}, |
284 | }; |
285 | |
286 | const PrettySuffix kPrettyTimeHmsSuffixes[] = { |
287 | {"h " , 60L * 60L}, |
288 | {"m " , 60L}, |
289 | {"s " , 1e0L}, |
290 | {"ms" , 1e-3L}, |
291 | {"us" , 1e-6L}, |
292 | {"ns" , 1e-9L}, |
293 | {"ps" , 1e-12L}, |
294 | {"s " , 0}, |
295 | {nullptr, 0}, |
296 | }; |
297 | |
298 | const PrettySuffix kPrettyBytesMetricSuffixes[] = { |
299 | {"EB" , 1e18L}, |
300 | {"PB" , 1e15L}, |
301 | {"TB" , 1e12L}, |
302 | {"GB" , 1e9L}, |
303 | {"MB" , 1e6L}, |
304 | {"kB" , 1e3L}, |
305 | {"B " , 0L}, |
306 | {nullptr, 0}, |
307 | }; |
308 | |
309 | const PrettySuffix kPrettyBytesBinarySuffixes[] = { |
310 | {"EB" , int64_t(1) << 60}, |
311 | {"PB" , int64_t(1) << 50}, |
312 | {"TB" , int64_t(1) << 40}, |
313 | {"GB" , int64_t(1) << 30}, |
314 | {"MB" , int64_t(1) << 20}, |
315 | {"kB" , int64_t(1) << 10}, |
316 | {"B " , 0L}, |
317 | {nullptr, 0}, |
318 | }; |
319 | |
320 | const PrettySuffix kPrettyBytesBinaryIECSuffixes[] = { |
321 | {"EiB" , int64_t(1) << 60}, |
322 | {"PiB" , int64_t(1) << 50}, |
323 | {"TiB" , int64_t(1) << 40}, |
324 | {"GiB" , int64_t(1) << 30}, |
325 | {"MiB" , int64_t(1) << 20}, |
326 | {"KiB" , int64_t(1) << 10}, |
327 | {"B " , 0L}, |
328 | {nullptr, 0}, |
329 | }; |
330 | |
331 | const PrettySuffix kPrettyUnitsMetricSuffixes[] = { |
332 | {"qntl" , 1e18L}, |
333 | {"qdrl" , 1e15L}, |
334 | {"tril" , 1e12L}, |
335 | {"bil" , 1e9L}, |
336 | {"M" , 1e6L}, |
337 | {"k" , 1e3L}, |
338 | {" " , 0}, |
339 | {nullptr, 0}, |
340 | }; |
341 | |
342 | const PrettySuffix kPrettyUnitsBinarySuffixes[] = { |
343 | {"E" , int64_t(1) << 60}, |
344 | {"P" , int64_t(1) << 50}, |
345 | {"T" , int64_t(1) << 40}, |
346 | {"G" , int64_t(1) << 30}, |
347 | {"M" , int64_t(1) << 20}, |
348 | {"k" , int64_t(1) << 10}, |
349 | {" " , 0}, |
350 | {nullptr, 0}, |
351 | }; |
352 | |
353 | const PrettySuffix kPrettyUnitsBinaryIECSuffixes[] = { |
354 | {"Ei" , int64_t(1) << 60}, |
355 | {"Pi" , int64_t(1) << 50}, |
356 | {"Ti" , int64_t(1) << 40}, |
357 | {"Gi" , int64_t(1) << 30}, |
358 | {"Mi" , int64_t(1) << 20}, |
359 | {"Ki" , int64_t(1) << 10}, |
360 | {" " , 0}, |
361 | {nullptr, 0}, |
362 | }; |
363 | |
364 | const PrettySuffix kPrettySISuffixes[] = { |
365 | {"Y" , 1e24L}, {"Z" , 1e21L}, {"E" , 1e18L}, {"P" , 1e15L}, {"T" , 1e12L}, |
366 | {"G" , 1e9L}, {"M" , 1e6L}, {"k" , 1e3L}, {"h" , 1e2L}, {"da" , 1e1L}, |
367 | {"d" , 1e-1L}, {"c" , 1e-2L}, {"m" , 1e-3L}, {"u" , 1e-6L}, {"n" , 1e-9L}, |
368 | {"p" , 1e-12L}, {"f" , 1e-15L}, {"a" , 1e-18L}, {"z" , 1e-21L}, {"y" , 1e-24L}, |
369 | {" " , 0}, {nullptr, 0}, |
370 | }; |
371 | |
372 | const PrettySuffix* const kPrettySuffixes[PRETTY_NUM_TYPES] = { |
373 | kPrettyTimeSuffixes, |
374 | kPrettyTimeHmsSuffixes, |
375 | kPrettyBytesMetricSuffixes, |
376 | kPrettyBytesBinarySuffixes, |
377 | kPrettyBytesBinaryIECSuffixes, |
378 | kPrettyUnitsMetricSuffixes, |
379 | kPrettyUnitsBinarySuffixes, |
380 | kPrettyUnitsBinaryIECSuffixes, |
381 | kPrettySISuffixes, |
382 | }; |
383 | |
384 | } // namespace |
385 | |
386 | std::string prettyPrint(double val, PrettyType type, bool addSpace) { |
387 | char buf[100]; |
388 | |
389 | // pick the suffixes to use |
390 | assert(type >= 0); |
391 | assert(type < PRETTY_NUM_TYPES); |
392 | const PrettySuffix* suffixes = kPrettySuffixes[type]; |
393 | |
394 | // find the first suffix we're bigger than -- then use it |
395 | double abs_val = fabs(val); |
396 | for (int i = 0; suffixes[i].suffix; ++i) { |
397 | if (abs_val >= suffixes[i].val) { |
398 | snprintf( |
399 | buf, |
400 | sizeof buf, |
401 | "%.4g%s%s" , |
402 | (suffixes[i].val ? (val / suffixes[i].val) : val), |
403 | (addSpace ? " " : "" ), |
404 | suffixes[i].suffix); |
405 | return std::string(buf); |
406 | } |
407 | } |
408 | |
409 | // no suffix, we've got a tiny value -- just print it in sci-notation |
410 | snprintf(buf, sizeof buf, "%.4g" , val); |
411 | return std::string(buf); |
412 | } |
413 | |
414 | // TODO: |
415 | // 1) Benchmark & optimize |
416 | double prettyToDouble( |
417 | folly::StringPiece* const prettyString, |
418 | const PrettyType type) { |
419 | double value = folly::to<double>(prettyString); |
420 | while (prettyString->size() > 0 && std::isspace(prettyString->front())) { |
421 | prettyString->advance(1); // Skipping spaces between number and suffix |
422 | } |
423 | const PrettySuffix* suffixes = kPrettySuffixes[type]; |
424 | int longestPrefixLen = -1; |
425 | int bestPrefixId = -1; |
426 | for (int j = 0; suffixes[j].suffix; ++j) { |
427 | if (suffixes[j].suffix[0] == ' ') { // Checking for " " -> number rule. |
428 | if (longestPrefixLen == -1) { |
429 | longestPrefixLen = 0; // No characters to skip |
430 | bestPrefixId = j; |
431 | } |
432 | } else if (prettyString->startsWith(suffixes[j].suffix)) { |
433 | int suffixLen = int(strlen(suffixes[j].suffix)); |
434 | // We are looking for a longest suffix matching prefix of the string |
435 | // after numeric value. We need this in case suffixes have common prefix. |
436 | if (suffixLen > longestPrefixLen) { |
437 | longestPrefixLen = suffixLen; |
438 | bestPrefixId = j; |
439 | } |
440 | } |
441 | } |
442 | if (bestPrefixId == -1) { // No valid suffix rule found |
443 | throw std::invalid_argument(folly::to<std::string>( |
444 | "Unable to parse suffix \"" , *prettyString, "\"" )); |
445 | } |
446 | prettyString->advance(size_t(longestPrefixLen)); |
447 | return suffixes[bestPrefixId].val ? value * suffixes[bestPrefixId].val |
448 | : value; |
449 | } |
450 | |
451 | double prettyToDouble(folly::StringPiece prettyString, const PrettyType type) { |
452 | double result = prettyToDouble(&prettyString, type); |
453 | detail::enforceWhitespace(prettyString); |
454 | return result; |
455 | } |
456 | |
457 | std::string hexDump(const void* ptr, size_t size) { |
458 | std::ostringstream os; |
459 | hexDump(ptr, size, std::ostream_iterator<StringPiece>(os, "\n" )); |
460 | return os.str(); |
461 | } |
462 | |
463 | fbstring errnoStr(int err) { |
464 | int savedErrno = errno; |
465 | |
466 | // Ensure that we reset errno upon exit. |
467 | auto guard(makeGuard([&] { errno = savedErrno; })); |
468 | |
469 | char buf[1024]; |
470 | buf[0] = '\0'; |
471 | |
472 | fbstring result; |
473 | |
474 | // https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/strerror_r.3.html |
475 | // http://www.kernel.org/doc/man-pages/online/pages/man3/strerror.3.html |
476 | #if defined(_WIN32) && (defined(__MINGW32__) || defined(_MSC_VER)) |
477 | // mingw64 has no strerror_r, but Windows has strerror_s, which C11 added |
478 | // as well. So maybe we should use this across all platforms (together |
479 | // with strerrorlen_s). Note strerror_r and _s have swapped args. |
480 | int r = strerror_s(buf, sizeof(buf), err); |
481 | if (r != 0) { |
482 | result = to<fbstring>( |
483 | "Unknown error " , err, " (strerror_r failed with error " , errno, ")" ); |
484 | } else { |
485 | result.assign(buf); |
486 | } |
487 | #elif FOLLY_HAVE_XSI_STRERROR_R || defined(__APPLE__) |
488 | |
489 | // Using XSI-compatible strerror_r |
490 | int r = strerror_r(err, buf, sizeof(buf)); |
491 | |
492 | // OSX/FreeBSD use EINVAL and Linux uses -1 so just check for non-zero |
493 | if (r != 0) { |
494 | result = to<fbstring>( |
495 | "Unknown error " , err, " (strerror_r failed with error " , errno, ")" ); |
496 | } else { |
497 | result.assign(buf); |
498 | } |
499 | #else |
500 | // Using GNU strerror_r |
501 | result.assign(strerror_r(err, buf, sizeof(buf))); |
502 | #endif |
503 | |
504 | return result; |
505 | } |
506 | |
507 | namespace { |
508 | |
509 | void toLowerAscii8(char& c) { |
510 | // Branchless tolower, based on the input-rotating trick described |
511 | // at http://www.azillionmonkeys.com/qed/asmexample.html |
512 | // |
513 | // This algorithm depends on an observation: each uppercase |
514 | // ASCII character can be converted to its lowercase equivalent |
515 | // by adding 0x20. |
516 | |
517 | // Step 1: Clear the high order bit. We'll deal with it in Step 5. |
518 | uint8_t rotated = uint8_t(c & 0x7f); |
519 | // Currently, the value of rotated, as a function of the original c is: |
520 | // below 'A': 0- 64 |
521 | // 'A'-'Z': 65- 90 |
522 | // above 'Z': 91-127 |
523 | |
524 | // Step 2: Add 0x25 (37) |
525 | rotated += 0x25; |
526 | // Now the value of rotated, as a function of the original c is: |
527 | // below 'A': 37-101 |
528 | // 'A'-'Z': 102-127 |
529 | // above 'Z': 128-164 |
530 | |
531 | // Step 3: clear the high order bit |
532 | rotated &= 0x7f; |
533 | // below 'A': 37-101 |
534 | // 'A'-'Z': 102-127 |
535 | // above 'Z': 0- 36 |
536 | |
537 | // Step 4: Add 0x1a (26) |
538 | rotated += 0x1a; |
539 | // below 'A': 63-127 |
540 | // 'A'-'Z': 128-153 |
541 | // above 'Z': 25- 62 |
542 | |
543 | // At this point, note that only the uppercase letters have been |
544 | // transformed into values with the high order bit set (128 and above). |
545 | |
546 | // Step 5: Shift the high order bit 2 spaces to the right: the spot |
547 | // where the only 1 bit in 0x20 is. But first, how we ignored the |
548 | // high order bit of the original c in step 1? If that bit was set, |
549 | // we may have just gotten a false match on a value in the range |
550 | // 128+'A' to 128+'Z'. To correct this, need to clear the high order |
551 | // bit of rotated if the high order bit of c is set. Since we don't |
552 | // care about the other bits in rotated, the easiest thing to do |
553 | // is invert all the bits in c and bitwise-and them with rotated. |
554 | rotated &= ~c; |
555 | rotated >>= 2; |
556 | |
557 | // Step 6: Apply a mask to clear everything except the 0x20 bit |
558 | // in rotated. |
559 | rotated &= 0x20; |
560 | |
561 | // At this point, rotated is 0x20 if c is 'A'-'Z' and 0x00 otherwise |
562 | |
563 | // Step 7: Add rotated to c |
564 | c += char(rotated); |
565 | } |
566 | |
567 | void toLowerAscii32(uint32_t& c) { |
568 | // Besides being branchless, the algorithm in toLowerAscii8() has another |
569 | // interesting property: None of the addition operations will cause |
570 | // an overflow in the 8-bit value. So we can pack four 8-bit values |
571 | // into a uint32_t and run each operation on all four values in parallel |
572 | // without having to use any CPU-specific SIMD instructions. |
573 | uint32_t rotated = c & uint32_t(0x7f7f7f7fL); |
574 | rotated += uint32_t(0x25252525L); |
575 | rotated &= uint32_t(0x7f7f7f7fL); |
576 | rotated += uint32_t(0x1a1a1a1aL); |
577 | |
578 | // Step 5 involves a shift, so some bits will spill over from each |
579 | // 8-bit value into the next. But that's okay, because they're bits |
580 | // that will be cleared by the mask in step 6 anyway. |
581 | rotated &= ~c; |
582 | rotated >>= 2; |
583 | rotated &= uint32_t(0x20202020L); |
584 | c += rotated; |
585 | } |
586 | |
587 | void toLowerAscii64(uint64_t& c) { |
588 | // 64-bit version of toLower32 |
589 | uint64_t rotated = c & uint64_t(0x7f7f7f7f7f7f7f7fL); |
590 | rotated += uint64_t(0x2525252525252525L); |
591 | rotated &= uint64_t(0x7f7f7f7f7f7f7f7fL); |
592 | rotated += uint64_t(0x1a1a1a1a1a1a1a1aL); |
593 | rotated &= ~c; |
594 | rotated >>= 2; |
595 | rotated &= uint64_t(0x2020202020202020L); |
596 | c += rotated; |
597 | } |
598 | |
599 | } // namespace |
600 | |
601 | void toLowerAscii(char* str, size_t length) { |
602 | static const size_t kAlignMask64 = 7; |
603 | static const size_t kAlignMask32 = 3; |
604 | |
605 | // Convert a character at a time until we reach an address that |
606 | // is at least 32-bit aligned |
607 | size_t n = (size_t)str; |
608 | n &= kAlignMask32; |
609 | n = std::min(n, length); |
610 | size_t offset = 0; |
611 | if (n != 0) { |
612 | n = std::min(4 - n, length); |
613 | do { |
614 | toLowerAscii8(str[offset]); |
615 | offset++; |
616 | } while (offset < n); |
617 | } |
618 | |
619 | n = (size_t)(str + offset); |
620 | n &= kAlignMask64; |
621 | if ((n != 0) && (offset + 4 <= length)) { |
622 | // The next address is 32-bit aligned but not 64-bit aligned. |
623 | // Convert the next 4 bytes in order to get to the 64-bit aligned |
624 | // part of the input. |
625 | toLowerAscii32(*(uint32_t*)(str + offset)); |
626 | offset += 4; |
627 | } |
628 | |
629 | // Convert 8 characters at a time |
630 | while (offset + 8 <= length) { |
631 | toLowerAscii64(*(uint64_t*)(str + offset)); |
632 | offset += 8; |
633 | } |
634 | |
635 | // Convert 4 characters at a time |
636 | while (offset + 4 <= length) { |
637 | toLowerAscii32(*(uint32_t*)(str + offset)); |
638 | offset += 4; |
639 | } |
640 | |
641 | // Convert any characters remaining after the last 4-byte aligned group |
642 | while (offset < length) { |
643 | toLowerAscii8(str[offset]); |
644 | offset++; |
645 | } |
646 | } |
647 | |
648 | namespace detail { |
649 | |
650 | size_t |
651 | hexDumpLine(const void* ptr, size_t offset, size_t size, std::string& line) { |
652 | static char hexValues[] = "0123456789abcdef" ; |
653 | // Line layout: |
654 | // 8: address |
655 | // 1: space |
656 | // (1+2)*16: hex bytes, each preceded by a space |
657 | // 1: space separating the two halves |
658 | // 3: " |" |
659 | // 16: characters |
660 | // 1: "|" |
661 | // Total: 78 |
662 | line.clear(); |
663 | line.reserve(78); |
664 | const uint8_t* p = reinterpret_cast<const uint8_t*>(ptr) + offset; |
665 | size_t n = std::min(size - offset, size_t(16)); |
666 | line.push_back(hexValues[(offset >> 28) & 0xf]); |
667 | line.push_back(hexValues[(offset >> 24) & 0xf]); |
668 | line.push_back(hexValues[(offset >> 20) & 0xf]); |
669 | line.push_back(hexValues[(offset >> 16) & 0xf]); |
670 | line.push_back(hexValues[(offset >> 12) & 0xf]); |
671 | line.push_back(hexValues[(offset >> 8) & 0xf]); |
672 | line.push_back(hexValues[(offset >> 4) & 0xf]); |
673 | line.push_back(hexValues[offset & 0xf]); |
674 | line.push_back(' '); |
675 | |
676 | for (size_t i = 0; i < n; i++) { |
677 | if (i == 8) { |
678 | line.push_back(' '); |
679 | } |
680 | |
681 | line.push_back(' '); |
682 | line.push_back(hexValues[(p[i] >> 4) & 0xf]); |
683 | line.push_back(hexValues[p[i] & 0xf]); |
684 | } |
685 | |
686 | // 3 spaces for each byte we're not printing, one separating the halves |
687 | // if necessary |
688 | line.append(3 * (16 - n) + (n <= 8), ' '); |
689 | line.append(" |" ); |
690 | |
691 | for (size_t i = 0; i < n; i++) { |
692 | char c = (p[i] >= 32 && p[i] <= 126 ? static_cast<char>(p[i]) : '.'); |
693 | line.push_back(c); |
694 | } |
695 | line.append(16 - n, ' '); |
696 | line.push_back('|'); |
697 | DCHECK_EQ(line.size(), 78u); |
698 | |
699 | return n; |
700 | } |
701 | |
702 | } // namespace detail |
703 | |
704 | std::string stripLeftMargin(std::string s) { |
705 | std::vector<StringPiece> pieces; |
706 | split("\n" , s, pieces); |
707 | auto piecer = range(pieces); |
708 | |
709 | auto piece = (piecer.end() - 1); |
710 | auto needle = std::find_if(piece->begin(), piece->end(), [](char c) { |
711 | return c != ' ' && c != '\t'; |
712 | }); |
713 | if (needle == piece->end()) { |
714 | (piecer.end() - 1)->clear(); |
715 | } |
716 | piece = piecer.begin(); |
717 | needle = std::find_if(piece->begin(), piece->end(), [](char c) { |
718 | return c != ' ' && c != '\t'; |
719 | }); |
720 | if (needle == piece->end()) { |
721 | piecer.erase(piecer.begin(), piecer.begin() + 1); |
722 | } |
723 | |
724 | const auto sentinel = std::numeric_limits<size_t>::max(); |
725 | auto indent = sentinel; |
726 | size_t max_length = 0; |
727 | for (piece = piecer.begin(); piece != piecer.end(); piece++) { |
728 | needle = std::find_if(piece->begin(), piece->end(), [](char c) { |
729 | return c != ' ' && c != '\t'; |
730 | }); |
731 | if (needle != piece->end()) { |
732 | indent = std::min<size_t>(indent, size_t(needle - piece->begin())); |
733 | } else { |
734 | max_length = std::max<size_t>(piece->size(), max_length); |
735 | } |
736 | } |
737 | indent = indent == sentinel ? max_length : indent; |
738 | for (piece = piecer.begin(); piece != piecer.end(); piece++) { |
739 | if (piece->size() < indent) { |
740 | piece->clear(); |
741 | } else { |
742 | piece->erase(piece->begin(), piece->begin() + indent); |
743 | } |
744 | } |
745 | return join("\n" , piecer); |
746 | } |
747 | |
748 | } // namespace folly |
749 | |
750 | #ifdef FOLLY_DEFINED_DMGL |
751 | #undef FOLLY_DEFINED_DMGL |
752 | #undef DMGL_NO_OPTS |
753 | #undef DMGL_PARAMS |
754 | #undef DMGL_ANSI |
755 | #undef DMGL_JAVA |
756 | #undef DMGL_VERBOSE |
757 | #undef DMGL_TYPES |
758 | #undef DMGL_RET_POSTFIX |
759 | #endif |
760 | |