204 lines
6.7 KiB
C++
204 lines
6.7 KiB
C++
/*
|
|
* 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/private/base/SkLog.h"
|
|
|
|
#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 { \
|
|
SKIA_LOG_E(SK_DUMP_LINE_FORMAT ": fatal error: \"" message "\"", \
|
|
__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.", 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.", 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.");
|
|
#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.", 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
|