Austin Schuh | 36244a1 | 2019-09-21 17:52:38 -0700 | [diff] [blame] | 1 | // Copyright 2017 The Abseil Authors. |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // https://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | #include "absl/strings/substitute.h" |
| 16 | |
| 17 | #include <algorithm> |
| 18 | |
| 19 | #include "absl/base/internal/raw_logging.h" |
| 20 | #include "absl/strings/ascii.h" |
| 21 | #include "absl/strings/escaping.h" |
| 22 | #include "absl/strings/internal/resize_uninitialized.h" |
| 23 | #include "absl/strings/string_view.h" |
| 24 | |
| 25 | namespace absl { |
Austin Schuh | b4691e9 | 2020-12-31 12:37:18 -0800 | [diff] [blame] | 26 | ABSL_NAMESPACE_BEGIN |
Austin Schuh | 36244a1 | 2019-09-21 17:52:38 -0700 | [diff] [blame] | 27 | namespace substitute_internal { |
| 28 | |
| 29 | void SubstituteAndAppendArray(std::string* output, absl::string_view format, |
| 30 | const absl::string_view* args_array, |
| 31 | size_t num_args) { |
| 32 | // Determine total size needed. |
| 33 | size_t size = 0; |
| 34 | for (size_t i = 0; i < format.size(); i++) { |
| 35 | if (format[i] == '$') { |
| 36 | if (i + 1 >= format.size()) { |
| 37 | #ifndef NDEBUG |
| 38 | ABSL_RAW_LOG(FATAL, |
Austin Schuh | b4691e9 | 2020-12-31 12:37:18 -0800 | [diff] [blame] | 39 | "Invalid absl::Substitute() format string: \"%s\".", |
Austin Schuh | 36244a1 | 2019-09-21 17:52:38 -0700 | [diff] [blame] | 40 | absl::CEscape(format).c_str()); |
| 41 | #endif |
| 42 | return; |
| 43 | } else if (absl::ascii_isdigit(format[i + 1])) { |
| 44 | int index = format[i + 1] - '0'; |
| 45 | if (static_cast<size_t>(index) >= num_args) { |
| 46 | #ifndef NDEBUG |
| 47 | ABSL_RAW_LOG( |
| 48 | FATAL, |
Austin Schuh | b4691e9 | 2020-12-31 12:37:18 -0800 | [diff] [blame] | 49 | "Invalid absl::Substitute() format string: asked for \"$" |
| 50 | "%d\", but only %d args were given. Full format string was: " |
Austin Schuh | 36244a1 | 2019-09-21 17:52:38 -0700 | [diff] [blame] | 51 | "\"%s\".", |
| 52 | index, static_cast<int>(num_args), absl::CEscape(format).c_str()); |
| 53 | #endif |
| 54 | return; |
| 55 | } |
| 56 | size += args_array[index].size(); |
| 57 | ++i; // Skip next char. |
| 58 | } else if (format[i + 1] == '$') { |
| 59 | ++size; |
| 60 | ++i; // Skip next char. |
| 61 | } else { |
| 62 | #ifndef NDEBUG |
| 63 | ABSL_RAW_LOG(FATAL, |
Austin Schuh | b4691e9 | 2020-12-31 12:37:18 -0800 | [diff] [blame] | 64 | "Invalid absl::Substitute() format string: \"%s\".", |
Austin Schuh | 36244a1 | 2019-09-21 17:52:38 -0700 | [diff] [blame] | 65 | absl::CEscape(format).c_str()); |
| 66 | #endif |
| 67 | return; |
| 68 | } |
| 69 | } else { |
| 70 | ++size; |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | if (size == 0) return; |
| 75 | |
Austin Schuh | b4691e9 | 2020-12-31 12:37:18 -0800 | [diff] [blame] | 76 | // Build the string. |
Austin Schuh | 36244a1 | 2019-09-21 17:52:38 -0700 | [diff] [blame] | 77 | size_t original_size = output->size(); |
| 78 | strings_internal::STLStringResizeUninitialized(output, original_size + size); |
| 79 | char* target = &(*output)[original_size]; |
| 80 | for (size_t i = 0; i < format.size(); i++) { |
| 81 | if (format[i] == '$') { |
| 82 | if (absl::ascii_isdigit(format[i + 1])) { |
| 83 | const absl::string_view src = args_array[format[i + 1] - '0']; |
| 84 | target = std::copy(src.begin(), src.end(), target); |
| 85 | ++i; // Skip next char. |
| 86 | } else if (format[i + 1] == '$') { |
| 87 | *target++ = '$'; |
| 88 | ++i; // Skip next char. |
| 89 | } |
| 90 | } else { |
| 91 | *target++ = format[i]; |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | assert(target == output->data() + output->size()); |
| 96 | } |
| 97 | |
Austin Schuh | 36244a1 | 2019-09-21 17:52:38 -0700 | [diff] [blame] | 98 | Arg::Arg(const void* value) { |
| 99 | static_assert(sizeof(scratch_) >= sizeof(value) * 2 + 2, |
| 100 | "fix sizeof(scratch_)"); |
| 101 | if (value == nullptr) { |
| 102 | piece_ = "NULL"; |
| 103 | } else { |
| 104 | char* ptr = scratch_ + sizeof(scratch_); |
| 105 | uintptr_t num = reinterpret_cast<uintptr_t>(value); |
| 106 | do { |
Austin Schuh | b4691e9 | 2020-12-31 12:37:18 -0800 | [diff] [blame] | 107 | *--ptr = absl::numbers_internal::kHexChar[num & 0xf]; |
Austin Schuh | 36244a1 | 2019-09-21 17:52:38 -0700 | [diff] [blame] | 108 | num >>= 4; |
| 109 | } while (num != 0); |
| 110 | *--ptr = 'x'; |
| 111 | *--ptr = '0'; |
| 112 | piece_ = absl::string_view(ptr, scratch_ + sizeof(scratch_) - ptr); |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | // TODO(jorg): Don't duplicate so much code between here and str_cat.cc |
| 117 | Arg::Arg(Hex hex) { |
| 118 | char* const end = &scratch_[numbers_internal::kFastToBufferSize]; |
| 119 | char* writer = end; |
| 120 | uint64_t value = hex.value; |
| 121 | do { |
Austin Schuh | b4691e9 | 2020-12-31 12:37:18 -0800 | [diff] [blame] | 122 | *--writer = absl::numbers_internal::kHexChar[value & 0xF]; |
Austin Schuh | 36244a1 | 2019-09-21 17:52:38 -0700 | [diff] [blame] | 123 | value >>= 4; |
| 124 | } while (value != 0); |
| 125 | |
| 126 | char* beg; |
| 127 | if (end - writer < hex.width) { |
| 128 | beg = end - hex.width; |
| 129 | std::fill_n(beg, writer - beg, hex.fill); |
| 130 | } else { |
| 131 | beg = writer; |
| 132 | } |
| 133 | |
| 134 | piece_ = absl::string_view(beg, end - beg); |
| 135 | } |
| 136 | |
| 137 | // TODO(jorg): Don't duplicate so much code between here and str_cat.cc |
| 138 | Arg::Arg(Dec dec) { |
| 139 | assert(dec.width <= numbers_internal::kFastToBufferSize); |
| 140 | char* const end = &scratch_[numbers_internal::kFastToBufferSize]; |
| 141 | char* const minfill = end - dec.width; |
| 142 | char* writer = end; |
| 143 | uint64_t value = dec.value; |
| 144 | bool neg = dec.neg; |
| 145 | while (value > 9) { |
| 146 | *--writer = '0' + (value % 10); |
| 147 | value /= 10; |
| 148 | } |
| 149 | *--writer = '0' + value; |
| 150 | if (neg) *--writer = '-'; |
| 151 | |
| 152 | ptrdiff_t fillers = writer - minfill; |
| 153 | if (fillers > 0) { |
| 154 | // Tricky: if the fill character is ' ', then it's <fill><+/-><digits> |
| 155 | // But...: if the fill character is '0', then it's <+/-><fill><digits> |
| 156 | bool add_sign_again = false; |
| 157 | if (neg && dec.fill == '0') { // If filling with '0', |
| 158 | ++writer; // ignore the sign we just added |
| 159 | add_sign_again = true; // and re-add the sign later. |
| 160 | } |
| 161 | writer -= fillers; |
| 162 | std::fill_n(writer, fillers, dec.fill); |
| 163 | if (add_sign_again) *--writer = '-'; |
| 164 | } |
| 165 | |
| 166 | piece_ = absl::string_view(writer, end - writer); |
| 167 | } |
| 168 | |
| 169 | } // namespace substitute_internal |
Austin Schuh | b4691e9 | 2020-12-31 12:37:18 -0800 | [diff] [blame] | 170 | ABSL_NAMESPACE_END |
Austin Schuh | 36244a1 | 2019-09-21 17:52:38 -0700 | [diff] [blame] | 171 | } // namespace absl |