libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | README | LICENSE

status.h (17440B)


      1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style
      4 // license that can be found in the LICENSE file.
      5 
      6 #ifndef LIB_JXL_BASE_STATUS_H_
      7 #define LIB_JXL_BASE_STATUS_H_
      8 
      9 // Error handling: Status return type + helper macros.
     10 
     11 #include <stdarg.h>
     12 #include <stdint.h>
     13 #include <stdio.h>
     14 #include <stdlib.h>
     15 
     16 #include <type_traits>
     17 #include <utility>
     18 
     19 #include "lib/jxl/base/common.h"
     20 #include "lib/jxl/base/compiler_specific.h"
     21 #include "lib/jxl/base/sanitizer_definitions.h"
     22 
     23 #if JXL_ADDRESS_SANITIZER || JXL_MEMORY_SANITIZER || JXL_THREAD_SANITIZER
     24 #include "sanitizer/common_interface_defs.h"  // __sanitizer_print_stack_trace
     25 #endif                                        // defined(*_SANITIZER)
     26 
     27 namespace jxl {
     28 
     29 // Uncomment to abort when JXL_FAILURE or JXL_STATUS with a fatal error is
     30 // reached:
     31 // #define JXL_CRASH_ON_ERROR
     32 
     33 #ifndef JXL_ENABLE_ASSERT
     34 #define JXL_ENABLE_ASSERT 1
     35 #endif
     36 
     37 #ifndef JXL_ENABLE_CHECK
     38 #define JXL_ENABLE_CHECK 1
     39 #endif
     40 
     41 // Pass -DJXL_DEBUG_ON_ERROR at compile time to print debug messages when a
     42 // function returns JXL_FAILURE or calls JXL_NOTIFY_ERROR. Note that this is
     43 // irrelevant if you also pass -DJXL_CRASH_ON_ERROR.
     44 #if defined(JXL_DEBUG_ON_ERROR) || defined(JXL_CRASH_ON_ERROR)
     45 #undef JXL_DEBUG_ON_ERROR
     46 #define JXL_DEBUG_ON_ERROR 1
     47 #else  // JXL_DEBUG_ON_ERROR || JXL_CRASH_ON_ERROR
     48 #ifdef NDEBUG
     49 #define JXL_DEBUG_ON_ERROR 0
     50 #else  // NDEBUG
     51 #define JXL_DEBUG_ON_ERROR 1
     52 #endif  // NDEBUG
     53 #endif  // JXL_DEBUG_ON_ERROR || JXL_CRASH_ON_ERROR
     54 
     55 // Pass -DJXL_DEBUG_ON_ALL_ERROR at compile time to print debug messages on
     56 // all error (fatal and non-fatal) status. This implies JXL_DEBUG_ON_ERROR.
     57 #if defined(JXL_DEBUG_ON_ALL_ERROR)
     58 #undef JXL_DEBUG_ON_ALL_ERROR
     59 #define JXL_DEBUG_ON_ALL_ERROR 1
     60 // JXL_DEBUG_ON_ALL_ERROR implies JXL_DEBUG_ON_ERROR too.
     61 #undef JXL_DEBUG_ON_ERROR
     62 #define JXL_DEBUG_ON_ERROR 1
     63 #else  // JXL_DEBUG_ON_ALL_ERROR
     64 #define JXL_DEBUG_ON_ALL_ERROR 0
     65 #endif  // JXL_DEBUG_ON_ALL_ERROR
     66 
     67 // The Verbose level for the library
     68 #ifndef JXL_DEBUG_V_LEVEL
     69 #define JXL_DEBUG_V_LEVEL 0
     70 #endif  // JXL_DEBUG_V_LEVEL
     71 
     72 // Pass -DJXL_DEBUG_ON_ABORT={0,1} to force disable/enable the debug messages on
     73 // JXL_ASSERT, JXL_CHECK and JXL_ABORT.
     74 #ifndef JXL_DEBUG_ON_ABORT
     75 #define JXL_DEBUG_ON_ABORT JXL_DEBUG_ON_ERROR
     76 #endif  // JXL_DEBUG_ON_ABORT
     77 
     78 #ifdef USE_ANDROID_LOGGER
     79 #include <android/log.h>
     80 #define LIBJXL_ANDROID_LOG_TAG ("libjxl")
     81 inline void android_vprintf(const char* format, va_list args) {
     82   char* message = nullptr;
     83   int res = vasprintf(&message, format, args);
     84   if (res != -1) {
     85     __android_log_write(ANDROID_LOG_DEBUG, LIBJXL_ANDROID_LOG_TAG, message);
     86     free(message);
     87   }
     88 }
     89 #endif
     90 
     91 // Print a debug message on standard error or android logs. You should use the
     92 // JXL_DEBUG macro instead of calling Debug directly. This function returns
     93 // false, so it can be used as a return value in JXL_FAILURE.
     94 JXL_FORMAT(1, 2)
     95 inline JXL_NOINLINE bool Debug(const char* format, ...) {
     96   va_list args;
     97   va_start(args, format);
     98 #ifdef USE_ANDROID_LOGGER
     99   android_vprintf(format, args);
    100 #else
    101   vfprintf(stderr, format, args);
    102 #endif
    103   va_end(args);
    104   return false;
    105 }
    106 
    107 // Print a debug message on standard error if "enabled" is true. "enabled" is
    108 // normally a macro that evaluates to 0 or 1 at compile time, so the Debug
    109 // function is never called and optimized out in release builds. Note that the
    110 // arguments are compiled but not evaluated when enabled is false. The format
    111 // string must be a explicit string in the call, for example:
    112 //   JXL_DEBUG(JXL_DEBUG_MYMODULE, "my module message: %d", some_var);
    113 // Add a header at the top of your module's .cc or .h file (depending on whether
    114 // you have JXL_DEBUG calls from the .h as well) like this:
    115 //   #ifndef JXL_DEBUG_MYMODULE
    116 //   #define JXL_DEBUG_MYMODULE 0
    117 //   #endif JXL_DEBUG_MYMODULE
    118 #define JXL_DEBUG_TMP(format, ...) \
    119   ::jxl::Debug(("%s:%d: " format "\n"), __FILE__, __LINE__, ##__VA_ARGS__)
    120 
    121 #define JXL_DEBUG(enabled, format, ...)     \
    122   do {                                      \
    123     if (enabled) {                          \
    124       JXL_DEBUG_TMP(format, ##__VA_ARGS__); \
    125     }                                       \
    126   } while (0)
    127 
    128 // JXL_DEBUG version that prints the debug message if the global verbose level
    129 // defined at compile time by JXL_DEBUG_V_LEVEL is greater or equal than the
    130 // passed level.
    131 #if JXL_DEBUG_V_LEVEL > 0
    132 #define JXL_DEBUG_V(level, format, ...) \
    133   JXL_DEBUG(level <= JXL_DEBUG_V_LEVEL, format, ##__VA_ARGS__)
    134 #else
    135 #define JXL_DEBUG_V(level, format, ...)
    136 #endif
    137 
    138 // Warnings (via JXL_WARNING) are enabled by default in debug builds (opt and
    139 // debug).
    140 #ifdef JXL_DEBUG_WARNING
    141 #undef JXL_DEBUG_WARNING
    142 #define JXL_DEBUG_WARNING 1
    143 #else  // JXL_DEBUG_WARNING
    144 #ifdef NDEBUG
    145 #define JXL_DEBUG_WARNING 0
    146 #else  // JXL_DEBUG_WARNING
    147 #define JXL_DEBUG_WARNING 1
    148 #endif  // NDEBUG
    149 #endif  // JXL_DEBUG_WARNING
    150 #define JXL_WARNING(format, ...) \
    151   JXL_DEBUG(JXL_DEBUG_WARNING, format, ##__VA_ARGS__)
    152 
    153 // Exits the program after printing a stack trace when possible.
    154 JXL_NORETURN inline JXL_NOINLINE bool Abort() {
    155 #if JXL_ADDRESS_SANITIZER || JXL_MEMORY_SANITIZER || JXL_THREAD_SANITIZER
    156   // If compiled with any sanitizer print a stack trace. This call doesn't crash
    157   // the program, instead the trap below will crash it also allowing gdb to
    158   // break there.
    159   __sanitizer_print_stack_trace();
    160 #endif  // *_SANITIZER)
    161 
    162 #if JXL_COMPILER_MSVC
    163   __debugbreak();
    164   abort();
    165 #else
    166   __builtin_trap();
    167 #endif
    168 }
    169 
    170 // Exits the program after printing file/line plus a formatted string.
    171 #define JXL_ABORT(format, ...)                                              \
    172   ((JXL_DEBUG_ON_ABORT) && ::jxl::Debug(("%s:%d: JXL_ABORT: " format "\n"), \
    173                                         __FILE__, __LINE__, ##__VA_ARGS__), \
    174    ::jxl::Abort())
    175 
    176 // Use this for code paths that are unreachable unless the code would change
    177 // to make it reachable, in which case it will print a warning and abort in
    178 // debug builds. In release builds no code is produced for this, so only use
    179 // this if this path is really unreachable.
    180 #define JXL_UNREACHABLE(format, ...)                                   \
    181   do {                                                                 \
    182     if (JXL_DEBUG_WARNING) {                                           \
    183       ::jxl::Debug(("%s:%d: JXL_UNREACHABLE: " format "\n"), __FILE__, \
    184                    __LINE__, ##__VA_ARGS__);                           \
    185       ::jxl::Abort();                                                  \
    186     } else {                                                           \
    187       JXL_UNREACHABLE_BUILTIN;                                         \
    188     }                                                                  \
    189   } while (0)
    190 
    191 // Does not guarantee running the code, use only for debug mode checks.
    192 #if JXL_ENABLE_ASSERT
    193 #define JXL_ASSERT(condition)                                      \
    194   do {                                                             \
    195     if (!(condition)) {                                            \
    196       JXL_DEBUG(JXL_DEBUG_ON_ABORT, "JXL_ASSERT: %s", #condition); \
    197       ::jxl::Abort();                                              \
    198     }                                                              \
    199   } while (0)
    200 #else
    201 #define JXL_ASSERT(condition) \
    202   do {                        \
    203   } while (0)
    204 #endif
    205 
    206 // Define JXL_IS_DEBUG_BUILD that denotes asan, msan and other debug builds,
    207 // but not opt or release.
    208 #ifndef JXL_IS_DEBUG_BUILD
    209 #if !defined(NDEBUG) || defined(ADDRESS_SANITIZER) ||         \
    210     defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \
    211     defined(__clang_analyzer__)
    212 #define JXL_IS_DEBUG_BUILD 1
    213 #else
    214 #define JXL_IS_DEBUG_BUILD 0
    215 #endif
    216 #endif  //  JXL_IS_DEBUG_BUILD
    217 
    218 // Same as above, but only runs in debug builds (builds where NDEBUG is not
    219 // defined). This is useful for slower asserts that we want to run more rarely
    220 // than usual. These will run on asan, msan and other debug builds, but not in
    221 // opt or release.
    222 #if JXL_IS_DEBUG_BUILD
    223 #define JXL_DASSERT(condition)                                      \
    224   do {                                                              \
    225     if (!(condition)) {                                             \
    226       JXL_DEBUG(JXL_DEBUG_ON_ABORT, "JXL_DASSERT: %s", #condition); \
    227       ::jxl::Abort();                                               \
    228     }                                                               \
    229   } while (0)
    230 #else
    231 #define JXL_DASSERT(condition) \
    232   do {                         \
    233   } while (0)
    234 #endif
    235 
    236 // Always runs the condition, so can be used for non-debug calls.
    237 #if JXL_ENABLE_CHECK
    238 #define JXL_CHECK(condition)                                      \
    239   do {                                                            \
    240     if (!(condition)) {                                           \
    241       JXL_DEBUG(JXL_DEBUG_ON_ABORT, "JXL_CHECK: %s", #condition); \
    242       ::jxl::Abort();                                             \
    243     }                                                             \
    244   } while (0)
    245 #else
    246 #define JXL_CHECK(condition) \
    247   do {                       \
    248     (void)(condition);       \
    249   } while (0)
    250 #endif
    251 
    252 // A jxl::Status value from a StatusCode or Status which prints a debug message
    253 // when enabled.
    254 #define JXL_STATUS(status, format, ...)                                        \
    255   ::jxl::StatusMessage(::jxl::Status(status), "%s:%d: " format "\n", __FILE__, \
    256                        __LINE__, ##__VA_ARGS__)
    257 
    258 // Notify of an error but discard the resulting Status value. This is only
    259 // useful for debug builds or when building with JXL_CRASH_ON_ERROR.
    260 #define JXL_NOTIFY_ERROR(format, ...)                                      \
    261   (void)JXL_STATUS(::jxl::StatusCode::kGenericError, "JXL_ERROR: " format, \
    262                    ##__VA_ARGS__)
    263 
    264 // An error Status with a message. The JXL_STATUS() macro will return a Status
    265 // object with a kGenericError code, but the comma operator helps with
    266 // clang-tidy inference and potentially with optimizations.
    267 #define JXL_FAILURE(format, ...)                                              \
    268   ((void)JXL_STATUS(::jxl::StatusCode::kGenericError, "JXL_FAILURE: " format, \
    269                     ##__VA_ARGS__),                                           \
    270    ::jxl::Status(::jxl::StatusCode::kGenericError))
    271 
    272 // Always evaluates the status exactly once, so can be used for non-debug calls.
    273 // Returns from the current context if the passed Status expression is an error
    274 // (fatal or non-fatal). The return value is the passed Status.
    275 #define JXL_RETURN_IF_ERROR(status)                                       \
    276   do {                                                                    \
    277     ::jxl::Status jxl_return_if_error_status = (status);                  \
    278     if (!jxl_return_if_error_status) {                                    \
    279       (void)::jxl::StatusMessage(                                         \
    280           jxl_return_if_error_status,                                     \
    281           "%s:%d: JXL_RETURN_IF_ERROR code=%d: %s\n", __FILE__, __LINE__, \
    282           static_cast<int>(jxl_return_if_error_status.code()), #status);  \
    283       return jxl_return_if_error_status;                                  \
    284     }                                                                     \
    285   } while (0)
    286 
    287 // As above, but without calling StatusMessage. Intended for bundles (see
    288 // fields.h), which have numerous call sites (-> relevant for code size) and do
    289 // not want to generate excessive messages when decoding partial headers.
    290 #define JXL_QUIET_RETURN_IF_ERROR(status)                \
    291   do {                                                   \
    292     ::jxl::Status jxl_return_if_error_status = (status); \
    293     if (!jxl_return_if_error_status) {                   \
    294       return jxl_return_if_error_status;                 \
    295     }                                                    \
    296   } while (0)
    297 
    298 enum class StatusCode : int32_t {
    299   // Non-fatal errors (negative values).
    300   kNotEnoughBytes = -1,
    301 
    302   // The only non-error status code.
    303   kOk = 0,
    304 
    305   // Fatal-errors (positive values)
    306   kGenericError = 1,
    307 };
    308 
    309 // Drop-in replacement for bool that raises compiler warnings if not used
    310 // after being returned from a function. Example:
    311 // Status LoadFile(...) { return true; } is more compact than
    312 // bool JXL_MUST_USE_RESULT LoadFile(...) { return true; }
    313 // In case of error, the status can carry an extra error code in its value which
    314 // is split between fatal and non-fatal error codes.
    315 class JXL_MUST_USE_RESULT Status {
    316  public:
    317   // We want implicit constructor from bool to allow returning "true" or "false"
    318   // on a function when using Status. "true" means kOk while "false" means a
    319   // generic fatal error.
    320   // NOLINTNEXTLINE(google-explicit-constructor)
    321   constexpr Status(bool ok)
    322       : code_(ok ? StatusCode::kOk : StatusCode::kGenericError) {}
    323 
    324   // NOLINTNEXTLINE(google-explicit-constructor)
    325   constexpr Status(StatusCode code) : code_(code) {}
    326 
    327   // We also want implicit cast to bool to check for return values of functions.
    328   // NOLINTNEXTLINE(google-explicit-constructor)
    329   constexpr operator bool() const { return code_ == StatusCode::kOk; }
    330 
    331   constexpr StatusCode code() const { return code_; }
    332 
    333   // Returns whether the status code is a fatal error.
    334   constexpr bool IsFatalError() const {
    335     return static_cast<int32_t>(code_) > 0;
    336   }
    337 
    338  private:
    339   StatusCode code_;
    340 };
    341 
    342 static constexpr Status OkStatus() { return Status(StatusCode::kOk); }
    343 
    344 // Helper function to create a Status and print the debug message or abort when
    345 // needed.
    346 inline JXL_FORMAT(2, 3) Status
    347     StatusMessage(const Status status, const char* format, ...) {
    348   // This block will be optimized out when JXL_DEBUG_ON_ERROR and
    349   // JXL_DEBUG_ON_ALL_ERROR are both disabled.
    350   if ((JXL_DEBUG_ON_ERROR && status.IsFatalError()) ||
    351       (JXL_DEBUG_ON_ALL_ERROR && !status)) {
    352     va_list args;
    353     va_start(args, format);
    354 #ifdef USE_ANDROID_LOGGER
    355     android_vprintf(format, args);
    356 #else
    357     vfprintf(stderr, format, args);
    358 #endif
    359     va_end(args);
    360   }
    361 #ifdef JXL_CRASH_ON_ERROR
    362   // JXL_CRASH_ON_ERROR means to Abort() only on non-fatal errors.
    363   if (status.IsFatalError()) {
    364     Abort();
    365   }
    366 #endif  // JXL_CRASH_ON_ERROR
    367   return status;
    368 }
    369 
    370 template <typename T>
    371 class JXL_MUST_USE_RESULT StatusOr {
    372   static_assert(!std::is_convertible<StatusCode, T>::value &&
    373                     !std::is_convertible<T, StatusCode>::value,
    374                 "You cannot make a StatusOr with a type convertible from or to "
    375                 "StatusCode");
    376   static_assert(std::is_move_constructible<T>::value &&
    377                     std::is_move_assignable<T>::value,
    378                 "T must be move constructible and move assignable");
    379 
    380  public:
    381   // NOLINTNEXTLINE(google-explicit-constructor)
    382   StatusOr(StatusCode code) : code_(code) {
    383     JXL_ASSERT(code_ != StatusCode::kOk);
    384   }
    385 
    386   // NOLINTNEXTLINE(google-explicit-constructor)
    387   StatusOr(Status status) : StatusOr(status.code()) {}
    388 
    389   // NOLINTNEXTLINE(google-explicit-constructor)
    390   StatusOr(T&& value) : code_(StatusCode::kOk) {
    391     new (&storage_.data_) T(std::move(value));
    392   }
    393 
    394   StatusOr(StatusOr&& other) noexcept {
    395     if (other.ok()) {
    396       new (&storage_.data_) T(std::move(other.storage_.data_));
    397     }
    398     code_ = other.code_;
    399   }
    400 
    401   StatusOr& operator=(StatusOr&& other) noexcept {
    402     if (this == &other) return *this;
    403     if (ok() && other.ok()) {
    404       storage_.data_ = std::move(other.storage_.data_);
    405     } else if (other.ok()) {
    406       new (&storage_.data_) T(std::move(other.storage_.data_));
    407     } else if (ok()) {
    408       storage_.data_.~T();
    409     }
    410     code_ = other.code_;
    411     return *this;
    412   }
    413 
    414   StatusOr(const StatusOr&) = delete;
    415   StatusOr operator=(const StatusOr&) = delete;
    416 
    417   bool ok() const { return code_ == StatusCode::kOk; }
    418   Status status() const { return code_; }
    419 
    420   // Only call this if you are absolutely sure that `ok()` is true.
    421   // Ideally, never call this manually and rely on JXL_ASSIGN_OR_RETURN.
    422   T value() && {
    423     JXL_ASSERT(ok());
    424     return std::move(storage_.data_);
    425   }
    426 
    427   ~StatusOr() {
    428     if (code_ == StatusCode::kOk) {
    429       storage_.data_.~T();
    430     }
    431   }
    432 
    433  private:
    434   union Storage {
    435     char placeholder_;
    436     T data_;
    437     Storage() {}
    438     ~Storage() {}
    439   } storage_;
    440 
    441   StatusCode code_;
    442 };
    443 
    444 #define JXL_ASSIGN_OR_RETURN(lhs, statusor) \
    445   PRIVATE_JXL_ASSIGN_OR_RETURN_IMPL(        \
    446       JXL_JOIN(assign_or_return_temporary_variable, __LINE__), lhs, statusor)
    447 
    448 // NOLINTBEGIN(bugprone-macro-parentheses)
    449 #define PRIVATE_JXL_ASSIGN_OR_RETURN_IMPL(name, lhs, statusor) \
    450   auto name = statusor;                                        \
    451   JXL_RETURN_IF_ERROR(name.status());                          \
    452   lhs = std::move(name).value();
    453 // NOLINTEND(bugprone-macro-parentheses)
    454 
    455 // NB: do not use outside of tests / tools!!!
    456 #define JXL_ASSIGN_OR_DIE(lhs, statusor) \
    457   PRIVATE_JXL_ASSIGN_OR_DIE_IMPL(        \
    458       JXL_JOIN(assign_or_die_temporary_variable, __LINE__), lhs, statusor)
    459 
    460 // NOLINTBEGIN(bugprone-macro-parentheses)
    461 #define PRIVATE_JXL_ASSIGN_OR_DIE_IMPL(name, lhs, statusor) \
    462   auto name = statusor;                                     \
    463   if (!name.ok()) jxl::Abort();                             \
    464   lhs = std::move(name).value();
    465 // NOLINTEND(bugprone-macro-parentheses)
    466 
    467 }  // namespace jxl
    468 
    469 #endif  // LIB_JXL_BASE_STATUS_H_