1 //
2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 #pragma once
8
9 #include "td/utils/common.h"
10 #include "td/utils/logging.h"
11 #include "td/utils/ScopeGuard.h"
12 #include "td/utils/Slice.h"
13 #include "td/utils/StackAllocator.h"
14 #include "td/utils/StringBuilder.h"
15
16 #include <cerrno>
17 #include <cstring>
18 #include <memory>
19 #include <new>
20 #include <type_traits>
21 #include <utility>
22
23 #define TRY_STATUS(status) \
24 { \
25 auto try_status = (status); \
26 if (try_status.is_error()) { \
27 return try_status.move_as_error(); \
28 } \
29 }
30
31 #define TRY_STATUS_PREFIX(status, prefix) \
32 { \
33 auto try_status = (status); \
34 if (try_status.is_error()) { \
35 return try_status.move_as_error_prefix(prefix); \
36 } \
37 }
38
39 #define TRY_STATUS_PROMISE(promise_name, status) \
40 { \
41 auto try_status = (status); \
42 if (try_status.is_error()) { \
43 promise_name.set_error(try_status.move_as_error()); \
44 return; \
45 } \
46 }
47
48 #define TRY_STATUS_PROMISE_PREFIX(promise_name, status, prefix) \
49 { \
50 auto try_status = (status); \
51 if (try_status.is_error()) { \
52 promise_name.set_error(try_status.move_as_error_prefix(prefix)); \
53 return; \
54 } \
55 }
56
57 #define TRY_RESULT(name, result) TRY_RESULT_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result)
58
59 #define TRY_RESULT_PROMISE(promise_name, name, result) \
60 TRY_RESULT_PROMISE_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result)
61
62 #define TRY_RESULT_ASSIGN(name, result) TRY_RESULT_IMPL(TD_CONCAT(r_response, __LINE__), name, result)
63
64 #define TRY_RESULT_PROMISE_ASSIGN(promise_name, name, result) \
65 TRY_RESULT_PROMISE_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result)
66
67 #define TRY_RESULT_PREFIX(name, result, prefix) \
68 TRY_RESULT_PREFIX_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result, prefix)
69
70 #define TRY_RESULT_PREFIX_ASSIGN(name, result, prefix) \
71 TRY_RESULT_PREFIX_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result, prefix)
72
73 #define TRY_RESULT_PROMISE_PREFIX(promise_name, name, result, prefix) \
74 TRY_RESULT_PROMISE_PREFIX_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result, prefix)
75
76 #define TRY_RESULT_PROMISE_PREFIX_ASSIGN(promise_name, name, result, prefix) \
77 TRY_RESULT_PROMISE_PREFIX_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result, prefix)
78
79 #define TRY_RESULT_IMPL(r_name, name, result) \
80 auto r_name = (result); \
81 if (r_name.is_error()) { \
82 return r_name.move_as_error(); \
83 } \
84 name = r_name.move_as_ok();
85
86 #define TRY_RESULT_PREFIX_IMPL(r_name, name, result, prefix) \
87 auto r_name = (result); \
88 if (r_name.is_error()) { \
89 return r_name.move_as_error_prefix(prefix); \
90 } \
91 name = r_name.move_as_ok();
92
93 #define TRY_RESULT_PROMISE_IMPL(promise_name, r_name, name, result) \
94 auto r_name = (result); \
95 if (r_name.is_error()) { \
96 promise_name.set_error(r_name.move_as_error()); \
97 return; \
98 } \
99 name = r_name.move_as_ok();
100
101 #define TRY_RESULT_PROMISE_PREFIX_IMPL(promise_name, r_name, name, result, prefix) \
102 auto r_name = (result); \
103 if (r_name.is_error()) { \
104 promise_name.set_error(r_name.move_as_error_prefix(prefix)); \
105 return; \
106 } \
107 name = r_name.move_as_ok();
108
109 #define LOG_STATUS(status) \
110 { \
111 auto log_status = (status); \
112 if (log_status.is_error()) { \
113 LOG(ERROR) << log_status.move_as_error(); \
114 } \
115 }
116
117 #ifndef TD_STATUS_NO_ENSURE
118 #define ensure() ensure_impl(__FILE__, __LINE__)
119 #define ensure_error() ensure_error_impl(__FILE__, __LINE__)
120 #endif
121
122 #if TD_PORT_POSIX
123 #define OS_ERROR(message) \
124 [&] { \
125 auto saved_errno = errno; \
126 return ::td::Status::PosixError(saved_errno, (message)); \
127 }()
128 #define OS_SOCKET_ERROR(message) OS_ERROR(message)
129 #elif TD_PORT_WINDOWS
130 #define OS_ERROR(message) \
131 [&] { \
132 auto saved_error = ::GetLastError(); \
133 return ::td::Status::WindowsError(saved_error, (message)); \
134 }()
135 #define OS_SOCKET_ERROR(message) \
136 [&] { \
137 auto saved_error = ::WSAGetLastError(); \
138 return ::td::Status::WindowsError(saved_error, (message)); \
139 }()
140 #endif
141
142 namespace td {
143
144 #if TD_PORT_POSIX
145 CSlice strerror_safe(int code);
146 #endif
147
148 #if TD_PORT_WINDOWS
149 string winerror_to_string(int code);
150 #endif
151
152 class Status {
153 enum class ErrorType : int8 { General, Os };
154
155 public:
156 Status() = default;
157
158 bool operator==(const Status &other) const {
159 return ptr_ == other.ptr_;
160 }
161
clone()162 Status clone() const TD_WARN_UNUSED_RESULT {
163 if (is_ok()) {
164 return Status();
165 }
166 auto info = get_info();
167 if (info.static_flag) {
168 return clone_static();
169 }
170 return Status(false, info.error_type, info.error_code, message());
171 }
172
OK()173 static Status OK() TD_WARN_UNUSED_RESULT {
174 return Status();
175 }
176
177 static Status Error(int err, Slice message = Slice()) TD_WARN_UNUSED_RESULT {
178 return Status(false, ErrorType::General, err, message);
179 }
180
Error(Slice message)181 static Status Error(Slice message) TD_WARN_UNUSED_RESULT {
182 return Error(0, message);
183 }
184
185 #if TD_PORT_WINDOWS
WindowsError(int saved_error,Slice message)186 static Status WindowsError(int saved_error, Slice message) TD_WARN_UNUSED_RESULT {
187 return Status(false, ErrorType::Os, saved_error, message);
188 }
189 #endif
190
191 #if TD_PORT_POSIX
PosixError(int32 saved_errno,Slice message)192 static Status PosixError(int32 saved_errno, Slice message) TD_WARN_UNUSED_RESULT {
193 return Status(false, ErrorType::Os, saved_errno, message);
194 }
195 #endif
196
Error()197 static Status Error() TD_WARN_UNUSED_RESULT {
198 return Error<0>();
199 }
200
201 template <int Code>
Error()202 static Status Error() {
203 static Status status(true, ErrorType::General, Code, "");
204 return status.clone_static();
205 }
206
print(StringBuilder & sb)207 StringBuilder &print(StringBuilder &sb) const {
208 if (is_ok()) {
209 return sb << "OK";
210 }
211 Info info = get_info();
212 switch (info.error_type) {
213 case ErrorType::General:
214 sb << "[Error";
215 break;
216 case ErrorType::Os:
217 #if TD_PORT_POSIX
218 sb << "[PosixError : " << strerror_safe(info.error_code);
219 #elif TD_PORT_WINDOWS
220 sb << "[WindowsError : " << winerror_to_string(info.error_code);
221 #endif
222 break;
223 default:
224 UNREACHABLE();
225 break;
226 }
227 sb << " : " << code() << " : " << message() << "]";
228 return sb;
229 }
230
to_string()231 string to_string() const {
232 auto buf = StackAllocator::alloc(4096);
233 StringBuilder sb(buf.as_slice());
234 print(sb);
235 return sb.as_cslice().str();
236 }
237
238 // Default interface
is_ok()239 bool is_ok() const TD_WARN_UNUSED_RESULT {
240 return !is_error();
241 }
242
is_error()243 bool is_error() const TD_WARN_UNUSED_RESULT {
244 return ptr_ != nullptr;
245 }
246
247 #ifdef TD_STATUS_NO_ENSURE
ensure()248 void ensure() const {
249 if (!is_ok()) {
250 LOG(FATAL) << "Unexpected Status " << to_string();
251 }
252 }
ensure_error()253 void ensure_error() const {
254 if (is_ok()) {
255 LOG(FATAL) << "Unexpected Status::OK";
256 }
257 }
258 #else
ensure_impl(CSlice file_name,int line)259 void ensure_impl(CSlice file_name, int line) const {
260 if (!is_ok()) {
261 LOG(FATAL) << "Unexpected Status " << to_string() << " in file " << file_name << " at line " << line;
262 }
263 }
ensure_error_impl(CSlice file_name,int line)264 void ensure_error_impl(CSlice file_name, int line) const {
265 if (is_ok()) {
266 LOG(FATAL) << "Unexpected Status::OK in file " << file_name << " at line " << line;
267 }
268 }
269 #endif
270
ignore()271 void ignore() const {
272 // nop
273 }
274
code()275 int32 code() const {
276 if (is_ok()) {
277 return 0;
278 }
279 return get_info().error_code;
280 }
281
message()282 CSlice message() const {
283 if (is_ok()) {
284 return CSlice("OK");
285 }
286 return CSlice(ptr_.get() + sizeof(Info));
287 }
288
public_message()289 string public_message() const {
290 if (is_ok()) {
291 return "OK";
292 }
293 Info info = get_info();
294 switch (info.error_type) {
295 case ErrorType::General:
296 return message().str();
297 case ErrorType::Os:
298 #if TD_PORT_POSIX
299 return strerror_safe(info.error_code).str();
300 #elif TD_PORT_WINDOWS
301 return winerror_to_string(info.error_code);
302 #endif
303 default:
304 UNREACHABLE();
305 return "";
306 }
307 }
308
error()309 const Status &error() const {
310 return *this;
311 }
312
move()313 Status move() TD_WARN_UNUSED_RESULT {
314 return std::move(*this);
315 }
316
move_as_error()317 Status move_as_error() TD_WARN_UNUSED_RESULT {
318 return std::move(*this);
319 }
320
321 Status move_as_ok() = delete;
322
move_as_error_prefix(const Status & status)323 Status move_as_error_prefix(const Status &status) const TD_WARN_UNUSED_RESULT {
324 return status.move_as_error_suffix(message());
325 }
326
327 Status move_as_error_prefix(Slice prefix) const TD_WARN_UNUSED_RESULT;
328
329 Status move_as_error_suffix(Slice suffix) const TD_WARN_UNUSED_RESULT;
330
331 private:
332 struct Info {
333 bool static_flag : 1;
334 signed int error_code : 23;
335 ErrorType error_type;
336 };
337
338 struct Deleter {
operatorDeleter339 void operator()(char *ptr) {
340 if (!get_info(ptr).static_flag) {
341 delete[] ptr;
342 }
343 }
344 };
345 std::unique_ptr<char[], Deleter> ptr_;
346
Status(Info info,Slice message)347 Status(Info info, Slice message) {
348 size_t size = sizeof(Info) + message.size() + 1;
349 ptr_ = std::unique_ptr<char[], Deleter>(new char[size]);
350 char *ptr = ptr_.get();
351 reinterpret_cast<Info *>(ptr)[0] = info;
352 ptr += sizeof(Info);
353 std::memcpy(ptr, message.begin(), message.size());
354 ptr += message.size();
355 *ptr = 0;
356 }
357
Status(bool static_flag,ErrorType error_type,int error_code,Slice message)358 Status(bool static_flag, ErrorType error_type, int error_code, Slice message)
359 : Status(to_info(static_flag, error_type, error_code), message) {
360 if (static_flag) {
361 TD_LSAN_IGNORE(ptr_.get());
362 }
363 }
364
clone_static()365 Status clone_static() const TD_WARN_UNUSED_RESULT {
366 CHECK(ptr_ != nullptr && get_info().static_flag);
367 Status result;
368 result.ptr_ = std::unique_ptr<char[], Deleter>(ptr_.get());
369 return result;
370 }
371
to_info(bool static_flag,ErrorType error_type,int error_code)372 static Info to_info(bool static_flag, ErrorType error_type, int error_code) {
373 const int MIN_ERROR_CODE = -(1 << 22) + 1;
374 const int MAX_ERROR_CODE = (1 << 22) - 1;
375 Info tmp;
376 tmp.static_flag = static_flag;
377 tmp.error_type = error_type;
378
379 if (error_code < MIN_ERROR_CODE) {
380 LOG(ERROR) << "Error code value is altered from " << error_code;
381 error_code = MIN_ERROR_CODE;
382 }
383 if (error_code > MAX_ERROR_CODE) {
384 LOG(ERROR) << "Error code value is altered from " << error_code;
385 error_code = MAX_ERROR_CODE;
386 }
387
388 #if TD_GCC
389 #pragma GCC diagnostic push
390 #pragma GCC diagnostic ignored "-Wconversion"
391 #endif
392 tmp.error_code = error_code;
393 #if TD_GCC
394 #pragma GCC diagnostic pop
395 #endif
396 CHECK(error_code == tmp.error_code);
397 return tmp;
398 }
399
get_info()400 Info get_info() const {
401 return get_info(ptr_.get());
402 }
get_info(char * ptr)403 static Info get_info(char *ptr) {
404 return reinterpret_cast<Info *>(ptr)[0];
405 }
406 };
407
408 template <class T = Unit>
409 class Result {
410 public:
411 using ValueT = T;
Result()412 Result() : status_(Status::Error<-1>()) {
413 }
414 template <class S, std::enable_if_t<!std::is_same<std::decay_t<S>, Result>::value, int> = 0>
Result(S && x)415 Result(S &&x) : status_(), value_(std::forward<S>(x)) {
416 }
417 struct emplace_t {};
418 template <class... ArgsT>
Result(emplace_t,ArgsT &&...args)419 Result(emplace_t, ArgsT &&... args) : status_(), value_(std::forward<ArgsT>(args)...) {
420 }
Result(Status && status)421 Result(Status &&status) : status_(std::move(status)) {
422 CHECK(status_.is_error());
423 }
424 Result(const Result &) = delete;
425 Result &operator=(const Result &) = delete;
Result(Result && other)426 Result(Result &&other) noexcept : status_(std::move(other.status_)) {
427 if (status_.is_ok()) {
428 new (&value_) T(std::move(other.value_));
429 other.value_.~T();
430 }
431 other.status_ = Status::Error<-2>();
432 }
433 Result &operator=(Result &&other) noexcept {
434 CHECK(this != &other);
435 if (status_.is_ok()) {
436 value_.~T();
437 }
438 if (other.status_.is_ok()) {
439 #if TD_GCC
440 #pragma GCC diagnostic push
441 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
442 #endif
443 new (&value_) T(std::move(other.value_));
444 #if TD_GCC
445 #pragma GCC diagnostic pop
446 #endif
447 other.value_.~T();
448 }
449 status_ = std::move(other.status_);
450 other.status_ = Status::Error<-3>();
451 return *this;
452 }
453 template <class... ArgsT>
emplace(ArgsT &&...args)454 void emplace(ArgsT &&... args) {
455 if (status_.is_ok()) {
456 value_.~T();
457 }
458 new (&value_) T(std::forward<ArgsT>(args)...);
459 status_ = Status::OK();
460 }
~Result()461 ~Result() {
462 if (status_.is_ok()) {
463 value_.~T();
464 }
465 }
466
467 #ifdef TD_STATUS_NO_ENSURE
ensure()468 void ensure() const {
469 status_.ensure();
470 }
ensure_error()471 void ensure_error() const {
472 status_.ensure_error();
473 }
474 #else
ensure_impl(CSlice file_name,int line)475 void ensure_impl(CSlice file_name, int line) const {
476 status_.ensure_impl(file_name, line);
477 }
ensure_error_impl(CSlice file_name,int line)478 void ensure_error_impl(CSlice file_name, int line) const {
479 status_.ensure_error_impl(file_name, line);
480 }
481 #endif
ignore()482 void ignore() const {
483 status_.ignore();
484 }
is_ok()485 bool is_ok() const {
486 return status_.is_ok();
487 }
is_error()488 bool is_error() const {
489 return status_.is_error();
490 }
error()491 const Status &error() const {
492 CHECK(status_.is_error());
493 return status_;
494 }
move_as_error()495 Status move_as_error() TD_WARN_UNUSED_RESULT {
496 CHECK(status_.is_error());
497 SCOPE_EXIT {
498 status_ = Status::Error<-4>();
499 };
500 return std::move(status_);
501 }
move_as_error_prefix(Slice prefix)502 Status move_as_error_prefix(Slice prefix) TD_WARN_UNUSED_RESULT {
503 SCOPE_EXIT {
504 status_ = Status::Error<-5>();
505 };
506 return status_.move_as_error_prefix(prefix);
507 }
move_as_error_prefix(const Status & prefix)508 Status move_as_error_prefix(const Status &prefix) TD_WARN_UNUSED_RESULT {
509 SCOPE_EXIT {
510 status_ = Status::Error<-6>();
511 };
512 return status_.move_as_error_prefix(prefix);
513 }
move_as_error_suffix(Slice suffix)514 Status move_as_error_suffix(Slice suffix) TD_WARN_UNUSED_RESULT {
515 SCOPE_EXIT {
516 status_ = Status::Error<-7>();
517 };
518 return status_.move_as_error_suffix(suffix);
519 }
ok()520 const T &ok() const {
521 LOG_CHECK(status_.is_ok()) << status_;
522 return value_;
523 }
ok_ref()524 T &ok_ref() {
525 LOG_CHECK(status_.is_ok()) << status_;
526 return value_;
527 }
ok_ref()528 const T &ok_ref() const {
529 LOG_CHECK(status_.is_ok()) << status_;
530 return value_;
531 }
move_as_ok()532 T move_as_ok() {
533 LOG_CHECK(status_.is_ok()) << status_;
534 return std::move(value_);
535 }
536
clone()537 Result<T> clone() const TD_WARN_UNUSED_RESULT {
538 if (is_ok()) {
539 return Result<T>(ok()); // TODO: return clone(ok());
540 }
541 return error().clone();
542 }
clear()543 void clear() {
544 *this = Result<T>();
545 }
546
547 template <class F>
move_map(F && f)548 Result<decltype(std::declval<F>()(std::declval<T>()))> move_map(F &&f) {
549 if (is_error()) {
550 return move_as_error();
551 }
552 return f(move_as_ok());
553 }
554
555 template <class F>
decltype(std::declval<F> ()(std::declval<T> ()))556 decltype(std::declval<F>()(std::declval<T>())) move_fmap(F &&f) {
557 if (is_error()) {
558 return move_as_error();
559 }
560 return f(move_as_ok());
561 }
562
563 private:
564 Status status_;
565 union {
566 T value_;
567 };
568 };
569
570 template <>
Result(Status && status)571 inline Result<Unit>::Result(Status &&status) : status_(std::move(status)) {
572 // no assert
573 }
574
575 inline StringBuilder &operator<<(StringBuilder &string_builder, const Status &status) {
576 return status.print(string_builder);
577 }
578
579 } // namespace td
580