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 ABSL_NAMESPACE_BEGIN
27 namespace substitute_internal {
28
SubstituteAndAppendArray(std::string * output,absl::string_view format,const absl::string_view * args_array,size_t num_args)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,
39 "Invalid absl::Substitute() format string: \"%s\".",
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,
49 "Invalid absl::Substitute() format string: asked for \"$"
50 "%d\", but only %d args were given. Full format string was: "
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,
64 "Invalid absl::Substitute() format string: \"%s\".",
65 absl::CEscape(format).c_str());
66 #endif
67 return;
68 }
69 } else {
70 ++size;
71 }
72 }
73
74 if (size == 0) return;
75
76 // Build the string.
77 size_t original_size = output->size();
78 strings_internal::STLStringResizeUninitializedAmortized(output,
79 original_size + size);
80 char* target = &(*output)[original_size];
81 for (size_t i = 0; i < format.size(); i++) {
82 if (format[i] == '$') {
83 if (absl::ascii_isdigit(format[i + 1])) {
84 const absl::string_view src = args_array[format[i + 1] - '0'];
85 target = std::copy(src.begin(), src.end(), target);
86 ++i; // Skip next char.
87 } else if (format[i + 1] == '$') {
88 *target++ = '$';
89 ++i; // Skip next char.
90 }
91 } else {
92 *target++ = format[i];
93 }
94 }
95
96 assert(target == output->data() + output->size());
97 }
98
Arg(const void * value)99 Arg::Arg(const void* value) {
100 static_assert(sizeof(scratch_) >= sizeof(value) * 2 + 2,
101 "fix sizeof(scratch_)");
102 if (value == nullptr) {
103 piece_ = "NULL";
104 } else {
105 char* ptr = scratch_ + sizeof(scratch_);
106 uintptr_t num = reinterpret_cast<uintptr_t>(value);
107 do {
108 *--ptr = absl::numbers_internal::kHexChar[num & 0xf];
109 num >>= 4;
110 } while (num != 0);
111 *--ptr = 'x';
112 *--ptr = '0';
113 piece_ = absl::string_view(ptr, scratch_ + sizeof(scratch_) - ptr);
114 }
115 }
116
117 // TODO(jorg): Don't duplicate so much code between here and str_cat.cc
Arg(Hex hex)118 Arg::Arg(Hex hex) {
119 char* const end = &scratch_[numbers_internal::kFastToBufferSize];
120 char* writer = end;
121 uint64_t value = hex.value;
122 do {
123 *--writer = absl::numbers_internal::kHexChar[value & 0xF];
124 value >>= 4;
125 } while (value != 0);
126
127 char* beg;
128 if (end - writer < hex.width) {
129 beg = end - hex.width;
130 std::fill_n(beg, writer - beg, hex.fill);
131 } else {
132 beg = writer;
133 }
134
135 piece_ = absl::string_view(beg, end - beg);
136 }
137
138 // TODO(jorg): Don't duplicate so much code between here and str_cat.cc
Arg(Dec dec)139 Arg::Arg(Dec dec) {
140 assert(dec.width <= numbers_internal::kFastToBufferSize);
141 char* const end = &scratch_[numbers_internal::kFastToBufferSize];
142 char* const minfill = end - dec.width;
143 char* writer = end;
144 uint64_t value = dec.value;
145 bool neg = dec.neg;
146 while (value > 9) {
147 *--writer = '0' + (value % 10);
148 value /= 10;
149 }
150 *--writer = '0' + value;
151 if (neg) *--writer = '-';
152
153 ptrdiff_t fillers = writer - minfill;
154 if (fillers > 0) {
155 // Tricky: if the fill character is ' ', then it's <fill><+/-><digits>
156 // But...: if the fill character is '0', then it's <+/-><fill><digits>
157 bool add_sign_again = false;
158 if (neg && dec.fill == '0') { // If filling with '0',
159 ++writer; // ignore the sign we just added
160 add_sign_again = true; // and re-add the sign later.
161 }
162 writer -= fillers;
163 std::fill_n(writer, fillers, dec.fill);
164 if (add_sign_again) *--writer = '-';
165 }
166
167 piece_ = absl::string_view(writer, end - writer);
168 }
169
170 } // namespace substitute_internal
171 ABSL_NAMESPACE_END
172 } // namespace absl
173