summaryrefslogtreecommitdiff
path: root/absl/status
diff options
context:
space:
mode:
authorGravatar Abseil Team <absl-team@google.com>2023-09-17 22:03:34 -0700
committerGravatar Copybara-Service <copybara-worker@google.com>2023-09-17 22:04:27 -0700
commit243b7d386a15b88dfa32eeadabeb3ddc396a37f4 (patch)
tree66c563d80cf164b8cd31d8682ab013d0d840ce67 /absl/status
parent2c1e7e3c5c71891735f21302fb339e0d6edcd94c (diff)
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
Diffstat (limited to 'absl/status')
-rw-r--r--absl/status/BUILD.bazel1
-rw-r--r--absl/status/CMakeLists.txt1
-rw-r--r--absl/status/internal/status_internal.cc246
-rw-r--r--absl/status/internal/status_internal.h58
-rw-r--r--absl/status/status.cc238
-rw-r--r--absl/status/status.h113
6 files changed, 376 insertions, 281 deletions
diff --git a/absl/status/BUILD.bazel b/absl/status/BUILD.bazel
index 6a6fb299..5be5c218 100644
--- a/absl/status/BUILD.bazel
+++ b/absl/status/BUILD.bazel
@@ -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",
diff --git a/absl/status/CMakeLists.txt b/absl/status/CMakeLists.txt
index ed2e3848..f369e341 100644
--- a/absl/status/CMakeLists.txt
+++ b/absl/status/CMakeLists.txt
@@ -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"
diff --git a/absl/status/internal/status_internal.cc b/absl/status/internal/status_internal.cc
new file mode 100644
index 00000000..2307579b
--- /dev/null
+++ b/absl/status/internal/status_internal.cc
@@ -0,0 +1,246 @@
+// 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
diff --git a/absl/status/internal/status_internal.h b/absl/status/internal/status_internal.h
index f22c611d..a445ce37 100644
--- a/absl/status/internal/status_internal.h
+++ b/absl/status/internal/status_internal.h
@@ -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);
diff --git a/absl/status/status.cc b/absl/status/status.cc
index 911f4b28..7eb5d66a 100644
--- a/absl/status/status.cc
+++ b/absl/status/status.cc
@@ -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.
diff --git a/absl/status/status.h b/absl/status/status.h
index 2dac2fea..2f03bb64 100644
--- a/absl/status/status.h
+++ b/absl/status/status.h
@@ -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(); }