• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <cassert>
19 #include <cstddef>
20 #include <cstdint>
21 #include <string>
22 
23 #include "absl/base/config.h"
24 #include "absl/base/internal/raw_logging.h"
25 #include "absl/strings/ascii.h"
26 #include "absl/strings/escaping.h"
27 #include "absl/strings/internal/resize_uninitialized.h"
28 #include "absl/strings/numbers.h"
29 #include "absl/strings/str_cat.h"
30 #include "absl/strings/string_view.h"
31 
32 namespace absl {
33 ABSL_NAMESPACE_BEGIN
34 namespace substitute_internal {
35 
SubstituteAndAppendArray(std::string * output,absl::string_view format,const absl::string_view * args_array,size_t num_args)36 void SubstituteAndAppendArray(std::string* output, absl::string_view format,
37                               const absl::string_view* args_array,
38                               size_t num_args) {
39   // Determine total size needed.
40   size_t size = 0;
41   for (size_t i = 0; i < format.size(); i++) {
42     if (format[i] == '$') {
43       if (i + 1 >= format.size()) {
44 #ifndef NDEBUG
45         ABSL_RAW_LOG(FATAL,
46                      "Invalid absl::Substitute() format string: \"%s\".",
47                      absl::CEscape(format).c_str());
48 #endif
49         return;
50       } else if (absl::ascii_isdigit(
51                      static_cast<unsigned char>(format[i + 1]))) {
52         int index = format[i + 1] - '0';
53         if (static_cast<size_t>(index) >= num_args) {
54 #ifndef NDEBUG
55           ABSL_RAW_LOG(
56               FATAL,
57               "Invalid absl::Substitute() format string: asked for \"$"
58               "%d\", but only %d args were given.  Full format string was: "
59               "\"%s\".",
60               index, static_cast<int>(num_args), absl::CEscape(format).c_str());
61 #endif
62           return;
63         }
64         size += args_array[index].size();
65         ++i;  // Skip next char.
66       } else if (format[i + 1] == '$') {
67         ++size;
68         ++i;  // Skip next char.
69       } else {
70 #ifndef NDEBUG
71         ABSL_RAW_LOG(FATAL,
72                      "Invalid absl::Substitute() format string: \"%s\".",
73                      absl::CEscape(format).c_str());
74 #endif
75         return;
76       }
77     } else {
78       ++size;
79     }
80   }
81 
82   if (size == 0) return;
83 
84   // Build the string.
85   size_t original_size = output->size();
86   strings_internal::STLStringResizeUninitializedAmortized(output,
87                                                           original_size + size);
88   char* target = &(*output)[original_size];
89   for (size_t i = 0; i < format.size(); i++) {
90     if (format[i] == '$') {
91       if (absl::ascii_isdigit(static_cast<unsigned char>(format[i + 1]))) {
92         const absl::string_view src = args_array[format[i + 1] - '0'];
93         target = std::copy(src.begin(), src.end(), target);
94         ++i;  // Skip next char.
95       } else if (format[i + 1] == '$') {
96         *target++ = '$';
97         ++i;  // Skip next char.
98       }
99     } else {
100       *target++ = format[i];
101     }
102   }
103 
104   assert(target == output->data() + output->size());
105 }
106 
Arg(const void * value)107 Arg::Arg(const void* value) {
108   static_assert(sizeof(scratch_) >= sizeof(value) * 2 + 2,
109                 "fix sizeof(scratch_)");
110   if (value == nullptr) {
111     piece_ = "NULL";
112   } else {
113     char* ptr = scratch_ + sizeof(scratch_);
114     uintptr_t num = reinterpret_cast<uintptr_t>(value);
115     do {
116       *--ptr = absl::numbers_internal::kHexChar[num & 0xf];
117       num >>= 4;
118     } while (num != 0);
119     *--ptr = 'x';
120     *--ptr = '0';
121     piece_ = absl::string_view(
122         ptr, static_cast<size_t>(scratch_ + sizeof(scratch_) - ptr));
123   }
124 }
125 
126 // TODO(jorg): Don't duplicate so much code between here and str_cat.cc
Arg(Hex hex)127 Arg::Arg(Hex hex) {
128   char* const end = &scratch_[numbers_internal::kFastToBufferSize];
129   char* writer = end;
130   uint64_t value = hex.value;
131   do {
132     *--writer = absl::numbers_internal::kHexChar[value & 0xF];
133     value >>= 4;
134   } while (value != 0);
135 
136   char* beg;
137   if (end - writer < hex.width) {
138     beg = end - hex.width;
139     std::fill_n(beg, writer - beg, hex.fill);
140   } else {
141     beg = writer;
142   }
143 
144   piece_ = absl::string_view(beg, static_cast<size_t>(end - beg));
145 }
146 
147 // TODO(jorg): Don't duplicate so much code between here and str_cat.cc
Arg(Dec dec)148 Arg::Arg(Dec dec) {
149   assert(dec.width <= numbers_internal::kFastToBufferSize);
150   char* const end = &scratch_[numbers_internal::kFastToBufferSize];
151   char* const minfill = end - dec.width;
152   char* writer = end;
153   uint64_t value = dec.value;
154   bool neg = dec.neg;
155   while (value > 9) {
156     *--writer = '0' + (value % 10);
157     value /= 10;
158   }
159   *--writer = '0' + static_cast<char>(value);
160   if (neg) *--writer = '-';
161 
162   ptrdiff_t fillers = writer - minfill;
163   if (fillers > 0) {
164     // Tricky: if the fill character is ' ', then it's <fill><+/-><digits>
165     // But...: if the fill character is '0', then it's <+/-><fill><digits>
166     bool add_sign_again = false;
167     if (neg && dec.fill == '0') {  // If filling with '0',
168       ++writer;                    // ignore the sign we just added
169       add_sign_again = true;       // and re-add the sign later.
170     }
171     writer -= fillers;
172     std::fill_n(writer, fillers, dec.fill);
173     if (add_sign_again) *--writer = '-';
174   }
175 
176   piece_ = absl::string_view(writer, static_cast<size_t>(end - writer));
177 }
178 
179 }  // namespace substitute_internal
180 ABSL_NAMESPACE_END
181 }  // namespace absl
182