src/common/utils/Status.h (137 lines of code) (raw):
#pragma once
#include <any>
#include <common/utils/StatusCode.h>
#include <common/utils/String.h>
#include <cstdint>
#include <fmt/format.h>
#include <memory>
#include <string_view>
#include "folly/Portability.h"
namespace hf3fs {
#if !FOLLY_X64 && !FOLLY_AARCH64
#error "The platform must be 64bit!"
#endif
static_assert(std::endian::native == std::endian::little);
// `Status` imitates `abseil::Status` which contains:
// - code
// - (optional) message
// - (optional) payload of any type
class [[nodiscard]] Status {
struct status_ok_t {};
public:
Status() = delete;
explicit Status(status_code_t code)
: data_(construct(code, nullptr)) {}
Status(const Status &other) { *this = other; }
Status(Status &&other) = default;
constexpr static status_ok_t OK{};
/* implicit */ Status(status_ok_t)
: Status(StatusCode::kOK) {}
Status(status_code_t code, std::string_view msg) {
auto rep = std::make_unique<StatusRep>();
rep->message = msg;
data_ = construct(code, std::move(rep));
}
Status(status_code_t code, std::string &&msg) {
auto rep = std::make_unique<StatusRep>();
rep->message = std::move(msg);
data_ = construct(code, std::move(rep));
}
Status(status_code_t code, const char *msg)
: Status(code, std::string_view(msg)) {}
template <typename T>
Status(status_code_t code, std::string_view msg, T &&payload)
: Status(code, msg) {
setPayload(std::forward<T>(payload));
}
Status &operator=(const Status &other) {
if (std::addressof(other) != this) {
data_ = construct(other.code(), other.rep() ? std::make_unique<StatusRep>(*other.rep()) : nullptr);
}
return *this;
}
Status &operator=(Status &&other) = default;
status_code_t code() const { return reinterpret_cast<uintptr_t>(data_.get()) >> kPtrBits; }
std::string_view message() const { return rep() ? std::string_view(rep()->message) : std::string_view(); }
Status convert(status_code_t code) const {
Status status = Status::OK;
status.data_ = construct(code, rep() ? std::make_unique<StatusRep>(*rep()) : nullptr);
return status;
}
String describe() const {
return rep() ? fmt::format("{}({}) {}", StatusCode::toString(code()), code(), rep()->message)
: fmt::format("{}({})", StatusCode::toString(code()), code());
}
std::ostream &operator<<(std::ostream &os) const { return os << describe(); }
bool isOK() const { return code() == StatusCode::kOK; }
explicit operator bool() const { return isOK(); }
bool hasPayload() const { return rep() && rep()->payload.has_value(); }
template <typename T>
T *payload() {
return std::any_cast<T>(&rep()->payload);
}
template <typename T>
const T *payload() const {
return std::any_cast<const T>(&rep()->payload);
}
template <typename T>
void setPayload(T &&payload) {
ensuredRep()->payload = std::forward<T>(payload);
}
template <typename T, typename... Args>
void emplacePayload(Args &&...args) {
ensuredRep()->payload.emplace<T>(std::forward<Args>(args)...);
}
void resetPayload() { rep() ? rep()->payload.reset() : void(); }
private:
static_assert(StatusCode::kOK == 0, "StatusCode::kOK must be 0!");
static_assert(sizeof(status_code_t) == 2, "The width of status_code_t must be 16b");
static constexpr auto kPtrBits = 48u;
static constexpr auto kPtrMask = ((1ul << kPtrBits) - 1);
struct StatusRep {
String message;
std::any payload;
};
struct StatusRepDeleter {
void operator()(StatusRep *rep) { delete extractPtr(rep); }
};
using StatusPtr = std::unique_ptr<StatusRep, StatusRepDeleter>;
static StatusPtr construct(status_code_t code, std::unique_ptr<StatusRep> rep) {
return StatusPtr(
reinterpret_cast<StatusRep *>(reinterpret_cast<uintptr_t>(rep.release()) | (uintptr_t(code) << kPtrBits)));
}
static StatusRep *extractPtr(StatusRep *rep) {
return reinterpret_cast<StatusRep *>(reinterpret_cast<uintptr_t>(rep) & kPtrMask);
}
StatusRep *rep() { return extractPtr(data_.get()); }
const StatusRep *rep() const { return extractPtr(data_.get()); }
StatusRep *ensuredRep() {
if (rep() == nullptr) {
data_ = construct(code(), std::make_unique<StatusRep>());
}
return rep();
}
private:
StatusPtr data_; // |<-- low 48 bits: rep ptr -->|<-- high 16 bits: status code -->|
};
class StatusException : public std::runtime_error {
public:
using std::runtime_error::runtime_error;
explicit StatusException(Status status)
: std::runtime_error(status.describe()),
status_(std::move(status)) {}
const Status &get() const { return status_; }
Status &get() { return status_; }
private:
Status status_;
};
} // namespace hf3fs
FMT_BEGIN_NAMESPACE
template <>
struct formatter<hf3fs::Status> : formatter<hf3fs::status_code_t> {
template <typename FormatContext>
auto format(const hf3fs::Status &status, FormatContext &ctx) const {
auto msg = status.message();
if (msg.empty()) {
return fmt::format_to(ctx.out(), "{}({})", hf3fs::StatusCode::toString(status.code()), status.code());
}
return fmt::format_to(ctx.out(), "{}({}) {}", hf3fs::StatusCode::toString(status.code()), status.code(), msg);
}
};
FMT_END_NAMESPACE