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 { |
26 | namespace substitute_internal { |
27 | |
28 | void SubstituteAndAppendArray(std::string* output, absl::string_view format, |
29 | const absl::string_view* args_array, |
30 | size_t num_args) { |
31 | // Determine total size needed. |
32 | size_t size = 0; |
33 | for (size_t i = 0; i < format.size(); i++) { |
34 | if (format[i] == '$') { |
35 | if (i + 1 >= format.size()) { |
36 | #ifndef NDEBUG |
37 | ABSL_RAW_LOG(FATAL, |
38 | "Invalid strings::Substitute() format std::string: \"%s\"." , |
39 | absl::CEscape(format).c_str()); |
40 | #endif |
41 | return; |
42 | } else if (absl::ascii_isdigit(format[i + 1])) { |
43 | int index = format[i + 1] - '0'; |
44 | if (static_cast<size_t>(index) >= num_args) { |
45 | #ifndef NDEBUG |
46 | ABSL_RAW_LOG( |
47 | FATAL, |
48 | "Invalid strings::Substitute() format std::string: asked for \"$" |
49 | "%d\", but only %d args were given. Full format std::string was: " |
50 | "\"%s\"." , |
51 | index, static_cast<int>(num_args), absl::CEscape(format).c_str()); |
52 | #endif |
53 | return; |
54 | } |
55 | size += args_array[index].size(); |
56 | ++i; // Skip next char. |
57 | } else if (format[i + 1] == '$') { |
58 | ++size; |
59 | ++i; // Skip next char. |
60 | } else { |
61 | #ifndef NDEBUG |
62 | ABSL_RAW_LOG(FATAL, |
63 | "Invalid strings::Substitute() format std::string: \"%s\"." , |
64 | absl::CEscape(format).c_str()); |
65 | #endif |
66 | return; |
67 | } |
68 | } else { |
69 | ++size; |
70 | } |
71 | } |
72 | |
73 | if (size == 0) return; |
74 | |
75 | // Build the std::string. |
76 | size_t original_size = output->size(); |
77 | strings_internal::STLStringResizeUninitialized(output, original_size + size); |
78 | char* target = &(*output)[original_size]; |
79 | for (size_t i = 0; i < format.size(); i++) { |
80 | if (format[i] == '$') { |
81 | if (absl::ascii_isdigit(format[i + 1])) { |
82 | const absl::string_view src = args_array[format[i + 1] - '0']; |
83 | target = std::copy(src.begin(), src.end(), target); |
84 | ++i; // Skip next char. |
85 | } else if (format[i + 1] == '$') { |
86 | *target++ = '$'; |
87 | ++i; // Skip next char. |
88 | } |
89 | } else { |
90 | *target++ = format[i]; |
91 | } |
92 | } |
93 | |
94 | assert(target == output->data() + output->size()); |
95 | } |
96 | |
97 | static const char kHexDigits[] = "0123456789abcdef" ; |
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 { |
107 | *--ptr = kHexDigits[num & 0xf]; |
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 { |
122 | *--writer = kHexDigits[value & 0xF]; |
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 |
170 | } // namespace absl |
171 | |