Coverage Report

Created: 2026-05-30 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/abseil-cpp/absl/status/internal/status_internal.cc
Line
Count
Source
1
// Copyright 2023 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/status/internal/status_internal.h"
16
17
#include <atomic>
18
#include <cassert>
19
#include <cstddef>
20
#include <cstdint>
21
#include <cstdio>
22
#include <cstring>
23
#include <memory>
24
#include <string>
25
#include <utility>
26
27
#include "absl/base/attributes.h"
28
#include "absl/base/config.h"
29
#include "absl/base/macros.h"
30
#include "absl/base/nullability.h"
31
#include "absl/debugging/leak_check.h"
32
#include "absl/debugging/stacktrace.h"
33
#include "absl/debugging/symbolize.h"
34
#include "absl/memory/memory.h"
35
#include "absl/status/status.h"
36
#include "absl/status/status_payload_printer.h"
37
#include "absl/strings/cord.h"
38
#include "absl/strings/escaping.h"
39
#include "absl/strings/str_cat.h"
40
#include "absl/strings/str_format.h"
41
#include "absl/strings/str_split.h"
42
#include "absl/strings/string_view.h"
43
#include "absl/types/optional.h"
44
45
namespace absl {
46
ABSL_NAMESPACE_BEGIN
47
namespace status_internal {
48
49
2.17k
void StatusRep::Unref() const {
50
  // Fast path: if ref==1, there is no need for a RefCountDec (since
51
  // this is the only reference and therefore no other thread is
52
  // allowed to be mucking with r).
53
2.17k
  if (ref_.load(std::memory_order_acquire) == 1 ||
54
2.17k
      ref_.fetch_sub(1, std::memory_order_acq_rel) - 1 == 0) {
55
2.17k
    delete this;
56
2.17k
  }
57
2.17k
}
58
59
static absl::optional<size_t> FindPayloadIndexByUrl(
60
0
    const Payloads* payloads, absl::string_view type_url) {
61
0
  if (payloads == nullptr) return absl::nullopt;
62
63
0
  for (size_t i = 0; i < payloads->size(); ++i) {
64
0
    if ((*payloads)[i].type_url == type_url) return i;
65
0
  }
66
67
0
  return absl::nullopt;
68
0
}
69
70
absl::optional<absl::Cord> StatusRep::GetPayload(
71
0
    absl::string_view type_url) const {
72
0
  absl::optional<size_t> index =
73
0
      status_internal::FindPayloadIndexByUrl(payloads_.get(), type_url);
74
0
  if (index.has_value()) return (*payloads_)[index.value()].payload;
75
76
0
  return absl::nullopt;
77
0
}
78
79
0
void StatusRep::SetPayload(absl::string_view type_url, absl::Cord payload) {
80
0
  if (payloads_ == nullptr) {
81
0
    payloads_ = absl::make_unique<status_internal::Payloads>();
82
0
  }
83
84
0
  absl::optional<size_t> index =
85
0
      status_internal::FindPayloadIndexByUrl(payloads_.get(), type_url);
86
0
  if (index.has_value()) {
87
0
    (*payloads_)[index.value()].payload = std::move(payload);
88
0
    return;
89
0
  }
90
91
0
  payloads_->push_back({std::string(type_url), std::move(payload)});
92
0
}
93
94
0
StatusRep::EraseResult StatusRep::ErasePayload(absl::string_view type_url) {
95
0
  absl::optional<size_t> index =
96
0
      status_internal::FindPayloadIndexByUrl(payloads_.get(), type_url);
97
0
  if (!index.has_value()) return {false, Status::PointerToRep(this)};
98
0
  payloads_->erase(payloads_->begin() + index.value());
99
0
  if (payloads_->empty() && message_.empty()) {
100
    // Special case: If this can be represented inlined, it MUST be inlined
101
    // (== depends on this behavior).
102
0
    EraseResult result = {true, Status::CodeToInlinedRep(code_)};
103
0
    Unref();
104
0
    return result;
105
0
  }
106
0
  return {true, Status::PointerToRep(this)};
107
0
}
108
109
void StatusRep::ForEachPayload(
110
    absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor)
111
0
    const {
112
0
  if (auto* payloads = payloads_.get()) {
113
0
    bool in_reverse =
114
0
        payloads->size() > 1 && reinterpret_cast<uintptr_t>(payloads) % 13 > 6;
115
116
0
    for (size_t index = 0; index < payloads->size(); ++index) {
117
0
      const auto& elem =
118
0
          (*payloads)[in_reverse ? payloads->size() - 1 - index : index];
119
120
#ifdef NDEBUG
121
      visitor(elem.type_url, elem.payload);
122
#else
123
      // In debug mode invalidate the type url to prevent users from relying on
124
      // this string lifetime.
125
126
      // NOLINTNEXTLINE intentional extra conversion to force temporary.
127
0
      visitor(std::string(elem.type_url), elem.payload);
128
0
#endif  // NDEBUG
129
0
    }
130
0
  }
131
0
}
132
133
0
std::string StatusRep::ToString(StatusToStringMode mode) const {
134
0
  std::string text;
135
0
  absl::StrAppend(&text, absl::StatusCodeToString(code()), ": ", message());
136
137
0
  const bool with_payload = (mode & StatusToStringMode::kWithPayload) ==
138
0
                            StatusToStringMode::kWithPayload;
139
140
0
  if (with_payload) {
141
0
    status_internal::StatusPayloadPrinter printer =
142
0
        status_internal::GetStatusPayloadPrinter();
143
0
    this->ForEachPayload([&](absl::string_view type_url,
144
0
                             const absl::Cord& payload) {
145
0
      absl::optional<std::string> result;
146
0
      if (printer) result = printer(type_url, payload);
147
0
      absl::StrAppend(
148
0
          &text, " [", type_url, "='",
149
0
          result.has_value() ? *result : absl::CHexEscape(std::string(payload)),
150
0
          "']");
151
0
    });
152
0
  }
153
154
0
  return text;
155
0
}
156
157
0
bool StatusRep::operator==(const StatusRep& other) const {
158
0
  assert(this != &other);
159
0
  if (code_ != other.code_) return false;
160
0
  if (message_ != other.message_) return false;
161
0
  const status_internal::Payloads* this_payloads = payloads_.get();
162
0
  const status_internal::Payloads* other_payloads = other.payloads_.get();
163
164
0
  const status_internal::Payloads no_payloads;
165
0
  const status_internal::Payloads* larger_payloads =
166
0
      this_payloads ? this_payloads : &no_payloads;
167
0
  const status_internal::Payloads* smaller_payloads =
168
0
      other_payloads ? other_payloads : &no_payloads;
169
0
  if (larger_payloads->size() < smaller_payloads->size()) {
170
0
    std::swap(larger_payloads, smaller_payloads);
171
0
  }
172
0
  if ((larger_payloads->size() - smaller_payloads->size()) > 1) return false;
173
  // Payloads can be ordered differently, so we can't just compare payload
174
  // vectors.
175
0
  for (const auto& payload : *larger_payloads) {
176
177
0
    bool found = false;
178
0
    for (const auto& other_payload : *smaller_payloads) {
179
0
      if (payload.type_url == other_payload.type_url) {
180
0
        if (payload.payload != other_payload.payload) {
181
0
          return false;
182
0
        }
183
0
        found = true;
184
0
        break;
185
0
      }
186
0
    }
187
0
    if (!found) return false;
188
0
  }
189
0
  return true;
190
0
}
191
192
0
StatusRep* absl_nonnull StatusRep::CloneAndUnref() const {
193
  // Optimization: no need to create a clone if we already have a refcount of 1.
194
0
  if (ref_.load(std::memory_order_acquire) == 1) {
195
    // All StatusRep instances are heap allocated and mutable, therefore this
196
    // const_cast will never cast away const from a stack instance.
197
    //
198
    // CloneAndUnref is the only method that doesn't involve an external cast to
199
    // get a mutable StatusRep* from the uintptr_t rep stored in Status.
200
0
    return const_cast<StatusRep*>(this);
201
0
  }
202
0
  std::unique_ptr<status_internal::Payloads> payloads;
203
0
  if (payloads_) {
204
0
    payloads = absl::make_unique<status_internal::Payloads>(*payloads_);
205
0
  }
206
0
  auto* new_rep = new StatusRep(code_, message_, std::move(payloads));
207
0
  Unref();
208
0
  return new_rep;
209
0
}
210
211
// Convert canonical code to a value known to this binary.
212
0
absl::StatusCode MapToLocalCode(int value) {
213
0
  absl::StatusCode code = static_cast<absl::StatusCode>(value);
214
0
  switch (code) {
215
0
    case absl::StatusCode::kOk:
216
0
    case absl::StatusCode::kCancelled:
217
0
    case absl::StatusCode::kUnknown:
218
0
    case absl::StatusCode::kInvalidArgument:
219
0
    case absl::StatusCode::kDeadlineExceeded:
220
0
    case absl::StatusCode::kNotFound:
221
0
    case absl::StatusCode::kAlreadyExists:
222
0
    case absl::StatusCode::kPermissionDenied:
223
0
    case absl::StatusCode::kResourceExhausted:
224
0
    case absl::StatusCode::kFailedPrecondition:
225
0
    case absl::StatusCode::kAborted:
226
0
    case absl::StatusCode::kOutOfRange:
227
0
    case absl::StatusCode::kUnimplemented:
228
0
    case absl::StatusCode::kInternal:
229
0
    case absl::StatusCode::kUnavailable:
230
0
    case absl::StatusCode::kDataLoss:
231
0
    case absl::StatusCode::kUnauthenticated:
232
0
      return code;
233
0
    default:
234
0
      return absl::StatusCode::kUnknown;
235
0
  }
236
0
}
237
238
const char* absl_nonnull MakeCheckFailString(
239
0
    const absl::Status* absl_nonnull status, const char* absl_nonnull prefix) {
240
  // There's no need to free this string since the process is crashing.
241
0
  return absl::IgnoreLeak(
242
0
             new std::string(absl::StrCat(
243
0
                 prefix, " (",
244
0
                 status->ToString(StatusToStringMode::kWithEverything), ")")))
245
0
      ->c_str();
246
0
}
247
248
}  // namespace status_internal
249
250
ABSL_NAMESPACE_END
251
}  // namespace absl