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
"random/uniform_real_distribution.h"
"random/zipf_distribution.h"
"status/internal/status_internal.h"
"status/internal/status_internal.cc"
"status/internal/statusor_internal.h"
"status/status.h"
"status/status.cc"
......
......@@ -31,6 +31,7 @@ licenses(["notice"])
cc_library(
name = "status",
srcs = [
"internal/status_internal.cc",
"internal/status_internal.h",
"status.cc",
"status_payload_printer.cc",
......
......@@ -20,6 +20,7 @@ absl_cc_library(
"status.h"
SRCS
"internal/status_internal.h"
"internal/status_internal.cc"
"status.cc"
"status_payload_printer.h"
"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 @@
#ifndef ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_
#define ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_
#include <atomic>
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/container/inlined_vector.h"
#include "absl/strings/cord.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#ifndef SWIG
// Disabled for SWIG as it doesn't parse attributes correctly.
......@@ -44,6 +49,7 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
enum class StatusCode : int;
enum class StatusToStringMode : int;
namespace status_internal {
......@@ -56,22 +62,54 @@ struct Payload {
using Payloads = absl::InlinedVector<Payload, 1>;
// Reference-counted representation of Status data.
struct StatusRep {
class StatusRep {
public:
StatusRep(absl::StatusCode code_arg, absl::string_view message_arg,
std::unique_ptr<status_internal::Payloads> payloads_arg)
: ref(int32_t{1}),
code(code_arg),
message(message_arg),
payloads(std::move(payloads_arg)) {}
std::atomic<int32_t> ref;
absl::StatusCode code;
: ref_(int32_t{1}),
code_(code_arg),
message_(message_arg),
payloads_(std::move(payloads_arg)) {}
absl::StatusCode code() const { return 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()
// is non-empty, then the resulting string_view is null terminated.
// This is required to implement 'StatusMessageAsCStr(...)'
std::string message;
std::unique_ptr<status_internal::Payloads> payloads;
std::string message_;
std::unique_ptr<status_internal::Payloads> payloads_;
};
absl::StatusCode MapToLocalCode(int value);
......
......@@ -22,7 +22,6 @@
#include <memory>
#include <ostream>
#include <string>
#include <utility>
#include "absl/base/attributes.h"
#include "absl/base/config.h"
......@@ -31,17 +30,12 @@
#include "absl/base/macros.h"
#include "absl/debugging/stacktrace.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/status_payload_printer.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"
#include "absl/types/span.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
......@@ -95,120 +89,6 @@ std::ostream& operator<<(std::ostream& os, StatusCode 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() {
static union EmptyString {
std::string str;
......@@ -226,17 +106,6 @@ const std::string* Status::MovedFromString() {
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)
: rep_(CodeToInlinedRep(code)) {
if (code != absl::StatusCode::kOk && !msg.empty()) {
......@@ -244,97 +113,19 @@ Status::Status(absl::StatusCode code, absl::string_view msg)
}
}
int Status::raw_code() const {
if (IsInlined(rep_)) {
return static_cast<int>(InlinedRepToCode(rep_));
}
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);
status_internal::StatusRep* Status::PrepareToModify(uintptr_t rep) {
if (IsInlined(rep)) {
return new status_internal::StatusRep(InlinedRepToCode(rep),
absl::string_view(), nullptr);
}
return RepToPointer(rep)->CloneAndUnref();
}
bool Status::EqualsSlow(const absl::Status& a, const absl::Status& b) {
if (IsInlined(a.rep_) != IsInlined(b.rep_)) return false;
if (a.message() != b.message()) return false;
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;
std::string Status::ToStringSlow(uintptr_t rep, StatusToStringMode mode) {
if (IsInlined(rep)) {
return absl::StrCat(absl::StatusCodeToString(InlinedRepToCode(rep)), ": ");
}
return true;
}
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;
return RepToPointer(rep)->ToString(mode);
}
std::ostream& operator<<(std::ostream& os, const Status& x) {
......@@ -623,17 +414,6 @@ Status ErrnoToStatus(int error_number, absl::string_view 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) {
// As an internal implementation detail, we guarantee that if status.message()
// is non-empty, then the resulting string_view is null terminated.
......
......@@ -59,10 +59,12 @@
#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/base/macros.h"
#include "absl/base/optimization.h"
#include "absl/functional/function_ref.h"
#include "absl/status/internal/status_internal.h"
#include "absl/strings/cord.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
......@@ -607,18 +609,15 @@ class Status final {
// code, and an empty error message.
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 Unref(uintptr_t rep);
// REQUIRES: !ok()
// Ensures rep_ is not shared with any other Status.
void PrepareToModify();
const status_internal::Payloads* GetPayloads() const;
status_internal::Payloads* GetPayloads();
static bool EqualsSlow(const absl::Status& a, const absl::Status& b);
// Ensures rep is not inlined or shared with any other Status.
static status_internal::StatusRep* PrepareToModify(uintptr_t rep);
// MSVC 14.0 limitation requires the const.
static constexpr const char kMovedFromString[] =
......@@ -629,24 +628,24 @@ class Status final {
// Returns whether rep contains an inlined representation.
// 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_
// for details.
static bool IsMovedFrom(uintptr_t rep);
static uintptr_t MovedFromRep();
static constexpr bool IsMovedFrom(uintptr_t rep);
static constexpr uintptr_t MovedFromRep();
// Convert between error::Code and the inlined uintptr_t representation used
// by rep_. See rep_ for details.
static uintptr_t CodeToInlinedRep(absl::StatusCode code);
static absl::StatusCode InlinedRepToCode(uintptr_t rep);
static constexpr uintptr_t CodeToInlinedRep(absl::StatusCode code);
static constexpr absl::StatusCode InlinedRepToCode(uintptr_t rep);
// Converts between StatusRep* and the external uintptr_t representation used
// by rep_. See rep_ for details.
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.
// - When the low bit is set it is an inlined representation.
......@@ -657,6 +656,8 @@ class Status final {
// In this case all the data comes from a heap allocated Rep object.
// rep_ is a status_internal::StatusRep* pointer to that structure.
uintptr_t rep_;
friend class status_internal::StatusRep;
};
// OkStatus()
......@@ -760,11 +761,11 @@ Status ErrnoToStatus(int error_number, absl::string_view message);
// 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) {
uintptr_t old_rep = rep_;
......@@ -776,7 +777,7 @@ inline Status& Status::operator=(const Status& x) {
return *this;
}
inline Status::Status(Status&& x) noexcept : rep_(x.rep_) {
inline Status::Status(Status&& x) noexcept : Status(x.rep_) {
x.rep_ = MovedFromRep();
}
......@@ -808,15 +809,27 @@ inline bool Status::ok() const {
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 {
return !IsInlined(rep_)
? RepToPointer(rep_)->message
? RepToPointer(rep_)->message()
: (IsMovedFrom(rep_) ? absl::string_view(kMovedFromString)
: absl::string_view());
}
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) {
......@@ -824,7 +837,7 @@ inline bool operator!=(const Status& lhs, const Status& rhs) {
}
inline std::string Status::ToString(StatusToStringMode mode) const {
return ok() ? "OK" : ToStringSlow(mode);
return ok() ? "OK" : ToStringSlow(rep_, mode);
}
inline void Status::IgnoreError() const {
......@@ -836,34 +849,54 @@ inline void swap(absl::Status& a, absl::Status& b) {
swap(a.rep_, b.rep_);
}
inline const status_internal::Payloads* Status::GetPayloads() const {
return IsInlined(rep_) ? nullptr : RepToPointer(rep_)->payloads.get();
inline absl::optional<absl::Cord> Status::GetPayload(
absl::string_view type_url) const {
if (IsInlined(rep_)) return absl::nullopt;
return RepToPointer(rep_)->GetPayload(type_url);
}
inline status_internal::Payloads* Status::GetPayloads() {
return IsInlined(rep_) ? nullptr : RepToPointer(rep_)->payloads.get();
inline void Status::SetPayload(absl::string_view type_url, absl::Cord payload) {
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::IsMovedFrom(uintptr_t rep) { return (rep & 2) != 0; }
inline bool Status::ErasePayload(absl::string_view type_url) {
if (IsInlined(rep_)) return false;
status_internal::StatusRep* rep = PrepareToModify(rep_);
auto res = rep->ErasePayload(type_url);
rep_ = res.new_rep;
return res.erased;
}
inline uintptr_t Status::MovedFromRep() {
return CodeToInlinedRep(absl::StatusCode::kInternal) | 2;
inline void Status::ForEachPayload(
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;
}
inline absl::StatusCode Status::InlinedRepToCode(uintptr_t rep) {
assert(IsInlined(rep));
constexpr absl::StatusCode Status::InlinedRepToCode(uintptr_t rep) {
ABSL_ASSERT(IsInlined(rep));
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));
return reinterpret_cast<status_internal::StatusRep*>(rep);
return reinterpret_cast<const 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) {
}
inline void Status::Ref(uintptr_t rep) {
if (!IsInlined(rep)) {
RepToPointer(rep)->ref.fetch_add(1, std::memory_order_relaxed);
}
if (!IsInlined(rep)) RepToPointer(rep)->Ref();
}
inline void Status::Unref(uintptr_t rep) {
if (!IsInlined(rep)) {
UnrefNonInlined(rep);
}
if (!IsInlined(rep)) RepToPointer(rep)->Unref();
}
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