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