Commit 243b7d38 by Abseil Team Committed by Copybara-Service

Change absl::Status implementation to be amenable to [[clang:trivial_abi]] annotation.

This moves the implementation of most methods from absl::Status to absl::status_internal::StatusRep, and ensures that no calls to absl::Status methods are in a cc file.

Stub implementations checking only inlined rep properties and calling no-op (RepToPointer) or out of line methods exist in status.h

PiperOrigin-RevId: 566187430
Change-Id: I356ec29c0970ffe82eac2a5d98850e647fcd5ea5
parent 2c1e7e3c
...@@ -249,6 +249,7 @@ set(ABSL_INTERNAL_DLL_FILES ...@@ -249,6 +249,7 @@ set(ABSL_INTERNAL_DLL_FILES
"random/uniform_real_distribution.h" "random/uniform_real_distribution.h"
"random/zipf_distribution.h" "random/zipf_distribution.h"
"status/internal/status_internal.h" "status/internal/status_internal.h"
"status/internal/status_internal.cc"
"status/internal/statusor_internal.h" "status/internal/statusor_internal.h"
"status/status.h" "status/status.h"
"status/status.cc" "status/status.cc"
......
...@@ -31,6 +31,7 @@ licenses(["notice"]) ...@@ -31,6 +31,7 @@ licenses(["notice"])
cc_library( cc_library(
name = "status", name = "status",
srcs = [ srcs = [
"internal/status_internal.cc",
"internal/status_internal.h", "internal/status_internal.h",
"status.cc", "status.cc",
"status_payload_printer.cc", "status_payload_printer.cc",
......
...@@ -20,6 +20,7 @@ absl_cc_library( ...@@ -20,6 +20,7 @@ absl_cc_library(
"status.h" "status.h"
SRCS SRCS
"internal/status_internal.h" "internal/status_internal.h"
"internal/status_internal.cc"
"status.cc" "status.cc"
"status_payload_printer.h" "status_payload_printer.h"
"status_payload_printer.cc" "status_payload_printer.cc"
......
// Copyright 2023 The Abseil Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/status/internal/status_internal.h"
#include <atomic>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <memory>
#include <string>
#include <utility>
#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/base/macros.h"
#include "absl/debugging/stacktrace.h"
#include "absl/debugging/symbolize.h"
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/status/status_payload_printer.h"
#include "absl/strings/cord.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace status_internal {
void StatusRep::Unref() const {
// Fast path: if ref==1, there is no need for a RefCountDec (since
// this is the only reference and therefore no other thread is
// allowed to be mucking with r).
if (ref_.load(std::memory_order_acquire) == 1 ||
ref_.fetch_sub(1, std::memory_order_acq_rel) - 1 == 0) {
delete this;
}
}
static absl::optional<size_t> FindPayloadIndexByUrl(
const Payloads* payloads, absl::string_view type_url) {
if (payloads == nullptr) return absl::nullopt;
for (size_t i = 0; i < payloads->size(); ++i) {
if ((*payloads)[i].type_url == type_url) return i;
}
return absl::nullopt;
}
absl::optional<absl::Cord> StatusRep::GetPayload(
absl::string_view type_url) const {
absl::optional<size_t> index =
status_internal::FindPayloadIndexByUrl(payloads_.get(), type_url);
if (index.has_value()) return (*payloads_)[index.value()].payload;
return absl::nullopt;
}
void StatusRep::SetPayload(absl::string_view type_url, absl::Cord payload) {
if (payloads_ == nullptr) {
payloads_ = absl::make_unique<status_internal::Payloads>();
}
absl::optional<size_t> index =
status_internal::FindPayloadIndexByUrl(payloads_.get(), type_url);
if (index.has_value()) {
(*payloads_)[index.value()].payload = std::move(payload);
return;
}
payloads_->push_back({std::string(type_url), std::move(payload)});
}
StatusRep::EraseResult StatusRep::ErasePayload(absl::string_view type_url) {
absl::optional<size_t> index =
status_internal::FindPayloadIndexByUrl(payloads_.get(), type_url);
if (!index.has_value()) return {false, Status::PointerToRep(this)};
payloads_->erase(payloads_->begin() + index.value());
if (payloads_->empty() && message_.empty()) {
// Special case: If this can be represented inlined, it MUST be inlined
// (== depends on this behavior).
EraseResult result = {true, Status::CodeToInlinedRep(code_)};
Unref();
return result;
}
return {true, Status::PointerToRep(this)};
}
void StatusRep::ForEachPayload(
absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor)
const {
if (auto* payloads = payloads_.get()) {
bool in_reverse =
payloads->size() > 1 && reinterpret_cast<uintptr_t>(payloads) % 13 > 6;
for (size_t index = 0; index < payloads->size(); ++index) {
const auto& elem =
(*payloads)[in_reverse ? payloads->size() - 1 - index : index];
#ifdef NDEBUG
visitor(elem.type_url, elem.payload);
#else
// In debug mode invalidate the type url to prevent users from relying on
// this string lifetime.
// NOLINTNEXTLINE intentional extra conversion to force temporary.
visitor(std::string(elem.type_url), elem.payload);
#endif // NDEBUG
}
}
}
std::string StatusRep::ToString(StatusToStringMode mode) const {
std::string text;
absl::StrAppend(&text, absl::StatusCodeToString(code()), ": ", message());
const bool with_payload = (mode & StatusToStringMode::kWithPayload) ==
StatusToStringMode::kWithPayload;
if (with_payload) {
status_internal::StatusPayloadPrinter printer =
status_internal::GetStatusPayloadPrinter();
this->ForEachPayload([&](absl::string_view type_url,
const absl::Cord& payload) {
absl::optional<std::string> result;
if (printer) result = printer(type_url, payload);
absl::StrAppend(
&text, " [", type_url, "='",
result.has_value() ? *result : absl::CHexEscape(std::string(payload)),
"']");
});
}
return text;
}
bool StatusRep::operator==(const StatusRep& other) const {
assert(this != &other);
if (code_ != other.code_) return false;
if (message_ != other.message_) return false;
const status_internal::Payloads* this_payloads = payloads_.get();
const status_internal::Payloads* other_payloads = other.payloads_.get();
const status_internal::Payloads no_payloads;
const status_internal::Payloads* larger_payloads =
this_payloads ? this_payloads : &no_payloads;
const status_internal::Payloads* smaller_payloads =
other_payloads ? other_payloads : &no_payloads;
if (larger_payloads->size() < smaller_payloads->size()) {
std::swap(larger_payloads, smaller_payloads);
}
if ((larger_payloads->size() - smaller_payloads->size()) > 1) return false;
// Payloads can be ordered differently, so we can't just compare payload
// vectors.
for (const auto& payload : *larger_payloads) {
bool found = false;
for (const auto& other_payload : *smaller_payloads) {
if (payload.type_url == other_payload.type_url) {
if (payload.payload != other_payload.payload) {
return false;
}
found = true;
break;
}
}
if (!found) return false;
}
return true;
}
StatusRep* StatusRep::CloneAndUnref() const {
// Optimization: no need to create a clone if we already have a refcount of 1.
if (ref_.load(std::memory_order_acquire) == 1) {
// All StatusRep instances are heap allocated and mutable, therefore this
// const_cast will never cast away const from a stack instance.
//
// CloneAndUnref is the only method that doesn't involve an external cast to
// get a mutable StatusRep* from the uintptr_t rep stored in Status.
return const_cast<StatusRep*>(this);
}
std::unique_ptr<status_internal::Payloads> payloads;
if (payloads_) {
payloads = absl::make_unique<status_internal::Payloads>(*payloads_);
}
auto* new_rep = new StatusRep(code_, message_, std::move(payloads));
Unref();
return new_rep;
}
// Convert canonical code to a value known to this binary.
absl::StatusCode MapToLocalCode(int value) {
absl::StatusCode code = static_cast<absl::StatusCode>(value);
switch (code) {
case absl::StatusCode::kOk:
case absl::StatusCode::kCancelled:
case absl::StatusCode::kUnknown:
case absl::StatusCode::kInvalidArgument:
case absl::StatusCode::kDeadlineExceeded:
case absl::StatusCode::kNotFound:
case absl::StatusCode::kAlreadyExists:
case absl::StatusCode::kPermissionDenied:
case absl::StatusCode::kResourceExhausted:
case absl::StatusCode::kFailedPrecondition:
case absl::StatusCode::kAborted:
case absl::StatusCode::kOutOfRange:
case absl::StatusCode::kUnimplemented:
case absl::StatusCode::kInternal:
case absl::StatusCode::kUnavailable:
case absl::StatusCode::kDataLoss:
case absl::StatusCode::kUnauthenticated:
return code;
default:
return absl::StatusCode::kUnknown;
}
}
std::string* MakeCheckFailString(const absl::Status* status,
const char* prefix) {
return new std::string(
absl::StrCat(prefix, " (",
status->ToString(StatusToStringMode::kWithEverything), ")"));
}
} // namespace status_internal
ABSL_NAMESPACE_END
} // namespace absl
...@@ -14,13 +14,18 @@ ...@@ -14,13 +14,18 @@
#ifndef ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_ #ifndef ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_
#define ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_ #define ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_
#include <atomic>
#include <cstdint>
#include <memory> #include <memory>
#include <string> #include <string>
#include <utility> #include <utility>
#include "absl/base/attributes.h" #include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/container/inlined_vector.h" #include "absl/container/inlined_vector.h"
#include "absl/strings/cord.h" #include "absl/strings/cord.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#ifndef SWIG #ifndef SWIG
// Disabled for SWIG as it doesn't parse attributes correctly. // Disabled for SWIG as it doesn't parse attributes correctly.
...@@ -44,6 +49,7 @@ namespace absl { ...@@ -44,6 +49,7 @@ namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
enum class StatusCode : int; enum class StatusCode : int;
enum class StatusToStringMode : int;
namespace status_internal { namespace status_internal {
...@@ -56,22 +62,54 @@ struct Payload { ...@@ -56,22 +62,54 @@ struct Payload {
using Payloads = absl::InlinedVector<Payload, 1>; using Payloads = absl::InlinedVector<Payload, 1>;
// Reference-counted representation of Status data. // Reference-counted representation of Status data.
struct StatusRep { class StatusRep {
public:
StatusRep(absl::StatusCode code_arg, absl::string_view message_arg, StatusRep(absl::StatusCode code_arg, absl::string_view message_arg,
std::unique_ptr<status_internal::Payloads> payloads_arg) std::unique_ptr<status_internal::Payloads> payloads_arg)
: ref(int32_t{1}), : ref_(int32_t{1}),
code(code_arg), code_(code_arg),
message(message_arg), message_(message_arg),
payloads(std::move(payloads_arg)) {} payloads_(std::move(payloads_arg)) {}
std::atomic<int32_t> ref; absl::StatusCode code() const { return code_; }
absl::StatusCode code; const std::string& message() const { return message_; }
// Ref and unref are const to allow access through a const pointer, and are
// used during copying operations.
void Ref() const { ref_.fetch_add(1, std::memory_order_relaxed); }
void Unref() const;
// Payload methods correspond to the same methods in absl::Status.
absl::optional<absl::Cord> GetPayload(absl::string_view type_url) const;
void SetPayload(absl::string_view type_url, absl::Cord payload);
struct EraseResult {
bool erased;
uintptr_t new_rep;
};
EraseResult ErasePayload(absl::string_view type_url);
void ForEachPayload(
absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor)
const;
std::string ToString(StatusToStringMode mode) const;
bool operator==(const StatusRep& other) const;
bool operator!=(const StatusRep& other) const { return !(*this == other); }
// Returns an equivalent heap allocated StatusRep with refcount 1.
//
// `this` is not safe to be used after calling as it may have been deleted.
StatusRep* CloneAndUnref() const;
private:
mutable std::atomic<int32_t> ref_;
absl::StatusCode code_;
// As an internal implementation detail, we guarantee that if status.message() // As an internal implementation detail, we guarantee that if status.message()
// is non-empty, then the resulting string_view is null terminated. // is non-empty, then the resulting string_view is null terminated.
// This is required to implement 'StatusMessageAsCStr(...)' // This is required to implement 'StatusMessageAsCStr(...)'
std::string message; std::string message_;
std::unique_ptr<status_internal::Payloads> payloads; std::unique_ptr<status_internal::Payloads> payloads_;
}; };
absl::StatusCode MapToLocalCode(int value); absl::StatusCode MapToLocalCode(int value);
......
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
#include <memory> #include <memory>
#include <ostream> #include <ostream>
#include <string> #include <string>
#include <utility>
#include "absl/base/attributes.h" #include "absl/base/attributes.h"
#include "absl/base/config.h" #include "absl/base/config.h"
...@@ -31,17 +30,12 @@ ...@@ -31,17 +30,12 @@
#include "absl/base/macros.h" #include "absl/base/macros.h"
#include "absl/debugging/stacktrace.h" #include "absl/debugging/stacktrace.h"
#include "absl/debugging/symbolize.h" #include "absl/debugging/symbolize.h"
#include "absl/functional/function_ref.h"
#include "absl/memory/memory.h"
#include "absl/status/internal/status_internal.h" #include "absl/status/internal/status_internal.h"
#include "absl/status/status_payload_printer.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h" #include "absl/strings/str_format.h"
#include "absl/strings/str_split.h" #include "absl/strings/str_split.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "absl/types/optional.h" #include "absl/types/optional.h"
#include "absl/types/span.h"
namespace absl { namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
...@@ -95,120 +89,6 @@ std::ostream& operator<<(std::ostream& os, StatusCode code) { ...@@ -95,120 +89,6 @@ std::ostream& operator<<(std::ostream& os, StatusCode code) {
return os << StatusCodeToString(code); return os << StatusCodeToString(code);
} }
namespace status_internal {
static absl::optional<size_t> FindPayloadIndexByUrl(
const Payloads* payloads, absl::string_view type_url) {
if (payloads == nullptr) return absl::nullopt;
for (size_t i = 0; i < payloads->size(); ++i) {
if ((*payloads)[i].type_url == type_url) return i;
}
return absl::nullopt;
}
// Convert canonical code to a value known to this binary.
absl::StatusCode MapToLocalCode(int value) {
absl::StatusCode code = static_cast<absl::StatusCode>(value);
switch (code) {
case absl::StatusCode::kOk:
case absl::StatusCode::kCancelled:
case absl::StatusCode::kUnknown:
case absl::StatusCode::kInvalidArgument:
case absl::StatusCode::kDeadlineExceeded:
case absl::StatusCode::kNotFound:
case absl::StatusCode::kAlreadyExists:
case absl::StatusCode::kPermissionDenied:
case absl::StatusCode::kResourceExhausted:
case absl::StatusCode::kFailedPrecondition:
case absl::StatusCode::kAborted:
case absl::StatusCode::kOutOfRange:
case absl::StatusCode::kUnimplemented:
case absl::StatusCode::kInternal:
case absl::StatusCode::kUnavailable:
case absl::StatusCode::kDataLoss:
case absl::StatusCode::kUnauthenticated:
return code;
default:
return absl::StatusCode::kUnknown;
}
}
} // namespace status_internal
absl::optional<absl::Cord> Status::GetPayload(
absl::string_view type_url) const {
const auto* payloads = GetPayloads();
absl::optional<size_t> index =
status_internal::FindPayloadIndexByUrl(payloads, type_url);
if (index.has_value()) return (*payloads)[index.value()].payload;
return absl::nullopt;
}
void Status::SetPayload(absl::string_view type_url, absl::Cord payload) {
if (ok()) return;
PrepareToModify();
status_internal::StatusRep* rep = RepToPointer(rep_);
if (!rep->payloads) {
rep->payloads = absl::make_unique<status_internal::Payloads>();
}
absl::optional<size_t> index =
status_internal::FindPayloadIndexByUrl(rep->payloads.get(), type_url);
if (index.has_value()) {
(*rep->payloads)[index.value()].payload = std::move(payload);
return;
}
rep->payloads->push_back({std::string(type_url), std::move(payload)});
}
bool Status::ErasePayload(absl::string_view type_url) {
absl::optional<size_t> index =
status_internal::FindPayloadIndexByUrl(GetPayloads(), type_url);
if (index.has_value()) {
PrepareToModify();
GetPayloads()->erase(GetPayloads()->begin() + index.value());
if (GetPayloads()->empty() && message().empty()) {
// Special case: If this can be represented inlined, it MUST be
// inlined (EqualsSlow depends on this behavior).
StatusCode c = static_cast<StatusCode>(raw_code());
Unref(rep_);
rep_ = CodeToInlinedRep(c);
}
return true;
}
return false;
}
void Status::ForEachPayload(
absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor)
const {
if (auto* payloads = GetPayloads()) {
bool in_reverse =
payloads->size() > 1 && reinterpret_cast<uintptr_t>(payloads) % 13 > 6;
for (size_t index = 0; index < payloads->size(); ++index) {
const auto& elem =
(*payloads)[in_reverse ? payloads->size() - 1 - index : index];
#ifdef NDEBUG
visitor(elem.type_url, elem.payload);
#else
// In debug mode invalidate the type url to prevent users from relying on
// this string lifetime.
// NOLINTNEXTLINE intentional extra conversion to force temporary.
visitor(std::string(elem.type_url), elem.payload);
#endif // NDEBUG
}
}
}
const std::string* Status::EmptyString() { const std::string* Status::EmptyString() {
static union EmptyString { static union EmptyString {
std::string str; std::string str;
...@@ -226,17 +106,6 @@ const std::string* Status::MovedFromString() { ...@@ -226,17 +106,6 @@ const std::string* Status::MovedFromString() {
return moved_from_string; return moved_from_string;
} }
void Status::UnrefNonInlined(uintptr_t rep) {
status_internal::StatusRep* r = RepToPointer(rep);
// Fast path: if ref==1, there is no need for a RefCountDec (since
// this is the only reference and therefore no other thread is
// allowed to be mucking with r).
if (r->ref.load(std::memory_order_acquire) == 1 ||
r->ref.fetch_sub(1, std::memory_order_acq_rel) - 1 == 0) {
delete r;
}
}
Status::Status(absl::StatusCode code, absl::string_view msg) Status::Status(absl::StatusCode code, absl::string_view msg)
: rep_(CodeToInlinedRep(code)) { : rep_(CodeToInlinedRep(code)) {
if (code != absl::StatusCode::kOk && !msg.empty()) { if (code != absl::StatusCode::kOk && !msg.empty()) {
...@@ -244,97 +113,19 @@ Status::Status(absl::StatusCode code, absl::string_view msg) ...@@ -244,97 +113,19 @@ Status::Status(absl::StatusCode code, absl::string_view msg)
} }
} }
int Status::raw_code() const { status_internal::StatusRep* Status::PrepareToModify(uintptr_t rep) {
if (IsInlined(rep_)) { if (IsInlined(rep)) {
return static_cast<int>(InlinedRepToCode(rep_)); return new status_internal::StatusRep(InlinedRepToCode(rep),
} absl::string_view(), nullptr);
status_internal::StatusRep* rep = RepToPointer(rep_);
return static_cast<int>(rep->code);
}
absl::StatusCode Status::code() const {
return status_internal::MapToLocalCode(raw_code());
}
void Status::PrepareToModify() {
ABSL_RAW_CHECK(!ok(), "PrepareToModify shouldn't be called on OK status.");
if (IsInlined(rep_)) {
rep_ = PointerToRep(new status_internal::StatusRep(
static_cast<absl::StatusCode>(raw_code()), absl::string_view(),
nullptr));
return;
}
uintptr_t rep_i = rep_;
status_internal::StatusRep* rep = RepToPointer(rep_);
if (rep->ref.load(std::memory_order_acquire) != 1) {
std::unique_ptr<status_internal::Payloads> payloads;
if (rep->payloads) {
payloads = absl::make_unique<status_internal::Payloads>(*rep->payloads);
}
status_internal::StatusRep* const new_rep = new status_internal::StatusRep(
rep->code, message(), std::move(payloads));
rep_ = PointerToRep(new_rep);
UnrefNonInlined(rep_i);
} }
return RepToPointer(rep)->CloneAndUnref();
} }
bool Status::EqualsSlow(const absl::Status& a, const absl::Status& b) { std::string Status::ToStringSlow(uintptr_t rep, StatusToStringMode mode) {
if (IsInlined(a.rep_) != IsInlined(b.rep_)) return false; if (IsInlined(rep)) {
if (a.message() != b.message()) return false; return absl::StrCat(absl::StatusCodeToString(InlinedRepToCode(rep)), ": ");
if (a.raw_code() != b.raw_code()) return false;
if (a.GetPayloads() == b.GetPayloads()) return true;
const status_internal::Payloads no_payloads;
const status_internal::Payloads* larger_payloads =
a.GetPayloads() ? a.GetPayloads() : &no_payloads;
const status_internal::Payloads* smaller_payloads =
b.GetPayloads() ? b.GetPayloads() : &no_payloads;
if (larger_payloads->size() < smaller_payloads->size()) {
std::swap(larger_payloads, smaller_payloads);
}
if ((larger_payloads->size() - smaller_payloads->size()) > 1) return false;
// Payloads can be ordered differently, so we can't just compare payload
// vectors.
for (const auto& payload : *larger_payloads) {
bool found = false;
for (const auto& other_payload : *smaller_payloads) {
if (payload.type_url == other_payload.type_url) {
if (payload.payload != other_payload.payload) {
return false;
}
found = true;
break;
}
}
if (!found) return false;
} }
return true; return RepToPointer(rep)->ToString(mode);
}
std::string Status::ToStringSlow(StatusToStringMode mode) const {
std::string text;
absl::StrAppend(&text, absl::StatusCodeToString(code()), ": ", message());
const bool with_payload = (mode & StatusToStringMode::kWithPayload) ==
StatusToStringMode::kWithPayload;
if (with_payload) {
status_internal::StatusPayloadPrinter printer =
status_internal::GetStatusPayloadPrinter();
this->ForEachPayload([&](absl::string_view type_url,
const absl::Cord& payload) {
absl::optional<std::string> result;
if (printer) result = printer(type_url, payload);
absl::StrAppend(
&text, " [", type_url, "='",
result.has_value() ? *result : absl::CHexEscape(std::string(payload)),
"']");
});
}
return text;
} }
std::ostream& operator<<(std::ostream& os, const Status& x) { std::ostream& operator<<(std::ostream& os, const Status& x) {
...@@ -623,17 +414,6 @@ Status ErrnoToStatus(int error_number, absl::string_view message) { ...@@ -623,17 +414,6 @@ Status ErrnoToStatus(int error_number, absl::string_view message) {
MessageForErrnoToStatus(error_number, message)); MessageForErrnoToStatus(error_number, message));
} }
namespace status_internal {
std::string* MakeCheckFailString(const absl::Status* status,
const char* prefix) {
return new std::string(
absl::StrCat(prefix, " (",
status->ToString(StatusToStringMode::kWithEverything), ")"));
}
} // namespace status_internal
const char* StatusMessageAsCStr(const Status& status) { const char* StatusMessageAsCStr(const Status& status) {
// As an internal implementation detail, we guarantee that if status.message() // As an internal implementation detail, we guarantee that if status.message()
// is non-empty, then the resulting string_view is null terminated. // is non-empty, then the resulting string_view is null terminated.
......
...@@ -59,10 +59,12 @@ ...@@ -59,10 +59,12 @@
#include "absl/base/attributes.h" #include "absl/base/attributes.h"
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/base/macros.h"
#include "absl/base/optimization.h" #include "absl/base/optimization.h"
#include "absl/functional/function_ref.h" #include "absl/functional/function_ref.h"
#include "absl/status/internal/status_internal.h" #include "absl/status/internal/status_internal.h"
#include "absl/strings/cord.h" #include "absl/strings/cord.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "absl/types/optional.h" #include "absl/types/optional.h"
...@@ -607,18 +609,15 @@ class Status final { ...@@ -607,18 +609,15 @@ class Status final {
// code, and an empty error message. // code, and an empty error message.
explicit Status(absl::StatusCode code); explicit Status(absl::StatusCode code);
static void UnrefNonInlined(uintptr_t rep); // Underlying constructor for status from a rep_.
explicit Status(uintptr_t rep) : rep_(rep) {}
static void Ref(uintptr_t rep); static void Ref(uintptr_t rep);
static void Unref(uintptr_t rep); static void Unref(uintptr_t rep);
// REQUIRES: !ok() // REQUIRES: !ok()
// Ensures rep_ is not shared with any other Status. // Ensures rep is not inlined or shared with any other Status.
void PrepareToModify(); static status_internal::StatusRep* PrepareToModify(uintptr_t rep);
const status_internal::Payloads* GetPayloads() const;
status_internal::Payloads* GetPayloads();
static bool EqualsSlow(const absl::Status& a, const absl::Status& b);
// MSVC 14.0 limitation requires the const. // MSVC 14.0 limitation requires the const.
static constexpr const char kMovedFromString[] = static constexpr const char kMovedFromString[] =
...@@ -629,24 +628,24 @@ class Status final { ...@@ -629,24 +628,24 @@ class Status final {
// Returns whether rep contains an inlined representation. // Returns whether rep contains an inlined representation.
// See rep_ for details. // See rep_ for details.
static bool IsInlined(uintptr_t rep); static constexpr bool IsInlined(uintptr_t rep);
// Indicates whether this Status was the rhs of a move operation. See rep_ // Indicates whether this Status was the rhs of a move operation. See rep_
// for details. // for details.
static bool IsMovedFrom(uintptr_t rep); static constexpr bool IsMovedFrom(uintptr_t rep);
static uintptr_t MovedFromRep(); static constexpr uintptr_t MovedFromRep();
// Convert between error::Code and the inlined uintptr_t representation used // Convert between error::Code and the inlined uintptr_t representation used
// by rep_. See rep_ for details. // by rep_. See rep_ for details.
static uintptr_t CodeToInlinedRep(absl::StatusCode code); static constexpr uintptr_t CodeToInlinedRep(absl::StatusCode code);
static absl::StatusCode InlinedRepToCode(uintptr_t rep); static constexpr absl::StatusCode InlinedRepToCode(uintptr_t rep);
// Converts between StatusRep* and the external uintptr_t representation used // Converts between StatusRep* and the external uintptr_t representation used
// by rep_. See rep_ for details. // by rep_. See rep_ for details.
static uintptr_t PointerToRep(status_internal::StatusRep* r); static uintptr_t PointerToRep(status_internal::StatusRep* r);
static status_internal::StatusRep* RepToPointer(uintptr_t r); static const status_internal::StatusRep* RepToPointer(uintptr_t r);
std::string ToStringSlow(StatusToStringMode mode) const; static std::string ToStringSlow(uintptr_t rep, StatusToStringMode mode);
// Status supports two different representations. // Status supports two different representations.
// - When the low bit is set it is an inlined representation. // - When the low bit is set it is an inlined representation.
...@@ -657,6 +656,8 @@ class Status final { ...@@ -657,6 +656,8 @@ class Status final {
// In this case all the data comes from a heap allocated Rep object. // In this case all the data comes from a heap allocated Rep object.
// rep_ is a status_internal::StatusRep* pointer to that structure. // rep_ is a status_internal::StatusRep* pointer to that structure.
uintptr_t rep_; uintptr_t rep_;
friend class status_internal::StatusRep;
}; };
// OkStatus() // OkStatus()
...@@ -760,11 +761,11 @@ Status ErrnoToStatus(int error_number, absl::string_view message); ...@@ -760,11 +761,11 @@ Status ErrnoToStatus(int error_number, absl::string_view message);
// Implementation details follow // Implementation details follow
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
inline Status::Status() : rep_(CodeToInlinedRep(absl::StatusCode::kOk)) {} inline Status::Status() : Status(absl::StatusCode::kOk) {}
inline Status::Status(absl::StatusCode code) : rep_(CodeToInlinedRep(code)) {} inline Status::Status(absl::StatusCode code) : Status(CodeToInlinedRep(code)) {}
inline Status::Status(const Status& x) : rep_(x.rep_) { Ref(rep_); } inline Status::Status(const Status& x) : Status(x.rep_) { Ref(rep_); }
inline Status& Status::operator=(const Status& x) { inline Status& Status::operator=(const Status& x) {
uintptr_t old_rep = rep_; uintptr_t old_rep = rep_;
...@@ -776,7 +777,7 @@ inline Status& Status::operator=(const Status& x) { ...@@ -776,7 +777,7 @@ inline Status& Status::operator=(const Status& x) {
return *this; return *this;
} }
inline Status::Status(Status&& x) noexcept : rep_(x.rep_) { inline Status::Status(Status&& x) noexcept : Status(x.rep_) {
x.rep_ = MovedFromRep(); x.rep_ = MovedFromRep();
} }
...@@ -808,15 +809,27 @@ inline bool Status::ok() const { ...@@ -808,15 +809,27 @@ inline bool Status::ok() const {
return rep_ == CodeToInlinedRep(absl::StatusCode::kOk); return rep_ == CodeToInlinedRep(absl::StatusCode::kOk);
} }
inline absl::StatusCode Status::code() const {
return status_internal::MapToLocalCode(raw_code());
}
inline int Status::raw_code() const {
if (IsInlined(rep_)) return static_cast<int>(InlinedRepToCode(rep_));
return static_cast<int>(RepToPointer(rep_)->code());
}
inline absl::string_view Status::message() const { inline absl::string_view Status::message() const {
return !IsInlined(rep_) return !IsInlined(rep_)
? RepToPointer(rep_)->message ? RepToPointer(rep_)->message()
: (IsMovedFrom(rep_) ? absl::string_view(kMovedFromString) : (IsMovedFrom(rep_) ? absl::string_view(kMovedFromString)
: absl::string_view()); : absl::string_view());
} }
inline bool operator==(const Status& lhs, const Status& rhs) { inline bool operator==(const Status& lhs, const Status& rhs) {
return lhs.rep_ == rhs.rep_ || Status::EqualsSlow(lhs, rhs); if (lhs.rep_ == rhs.rep_) return true;
if (Status::IsInlined(lhs.rep_)) return false;
if (Status::IsInlined(rhs.rep_)) return false;
return *Status::RepToPointer(lhs.rep_) == *Status::RepToPointer(rhs.rep_);
} }
inline bool operator!=(const Status& lhs, const Status& rhs) { inline bool operator!=(const Status& lhs, const Status& rhs) {
...@@ -824,7 +837,7 @@ inline bool operator!=(const Status& lhs, const Status& rhs) { ...@@ -824,7 +837,7 @@ inline bool operator!=(const Status& lhs, const Status& rhs) {
} }
inline std::string Status::ToString(StatusToStringMode mode) const { inline std::string Status::ToString(StatusToStringMode mode) const {
return ok() ? "OK" : ToStringSlow(mode); return ok() ? "OK" : ToStringSlow(rep_, mode);
} }
inline void Status::IgnoreError() const { inline void Status::IgnoreError() const {
...@@ -836,34 +849,54 @@ inline void swap(absl::Status& a, absl::Status& b) { ...@@ -836,34 +849,54 @@ inline void swap(absl::Status& a, absl::Status& b) {
swap(a.rep_, b.rep_); swap(a.rep_, b.rep_);
} }
inline const status_internal::Payloads* Status::GetPayloads() const { inline absl::optional<absl::Cord> Status::GetPayload(
return IsInlined(rep_) ? nullptr : RepToPointer(rep_)->payloads.get(); absl::string_view type_url) const {
if (IsInlined(rep_)) return absl::nullopt;
return RepToPointer(rep_)->GetPayload(type_url);
} }
inline status_internal::Payloads* Status::GetPayloads() { inline void Status::SetPayload(absl::string_view type_url, absl::Cord payload) {
return IsInlined(rep_) ? nullptr : RepToPointer(rep_)->payloads.get(); if (ok()) return;
status_internal::StatusRep* rep = PrepareToModify(rep_);
rep->SetPayload(type_url, std::move(payload));
rep_ = PointerToRep(rep);
} }
inline bool Status::IsInlined(uintptr_t rep) { return (rep & 1) != 0; } inline bool Status::ErasePayload(absl::string_view type_url) {
if (IsInlined(rep_)) return false;
inline bool Status::IsMovedFrom(uintptr_t rep) { return (rep & 2) != 0; } status_internal::StatusRep* rep = PrepareToModify(rep_);
auto res = rep->ErasePayload(type_url);
rep_ = res.new_rep;
return res.erased;
}
inline uintptr_t Status::MovedFromRep() { inline void Status::ForEachPayload(
return CodeToInlinedRep(absl::StatusCode::kInternal) | 2; absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor)
const {
if (IsInlined(rep_)) return;
RepToPointer(rep_)->ForEachPayload(visitor);
} }
inline uintptr_t Status::CodeToInlinedRep(absl::StatusCode code) { constexpr bool Status::IsInlined(uintptr_t rep) { return (rep & 1) != 0; }
constexpr bool Status::IsMovedFrom(uintptr_t rep) { return (rep & 2) != 0; }
constexpr uintptr_t Status::CodeToInlinedRep(absl::StatusCode code) {
return (static_cast<uintptr_t>(code) << 2) + 1; return (static_cast<uintptr_t>(code) << 2) + 1;
} }
inline absl::StatusCode Status::InlinedRepToCode(uintptr_t rep) { constexpr absl::StatusCode Status::InlinedRepToCode(uintptr_t rep) {
assert(IsInlined(rep)); ABSL_ASSERT(IsInlined(rep));
return static_cast<absl::StatusCode>(rep >> 2); return static_cast<absl::StatusCode>(rep >> 2);
} }
inline status_internal::StatusRep* Status::RepToPointer(uintptr_t rep) { constexpr uintptr_t Status::MovedFromRep() {
return CodeToInlinedRep(absl::StatusCode::kInternal) | 2;
}
inline const status_internal::StatusRep* Status::RepToPointer(uintptr_t rep) {
assert(!IsInlined(rep)); assert(!IsInlined(rep));
return reinterpret_cast<status_internal::StatusRep*>(rep); return reinterpret_cast<const status_internal::StatusRep*>(rep);
} }
inline uintptr_t Status::PointerToRep(status_internal::StatusRep* rep) { inline uintptr_t Status::PointerToRep(status_internal::StatusRep* rep) {
...@@ -871,15 +904,11 @@ inline uintptr_t Status::PointerToRep(status_internal::StatusRep* rep) { ...@@ -871,15 +904,11 @@ inline uintptr_t Status::PointerToRep(status_internal::StatusRep* rep) {
} }
inline void Status::Ref(uintptr_t rep) { inline void Status::Ref(uintptr_t rep) {
if (!IsInlined(rep)) { if (!IsInlined(rep)) RepToPointer(rep)->Ref();
RepToPointer(rep)->ref.fetch_add(1, std::memory_order_relaxed);
}
} }
inline void Status::Unref(uintptr_t rep) { inline void Status::Unref(uintptr_t rep) {
if (!IsInlined(rep)) { if (!IsInlined(rep)) RepToPointer(rep)->Unref();
UnrefNonInlined(rep);
}
} }
inline Status OkStatus() { return Status(); } inline Status OkStatus() { return Status(); }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment