xref: /aosp_15_r20/external/skia/include/private/base/SkAssert.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2022 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #ifndef SkAssert_DEFINED
9 #define SkAssert_DEFINED
10 
11 #include "include/private/base/SkAPI.h"
12 #include "include/private/base/SkAttributes.h"
13 #include "include/private/base/SkDebug.h" // IWYU pragma: keep
14 
15 #include <cstddef>
16 #include <limits>
17 
18 #if defined(__clang__) && defined(__has_attribute)
19     #if __has_attribute(likely)
20         #define SK_LIKELY [[likely]]
21         #define SK_UNLIKELY [[unlikely]]
22     #else
23         #define SK_LIKELY
24         #define SK_UNLIKELY
25     #endif
26 #else
27     #define SK_LIKELY
28     #define SK_UNLIKELY
29 #endif
30 
31 // c++23 will give us [[assume]] -- until then we're stuck with various other options:
32 #if defined(__clang__)
33     #define SK_ASSUME(cond) __builtin_assume(cond)
34 #elif defined(__GNUC__)
35     #if __GNUC__ >= 13
36         #define SK_ASSUME(cond) __attribute__((assume(cond)))
37     #else
38         // NOTE: This implementation could actually evaluate `cond`, which is not desirable.
39         #define SK_ASSUME(cond) ((cond) ? (void)0 : __builtin_unreachable())
40     #endif
41 #elif defined(_MSC_VER)
42     #define SK_ASSUME(cond) __assume(cond)
43 #else
44     #define SK_ASSUME(cond) ((void)0)
45 #endif
46 
47 /** Called internally if we hit an unrecoverable error.
48     The platform implementation must not return, but should either throw
49     an exception or otherwise exit.
50 */
51 [[noreturn]] SK_API extern void sk_abort_no_print(void);
52 
53 #if defined(SK_BUILD_FOR_GOOGLE3)
54     void SkDebugfForDumpStackTrace(const char* data, void* unused);
55     namespace base {
56         void DumpStackTrace(int skip_count, void w(const char*, void*), void* arg);
57     }
58 #  define SK_DUMP_GOOGLE3_STACK() ::base::DumpStackTrace(0, SkDebugfForDumpStackTrace, nullptr)
59 #else
60 #  define SK_DUMP_GOOGLE3_STACK()
61 #endif
62 
63 #if !defined(SK_ABORT)
64 #  if defined(SK_BUILD_FOR_WIN)
65      // This style lets Visual Studio follow errors back to the source file.
66 #    define SK_DUMP_LINE_FORMAT "%s(%d)"
67 #  else
68 #    define SK_DUMP_LINE_FORMAT "%s:%d"
69 #  endif
70 #  define SK_ABORT(message, ...) \
71     do { \
72         SkDebugf(SK_DUMP_LINE_FORMAT ": fatal error: \"" message "\"\n", \
73                  __FILE__, __LINE__, ##__VA_ARGS__); \
74         SK_DUMP_GOOGLE3_STACK(); \
75         sk_abort_no_print(); \
76     } while (false)
77 #endif
78 
79 // SkASSERT, SkASSERTF and SkASSERT_RELEASE can be used as standalone assertion expressions, e.g.
80 //    uint32_t foo(int x) {
81 //        SkASSERT(x > 4);
82 //        return x - 4;
83 //    }
84 // and are also written to be compatible with constexpr functions:
85 //    constexpr uint32_t foo(int x) {
86 //        return SkASSERT(x > 4),
87 //               x - 4;
88 //    }
89 #if defined(__clang__)
90 #define SkASSERT_RELEASE(cond) \
91     static_cast<void>( __builtin_expect(static_cast<bool>(cond), 1) \
92         ? static_cast<void>(0) \
93         : []{ SK_ABORT("check(%s)", #cond); }() )
94 
95 #define SkASSERTF_RELEASE(cond, fmt, ...)                                  \
96     static_cast<void>( __builtin_expect(static_cast<bool>(cond), 1)        \
97         ? static_cast<void>(0)                                             \
98         : [&]{ SK_ABORT("assertf(%s): " fmt, #cond, ##__VA_ARGS__); }() )
99 #else
100 #define SkASSERT_RELEASE(cond) \
101     static_cast<void>( (cond) ? static_cast<void>(0) : []{ SK_ABORT("check(%s)", #cond); }() )
102 
103 #define SkASSERTF_RELEASE(cond, fmt, ...)                                   \
104     static_cast<void>( (cond)                                               \
105         ? static_cast<void>(0)                                              \
106         : [&]{ SK_ABORT("assertf(%s): " fmt, #cond, ##__VA_ARGS__); }() )
107 #endif
108 
109 #if defined(SK_DEBUG)
110     #define SkASSERT(cond)            SkASSERT_RELEASE(cond)
111     #define SkASSERTF(cond, fmt, ...) SkASSERTF_RELEASE(cond, fmt, ##__VA_ARGS__)
112     #define SkDEBUGFAIL(message)      SK_ABORT("%s", message)
113     #define SkDEBUGFAILF(fmt, ...)    SK_ABORT(fmt, ##__VA_ARGS__)
114     #define SkAssertResult(cond)      SkASSERT(cond)
115 #else
116     #define SkASSERT(cond)            static_cast<void>(0)
117     #define SkASSERTF(cond, fmt, ...) static_cast<void>(0)
118     #define SkDEBUGFAIL(message)
119     #define SkDEBUGFAILF(fmt, ...)
120 
121     // unlike SkASSERT, this macro executes its condition in the non-debug build.
122     // The if is present so that this can be used with functions marked [[nodiscard]].
123     #define SkAssertResult(cond)         if (cond) {} do {} while(false)
124 #endif
125 
126 #if !defined(SkUNREACHABLE)
127 #  if defined(_MSC_VER) && !defined(__clang__)
128 #    include <intrin.h>
129 #    define FAST_FAIL_INVALID_ARG                 5
130 // See https://developercommunity.visualstudio.com/content/problem/1128631/code-flow-doesnt-see-noreturn-with-extern-c.html
131 // for why this is wrapped. Hopefully removable after msvc++ 19.27 is no longer supported.
sk_fast_fail()132 [[noreturn]] static inline void sk_fast_fail() { __fastfail(FAST_FAIL_INVALID_ARG); }
133 #    define SkUNREACHABLE sk_fast_fail()
134 #  else
135 #    define SkUNREACHABLE __builtin_trap()
136 #  endif
137 #endif
138 
sk_print_index_out_of_bounds(size_t i,size_t size)139 [[noreturn]] SK_API inline void sk_print_index_out_of_bounds(size_t i, size_t size) {
140     SK_ABORT("Index (%zu) out of bounds for size %zu.\n", i, size);
141 }
142 
sk_collection_check_bounds(T i,T size)143 template <typename T> SK_API inline T sk_collection_check_bounds(T i, T size) {
144     if (0 <= i && i < size) SK_LIKELY {
145         return i;
146     }
147 
148     SK_UNLIKELY {
149         #if defined(SK_DEBUG)
150             sk_print_index_out_of_bounds(static_cast<size_t>(i), static_cast<size_t>(size));
151         #else
152             SkUNREACHABLE;
153         #endif
154     }
155 }
156 
sk_print_length_too_big(size_t i,size_t size)157 [[noreturn]] SK_API inline void sk_print_length_too_big(size_t i, size_t size) {
158     SK_ABORT("Length (%zu) is too big for size %zu.\n", i, size);
159 }
160 
sk_collection_check_length(T i,T size)161 template <typename T> SK_API inline T sk_collection_check_length(T i, T size) {
162     if (0 <= i && i <= size) SK_LIKELY {
163         return i;
164     }
165 
166     SK_UNLIKELY {
167         #if defined(SK_DEBUG)
168             sk_print_length_too_big(static_cast<size_t>(i), static_cast<size_t>(size));
169         #else
170             SkUNREACHABLE;
171         #endif
172     }
173 }
174 
sk_collection_not_empty(bool empty)175 SK_API inline void sk_collection_not_empty(bool empty) {
176     if (empty) SK_UNLIKELY {
177         #if defined(SK_DEBUG)
178             SK_ABORT("Collection is empty.\n");
179         #else
180             SkUNREACHABLE;
181         #endif
182     }
183 }
184 
sk_print_size_too_big(size_t size,size_t maxSize)185 [[noreturn]] SK_API inline void sk_print_size_too_big(size_t size, size_t maxSize) {
186     SK_ABORT("Size (%zu) can't be represented in bytes. Max size is %zu.\n", size, maxSize);
187 }
188 
189 template <typename T>
check_size_bytes_too_big(size_t size)190 SK_ALWAYS_INLINE size_t check_size_bytes_too_big(size_t size) {
191     const size_t kMaxSize = std::numeric_limits<size_t>::max() / sizeof(T);
192     if (size > kMaxSize) {
193         #if defined(SK_DEBUG)
194             sk_print_size_too_big(size, kMaxSize);
195         #else
196             SkUNREACHABLE;
197         #endif
198     }
199     return size;
200 }
201 
202 #endif  // SkAssert_DEFINED
203