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