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
33namespace folly {
34
35static_assert(IsConvertible<float>::value, "");
36static_assert(IsConvertible<int>::value, "");
37static_assert(IsConvertible<bool>::value, "");
38static_assert(IsConvertible<int>::value, "");
39static_assert(!IsConvertible<std::vector<int>>::value, "");
40
41namespace detail {
42
43struct 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
59struct 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
81struct 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
93struct 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
116FOLLY_STORAGE_CONSTEXPR decltype(cEscapeTable) cEscapeTable =
117 make_array_with<256>(string_table_c_escape_make_item{});
118FOLLY_STORAGE_CONSTEXPR decltype(cUnescapeTable) cUnescapeTable =
119 make_array_with<256>(string_table_c_unescape_make_item{});
120FOLLY_STORAGE_CONSTEXPR decltype(hexTable) hexTable =
121 make_array_with<256>(string_table_hex_make_item{});
122FOLLY_STORAGE_CONSTEXPR decltype(uriEscapeTable) uriEscapeTable =
123 make_array_with<256>(string_table_uri_escape_make_item{});
124
125} // namespace detail
126
127static inline bool is_oddspace(char c) {
128 return c == '\n' || c == '\t' || c == '\r';
129}
130
131StringPiece 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
149StringPiece 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
167namespace {
168
169int 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
181void 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
223std::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
232std::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.
240std::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
249std::string&
250stringVAppendf(std::string* output, const char* format, va_list ap) {
251 stringAppendfImpl(*output, format, ap);
252 return *output;
253}
254
255void 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
264void stringVPrintf(std::string* output, const char* format, va_list ap) {
265 output->clear();
266 stringAppendfImpl(*output, format, ap);
267}
268
269namespace {
270
271struct PrettySuffix {
272 const char* suffix;
273 double val;
274};
275
276const 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
286const 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
298const 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
309const 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
320const 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
331const 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
342const 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
353const 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
364const 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
372const 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
386std::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
416double 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
451double prettyToDouble(folly::StringPiece prettyString, const PrettyType type) {
452 double result = prettyToDouble(&prettyString, type);
453 detail::enforceWhitespace(prettyString);
454 return result;
455}
456
457std::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
463fbstring 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
507namespace {
508
509void 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
567void 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
587void 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
601void 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
648namespace detail {
649
650size_t
651hexDumpLine(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
704std::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