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