Skip to content

Commit d53e446

Browse files
committed
add Status data structure
Adpated from Apache Arrow with lot of simplification. Signed-off-by: Junwang Zhao <[email protected]>
1 parent c8d8af9 commit d53e446

File tree

8 files changed

+668
-2
lines changed

8 files changed

+668
-2
lines changed

src/iceberg/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# specific language governing permissions and limitations
1616
# under the License.
1717

18-
set(ICEBERG_SOURCES demo_table.cc)
18+
set(ICEBERG_SOURCES demo_table.cc status.cc)
1919

2020
add_iceberg_lib(iceberg
2121
SOURCES

src/iceberg/status.cc

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#include "iceberg/status.h"
21+
22+
#include <cassert>
23+
#include <cstdlib>
24+
#include <iostream>
25+
#include <sstream>
26+
27+
namespace iceberg {
28+
29+
Status::Status(StatusCode code, const std::string& msg)
30+
: Status::Status(code, msg, nullptr) {}
31+
32+
Status::Status(StatusCode code, std::string msg, std::shared_ptr<StatusDetail> detail) {
33+
state_ = new State{code, std::move(msg), std::move(detail)};
34+
}
35+
36+
void Status::CopyFrom(const Status& s) {
37+
if (ICEBERG_PREDICT_FALSE(state_ != nullptr)) {
38+
DeleteState();
39+
}
40+
if (s.state_ == nullptr) {
41+
state_ = nullptr;
42+
} else {
43+
state_ = new State(*s.state_);
44+
}
45+
}
46+
47+
std::string Status::CodeAsString() const {
48+
if (state_ == nullptr) {
49+
return "OK";
50+
}
51+
return CodeAsString(code());
52+
}
53+
54+
std::string Status::CodeAsString(StatusCode code) {
55+
const char* type;
56+
switch (code) {
57+
case StatusCode::OK:
58+
type = "OK";
59+
break;
60+
case StatusCode::IOError:
61+
type = "IOError";
62+
break;
63+
case StatusCode::NotImplemented:
64+
type = "NotImplemented";
65+
break;
66+
case StatusCode::UnknownError:
67+
type = "Unknown error";
68+
break;
69+
default:
70+
type = "Unknown";
71+
break;
72+
}
73+
return std::string(type);
74+
}
75+
76+
std::string Status::ToString() const {
77+
std::ostringstream oss;
78+
oss << CodeAsString();
79+
if (state_ == nullptr) {
80+
return oss.str();
81+
}
82+
oss << ": ";
83+
oss << state_->msg;
84+
if (state_->detail != nullptr) {
85+
oss << ". Detail: " << state_->detail->ToString();
86+
}
87+
88+
return oss.str();
89+
}
90+
91+
void Status::Warn() const { std::cerr << *this; }
92+
93+
void Status::Warn(const std::string& message) const {
94+
std::cerr << message << ": " << *this;
95+
}
96+
97+
} // namespace iceberg

src/iceberg/status.h

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#pragma once
21+
22+
#include <cstring>
23+
#include <format>
24+
#include <iosfwd>
25+
#include <memory>
26+
#include <string>
27+
#include <utility>
28+
29+
#include "util/compare.h"
30+
#include "util/macros.h"
31+
#include "util/to_string_ostreamable.h"
32+
33+
namespace iceberg {
34+
35+
enum class StatusCode : char {
36+
OK = 0,
37+
IOError = 1,
38+
NotImplemented = 2,
39+
UnknownError = 127
40+
};
41+
42+
/// \brief An opaque class that allows subsystems to retain
43+
/// additional information inside the Status.
44+
class StatusDetail {
45+
public:
46+
virtual ~StatusDetail() = default;
47+
/// \brief Return a unique id for the type of the StatusDetail
48+
/// (effectively a poor man's substitute for RTTI).
49+
virtual const char* type_id() const = 0;
50+
/// \brief Produce a human-readable description of this status.
51+
virtual std::string ToString() const = 0;
52+
53+
bool operator==(const StatusDetail& other) const noexcept {
54+
return std::string(type_id()) == other.type_id() && ToString() == other.ToString();
55+
}
56+
};
57+
58+
/// \brief Status outcome object (success or error)
59+
///
60+
/// The Status object is an object holding the outcome of an operation.
61+
/// The outcome is represented as a StatusCode, either success
62+
/// (StatusCode::OK) or an error (any other of the StatusCode enumeration values).
63+
///
64+
/// Additionally, if an error occurred, a specific error message is generally
65+
/// attached.
66+
class [[nodiscard]] Status : public util::EqualityComparable<Status>,
67+
public util::ToStringOstreamable<Status> {
68+
public:
69+
// Create a success status.
70+
constexpr Status() noexcept : state_(nullptr) {}
71+
~Status() noexcept {
72+
if (ICEBERG_PREDICT_FALSE(state_ != nullptr)) {
73+
DeleteState();
74+
}
75+
}
76+
77+
Status(StatusCode code, const std::string& msg);
78+
/// \brief Pluggable constructor for use by sub-systems. detail cannot be null.
79+
Status(StatusCode code, std::string msg, std::shared_ptr<StatusDetail> detail);
80+
81+
// Copy the specified status.
82+
inline Status(const Status& s);
83+
inline Status& operator=(const Status& s);
84+
85+
// Move the specified status.
86+
inline Status(Status&& s) noexcept;
87+
inline Status& operator=(Status&& s) noexcept;
88+
89+
inline bool Equals(const Status& other) const;
90+
91+
// AND the statuses.
92+
inline Status operator&(const Status& s) const noexcept;
93+
inline Status operator&(Status&& s) const noexcept;
94+
inline Status& operator&=(const Status& s) noexcept;
95+
inline Status& operator&=(Status&& s) noexcept;
96+
97+
/// Return a success status.
98+
static Status OK() { return Status(); }
99+
100+
template <typename... Args>
101+
static Status FromArgs(StatusCode code, std::string_view fmt, Args&&... args) {
102+
return Status(code, std::vformat(fmt, std::make_format_args(args...)));
103+
}
104+
105+
template <typename... Args>
106+
static Status FromDetailAndArgs(StatusCode code, std::shared_ptr<StatusDetail> detail,
107+
std::string_view fmt, Args&&... args) {
108+
return Status(code, std::vformat(fmt, std::make_format_args(args...)),
109+
std::move(detail));
110+
}
111+
112+
/// Return an error status when some IO-related operation failed
113+
template <typename... Args>
114+
static Status IOError(Args&&... args) {
115+
return Status::FromArgs(StatusCode::IOError, std::forward<Args>(args)...);
116+
}
117+
118+
/// Return an error status when an operation or a combination of operation and
119+
/// data types is unimplemented
120+
template <typename... Args>
121+
static Status NotImplemented(Args&&... args) {
122+
return Status::FromArgs(StatusCode::NotImplemented, std::forward<Args>(args)...);
123+
}
124+
125+
/// Return an error status for unknown errors
126+
template <typename... Args>
127+
static Status UnknownError(Args&&... args) {
128+
return Status::FromArgs(StatusCode::UnknownError, std::forward<Args>(args)...);
129+
}
130+
131+
/// Return true iff the status indicates success.
132+
constexpr bool ok() const { return (state_ == nullptr); }
133+
134+
/// Return true iff the status indicates an IO-related failure.
135+
constexpr bool IsIOError() const { return code() == StatusCode::IOError; }
136+
/// Return true iff the status indicates an unimplemented operation.
137+
constexpr bool IsNotImplemented() const { return code() == StatusCode::NotImplemented; }
138+
/// Return true iff the status indicates an unknown error.
139+
constexpr bool IsUnknownError() const { return code() == StatusCode::UnknownError; }
140+
141+
/// \brief Return a string representation of this status suitable for printing.
142+
///
143+
/// The string "OK" is returned for success.
144+
std::string ToString() const;
145+
146+
/// \brief Return a string representation of the status code, without the message
147+
/// text or POSIX code information.
148+
std::string CodeAsString() const;
149+
static std::string CodeAsString(StatusCode);
150+
151+
/// \brief Return the StatusCode value attached to this status.
152+
constexpr StatusCode code() const { return ok() ? StatusCode::OK : state_->code; }
153+
154+
/// \brief Return the specific error message attached to this status.
155+
const std::string& message() const {
156+
static const std::string no_message = "";
157+
return ok() ? no_message : state_->msg;
158+
}
159+
160+
/// \brief Return the status detail attached to this message.
161+
const std::shared_ptr<StatusDetail>& detail() const {
162+
static std::shared_ptr<StatusDetail> no_detail = nullptr;
163+
return state_ ? state_->detail : no_detail;
164+
}
165+
166+
/// \brief Return a new Status copying the existing status, but
167+
/// updating with the existing detail.
168+
Status WithDetail(std::shared_ptr<StatusDetail> new_detail) const {
169+
return Status(code(), message(), std::move(new_detail));
170+
}
171+
172+
/// \brief Return a new Status with changed message, copying the
173+
/// existing status code and detail.
174+
template <typename... Args>
175+
Status WithMessage(Args&&... args) const {
176+
return FromArgs(code(), std::forward<Args>(args)...).WithDetail(detail());
177+
}
178+
179+
void Warn() const;
180+
void Warn(const std::string& message) const;
181+
182+
private:
183+
struct State {
184+
StatusCode code;
185+
std::string msg;
186+
std::shared_ptr<StatusDetail> detail;
187+
};
188+
// OK status has a `NULL` state_. Otherwise, `state_` points to
189+
// a `State` structure containing the error code and message(s)
190+
State* state_;
191+
192+
void DeleteState() noexcept {
193+
// On certain compilers, splitting off the slow path improves performance
194+
// significantly.
195+
delete state_;
196+
state_ = nullptr;
197+
}
198+
199+
void CopyFrom(const Status& s);
200+
inline void MoveFrom(Status& s);
201+
};
202+
203+
void Status::MoveFrom(Status& s) {
204+
if (ICEBERG_PREDICT_FALSE(state_ != nullptr)) {
205+
DeleteState();
206+
}
207+
state_ = s.state_;
208+
s.state_ = nullptr;
209+
}
210+
211+
Status::Status(const Status& s) : state_{nullptr} { CopyFrom(s); }
212+
213+
Status& Status::operator=(const Status& s) {
214+
// The following condition catches both aliasing (when this == &s),
215+
// and the common case where both s and *this are ok.
216+
if (state_ != s.state_) {
217+
CopyFrom(s);
218+
}
219+
return *this;
220+
}
221+
222+
Status::Status(Status&& s) noexcept : state_(s.state_) { s.state_ = nullptr; }
223+
224+
Status& Status::operator=(Status&& s) noexcept {
225+
MoveFrom(s);
226+
return *this;
227+
}
228+
229+
bool Status::Equals(const Status& s) const {
230+
if (state_ == s.state_) {
231+
return true;
232+
}
233+
234+
if (ok() || s.ok()) {
235+
return false;
236+
}
237+
238+
if (detail() != s.detail()) {
239+
if ((detail() && !s.detail()) || (!detail() && s.detail())) {
240+
return false;
241+
}
242+
return *detail() == *s.detail();
243+
}
244+
245+
return code() == s.code() && message() == s.message();
246+
}
247+
248+
Status Status::operator&(const Status& s) const noexcept {
249+
if (ok()) {
250+
return s;
251+
} else {
252+
return *this;
253+
}
254+
}
255+
256+
Status Status::operator&(Status&& s) const noexcept {
257+
if (ok()) {
258+
return std::move(s);
259+
} else {
260+
return *this;
261+
}
262+
}
263+
264+
Status& Status::operator&=(const Status& s) noexcept {
265+
if (ok() && !s.ok()) {
266+
CopyFrom(s);
267+
}
268+
return *this;
269+
}
270+
271+
Status& Status::operator&=(Status&& s) noexcept {
272+
if (ok() && !s.ok()) {
273+
MoveFrom(s);
274+
}
275+
return *this;
276+
}
277+
278+
} // namespace iceberg

0 commit comments

Comments
 (0)