/* * Copyright 2022 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkAssert_DEFINED #define SkAssert_DEFINED #include "include/private/base/SkAPI.h" #include "include/private/base/SkAttributes.h" #include "include/private/base/SkDebug.h" // IWYU pragma: keep #include #include #if defined(__clang__) && defined(__has_attribute) #if __has_attribute(likely) #define SK_LIKELY [[likely]] #define SK_UNLIKELY [[unlikely]] #else #define SK_LIKELY #define SK_UNLIKELY #endif #else #define SK_LIKELY #define SK_UNLIKELY #endif // c++23 will give us [[assume]] -- until then we're stuck with various other options: #if defined(__clang__) #define SK_ASSUME(cond) __builtin_assume(cond) #elif defined(__GNUC__) #if __GNUC__ >= 13 #define SK_ASSUME(cond) __attribute__((assume(cond))) #else // NOTE: This implementation could actually evaluate `cond`, which is not desirable. #define SK_ASSUME(cond) ((cond) ? (void)0 : __builtin_unreachable()) #endif #elif defined(_MSC_VER) #define SK_ASSUME(cond) __assume(cond) #else #define SK_ASSUME(cond) ((void)0) #endif /** Called internally if we hit an unrecoverable error. The platform implementation must not return, but should either throw an exception or otherwise exit. */ [[noreturn]] SK_API extern void sk_abort_no_print(void); #if defined(SK_BUILD_FOR_GOOGLE3) void SkDebugfForDumpStackTrace(const char* data, void* unused); namespace base { void DumpStackTrace(int skip_count, void w(const char*, void*), void* arg); } # define SK_DUMP_GOOGLE3_STACK() ::base::DumpStackTrace(0, SkDebugfForDumpStackTrace, nullptr) #else # define SK_DUMP_GOOGLE3_STACK() #endif #if !defined(SK_ABORT) # if defined(SK_BUILD_FOR_WIN) // This style lets Visual Studio follow errors back to the source file. # define SK_DUMP_LINE_FORMAT "%s(%d)" # else # define SK_DUMP_LINE_FORMAT "%s:%d" # endif # define SK_ABORT(message, ...) \ do { \ SkDebugf(SK_DUMP_LINE_FORMAT ": fatal error: \"" message "\"\n", \ __FILE__, __LINE__, ##__VA_ARGS__); \ SK_DUMP_GOOGLE3_STACK(); \ sk_abort_no_print(); \ } while (false) #endif // SkASSERT, SkASSERTF and SkASSERT_RELEASE can be used as standalone assertion expressions, e.g. // uint32_t foo(int x) { // SkASSERT(x > 4); // return x - 4; // } // and are also written to be compatible with constexpr functions: // constexpr uint32_t foo(int x) { // return SkASSERT(x > 4), // x - 4; // } #if defined(__clang__) #define SkASSERT_RELEASE(cond) \ static_cast( __builtin_expect(static_cast(cond), 1) \ ? static_cast(0) \ : []{ SK_ABORT("check(%s)", #cond); }() ) #define SkASSERTF_RELEASE(cond, fmt, ...) \ static_cast( __builtin_expect(static_cast(cond), 1) \ ? static_cast(0) \ : [&]{ SK_ABORT("assertf(%s): " fmt, #cond, ##__VA_ARGS__); }() ) #else #define SkASSERT_RELEASE(cond) \ static_cast( (cond) ? static_cast(0) : []{ SK_ABORT("check(%s)", #cond); }() ) #define SkASSERTF_RELEASE(cond, fmt, ...) \ static_cast( (cond) \ ? static_cast(0) \ : [&]{ SK_ABORT("assertf(%s): " fmt, #cond, ##__VA_ARGS__); }() ) #endif #if defined(SK_DEBUG) #define SkASSERT(cond) SkASSERT_RELEASE(cond) #define SkASSERTF(cond, fmt, ...) SkASSERTF_RELEASE(cond, fmt, ##__VA_ARGS__) #define SkDEBUGFAIL(message) SK_ABORT("%s", message) #define SkDEBUGFAILF(fmt, ...) SK_ABORT(fmt, ##__VA_ARGS__) #define SkAssertResult(cond) SkASSERT(cond) #else #define SkASSERT(cond) static_cast(0) #define SkASSERTF(cond, fmt, ...) static_cast(0) #define SkDEBUGFAIL(message) #define SkDEBUGFAILF(fmt, ...) // unlike SkASSERT, this macro executes its condition in the non-debug build. // The if is present so that this can be used with functions marked [[nodiscard]]. #define SkAssertResult(cond) if (cond) {} do {} while(false) #endif #if !defined(SkUNREACHABLE) # if defined(_MSC_VER) && !defined(__clang__) # include # define FAST_FAIL_INVALID_ARG 5 // See https://developercommunity.visualstudio.com/content/problem/1128631/code-flow-doesnt-see-noreturn-with-extern-c.html // for why this is wrapped. Hopefully removable after msvc++ 19.27 is no longer supported. [[noreturn]] static inline void sk_fast_fail() { __fastfail(FAST_FAIL_INVALID_ARG); } # define SkUNREACHABLE sk_fast_fail() # else # define SkUNREACHABLE __builtin_trap() # endif #endif [[noreturn]] SK_API inline void sk_print_index_out_of_bounds(size_t i, size_t size) { SK_ABORT("Index (%zu) out of bounds for size %zu.\n", i, size); } template SK_API inline T sk_collection_check_bounds(T i, T size) { if (0 <= i && i < size) SK_LIKELY { return i; } SK_UNLIKELY { #if defined(SK_DEBUG) sk_print_index_out_of_bounds(static_cast(i), static_cast(size)); #else SkUNREACHABLE; #endif } } [[noreturn]] SK_API inline void sk_print_length_too_big(size_t i, size_t size) { SK_ABORT("Length (%zu) is too big for size %zu.\n", i, size); } template SK_API inline T sk_collection_check_length(T i, T size) { if (0 <= i && i <= size) SK_LIKELY { return i; } SK_UNLIKELY { #if defined(SK_DEBUG) sk_print_length_too_big(static_cast(i), static_cast(size)); #else SkUNREACHABLE; #endif } } SK_API inline void sk_collection_not_empty(bool empty) { if (empty) SK_UNLIKELY { #if defined(SK_DEBUG) SK_ABORT("Collection is empty.\n"); #else SkUNREACHABLE; #endif } } [[noreturn]] SK_API inline void sk_print_size_too_big(size_t size, size_t maxSize) { SK_ABORT("Size (%zu) can't be represented in bytes. Max size is %zu.\n", size, maxSize); } template SK_ALWAYS_INLINE size_t check_size_bytes_too_big(size_t size) { const size_t kMaxSize = std::numeric_limits::max() / sizeof(T); if (size > kMaxSize) { #if defined(SK_DEBUG) sk_print_size_too_big(size, kMaxSize); #else SkUNREACHABLE; #endif } return size; } #endif // SkAssert_DEFINED