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
25namespace absl {
26namespace substitute_internal {
27
28void 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
97static const char kHexDigits[] = "0123456789abcdef";
98Arg::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
117Arg::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
138Arg::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