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