1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
|
/*
* 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 <cstddef>
#include <limits>
#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<void>( __builtin_expect(static_cast<bool>(cond), 1) \
? static_cast<void>(0) \
: []{ SK_ABORT("check(%s)", #cond); }() )
#define SkASSERTF_RELEASE(cond, fmt, ...) \
static_cast<void>( __builtin_expect(static_cast<bool>(cond), 1) \
? static_cast<void>(0) \
: [&]{ SK_ABORT("assertf(%s): " fmt, #cond, ##__VA_ARGS__); }() )
#else
#define SkASSERT_RELEASE(cond) \
static_cast<void>( (cond) ? static_cast<void>(0) : []{ SK_ABORT("check(%s)", #cond); }() )
#define SkASSERTF_RELEASE(cond, fmt, ...) \
static_cast<void>( (cond) \
? static_cast<void>(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<void>(0)
#define SkASSERTF(cond, fmt, ...) static_cast<void>(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 <intrin.h>
# 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 <typename T> 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<size_t>(i), static_cast<size_t>(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 <typename T> 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<size_t>(i), static_cast<size_t>(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 <typename T>
SK_ALWAYS_INLINE size_t check_size_bytes_too_big(size_t size) {
const size_t kMaxSize = std::numeric_limits<size_t>::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
|