Coverage Report

Created: 2025-11-29 07:01

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