src: introduce upx::atomic_exchange; cleanups

This commit is contained in:
Markus F.X.J. Oberhumer 2024-05-07 10:05:44 +02:00
parent e5546bc8b0
commit 3d82f0cfe1
10 changed files with 174 additions and 70 deletions

View File

@ -59,10 +59,10 @@ CTEST = ctest
CTEST_JOBS ?= 8
CTEST_FLAGS = --output-on-failure --parallel $(CTEST_JOBS)
build/debug+test: $$(dir $$@)debug PHONY; cd "$(dir $@)debug" && $(CTEST) -C Debug $(CTEST_FLAGS)
build/%/debug+test: $$(dir $$@)debug PHONY; cd "$(dir $@)debug" && $(CTEST) -C Debug $(CTEST_FLAGS)
build/release+test: $$(dir $$@)release PHONY; cd "$(dir $@)release" && $(CTEST) -C Release $(CTEST_FLAGS)
build/%/release+test: $$(dir $$@)release PHONY; cd "$(dir $@)release" && $(CTEST) -C Release $(CTEST_FLAGS)
build/debug+test: $$(dir $$@)debug PHONY; cd "$(dir $@)debug" && $(CTEST) $(CTEST_FLAGS) -C Debug
build/%/debug+test: $$(dir $$@)debug PHONY; cd "$(dir $@)debug" && $(CTEST) $(CTEST_FLAGS) -C Debug
build/release+test: $$(dir $$@)release PHONY; cd "$(dir $@)release" && $(CTEST) $(CTEST_FLAGS) -C Release
build/%/release+test: $$(dir $$@)release PHONY; cd "$(dir $@)release" && $(CTEST) $(CTEST_FLAGS) -C Release
build/%/all+test: $$(dir $$@)debug+test $$(dir $$@)release+test PHONY ;
# shortcuts

View File

@ -24,7 +24,7 @@ upx_print_var(CMAKE_APPLE_SILICON_PROCESSOR CMAKE_OSX_DEPLOYMENT_TARGET CMAKE_OS
upx_print_Var(CMAKE_CROSSCOMPILING CMAKE_CROSSCOMPILING_EMULATOR)
# binutils
upx_print_var(CMAKE_AR CMAKE_OBJCOPY CMAKE_RANLIB)
upx_print_var(CMAKE_AR CMAKE_RANLIB)
# compilers
foreach(lang IN ITEMS ASM C CXX)

View File

@ -19,7 +19,7 @@ RUN dpkg --add-architecture i386 \
libzstd-dev lsb-release lz4 lzip lzop minify mksh moreutils musl neovim ninja-build \
p7zip parallel patch patchelf patchutils pax-utils proot \
python3 python3-pyasn1 python3-pycryptodome python3-pycurl python3-toml python3-yaml python3-zstd \
re2c ripgrep rsync screen universal-ctags unzip yash yq \
re2c ripgrep rsync screen strace universal-ctags unzip valgrind yash yq \
zip zlib1g-dev zoxide zsh zstd \
# extra packages for compiling with "gcc -m32" and "gcc -mx32":
g++-multilib gcc-multilib \
@ -83,8 +83,11 @@ RUN useradd upx -U --uid 2000 --shell /bin/bash -m \
&& echo "alias rd=rmdir" >> .bashrc \
&& echo 'mdd() { mkdir "$1" && cd "$1"; }' >> .bashrc \
&& echo 'm() { make "$@"; }' >> .bashrc \
&& echo 'tn() { time nice "$@"; }' >> .bashrc \
&& echo 'tnm() { time nice make "$@"; }' >> .bashrc \
&& echo 'tnmm() { time nice make -j "$@"; }' >> .bashrc \
&& echo 'source /usr/share/bash-completion/completions/make' >> .bashrc \
&& echo 'complete -F _make m' >> .bashrc \
&& echo 'complete -F _make m tnm tnmm' >> .bashrc \
&& echo 'eval "$(zoxide init bash)"' >> .bashrc \
&& echo "alias zz=zi\nalias y=z\nalias yy=zi" >> .bashrc \
&& echo "alias cd..='cd ..'" >> .bashrc \

View File

@ -109,6 +109,7 @@ ii libbz2-1.0:amd64 1.0.8-5.1 amd64
ii libbzip3-0:amd64 1.4.0-1 amd64 better, faster and stronger spiritual successor to bzip2 - runtime
ii libc-bin 2.39-0ubuntu8.1 amd64 GNU C Library: Binaries
ii libc-dev-bin 2.39-0ubuntu8.1 amd64 GNU C Library: Development binaries
ii libc6-dbg:amd64 2.39-0ubuntu8.1 amd64 GNU C Library: detached debugging symbols
ii libc6-dev-i386 2.39-0ubuntu8.1 amd64 GNU C Library: 32-bit development libraries for AMD64
ii libc6-dev-x32 2.39-0ubuntu8.1 amd64 GNU C Library: X32 ABI Development Libraries for AMD64
ii libc6-dev:amd64 2.39-0ubuntu8.1 amd64 GNU C Library: Development Libraries and Header Files
@ -246,6 +247,7 @@ ii libuchardet0:amd64 0.0.8-1build1 amd64
ii libudev1:amd64 255.4-1ubuntu8 amd64 libudev shared library
ii libunibilium4:amd64 2.1.0-3 amd64 simple, self-contained terminfo library
ii libunistring5:amd64 1.1-2build1 amd64 Unicode string library for C
ii libunwind8:amd64 1.6.2-3build1 amd64 library to determine the call-chain of a program - runtime
ii liburing2:amd64 2.5-1build1 amd64 Linux kernel io_uring access library - shared library
ii libutempter0:amd64 1.2.1-3build1 amd64 privileged helper for utmp/wtmp updates (runtime)
ii libuuid1:amd64 2.39.3-9ubuntu6 amd64 Universally Unique ID library
@ -330,6 +332,7 @@ ii rsync 3.2.7-1ubuntu1 amd64
ii screen 4.9.1-1build1 amd64 terminal multiplexer with VT100/ANSI terminal emulation
ii sed 4.9-2build1 amd64 GNU stream editor for filtering/transforming text
ii sensible-utils 0.0.22 all Utilities for sensible alternative selection
ii strace 6.8-0ubuntu2 amd64 System call tracer
ii sysstat 12.6.1-2 amd64 system performance tools for Linux
ii sysvinit-utils 3.08-6ubuntu3 amd64 System-V-like utilities
ii tar 1.35+dfsg-3build1 amd64 GNU version of the tar archiving utility
@ -340,6 +343,7 @@ ii ucf 3.0043+nmu1 all
ii universal-ctags 5.9.20210829.0-1 amd64 build tag file indexes of source code definitions
ii unzip 6.0-28ubuntu4 amd64 De-archiver for .zip files
ii util-linux 2.39.3-9ubuntu6 amd64 miscellaneous system utilities
ii valgrind 1:3.22.0-0ubuntu3 amd64 instrumentation framework for building dynamic analysis tools
ii wget 1.21.4-1ubuntu4 amd64 retrieves files from the web
ii xz-utils 5.6.1+really5.4.5-1 amd64 XZ-format compression utilities
ii yash 2.55-2 amd64 yet another shell
@ -357,7 +361,8 @@ ii zstd 1.5.5+dfsg2-2build1 amd64
||/ Name Version Architecture Description
Packages sorted by Installed-Size:
874252 ===== TOTAL (351 packages)
964659 ===== TOTAL (355 packages)
76943 valgrind amd64
72249 gcc-13-x86-64-linux-gnu amd64
37841 g++-13-x86-64-linux-gnu amd64
36493 cmake amd64
@ -378,6 +383,7 @@ Packages sorted by Installed-Size:
12003 libc6-x32 amd64
11992 libc6-i386 amd64
11569 binutils-x86-64-linux-gnu amd64
11174 libc6-dbg amd64
10984 cmake-data all
10764 libasan8 amd64
9665 libx32asan8 amd64
@ -438,6 +444,7 @@ Packages sorted by Installed-Size:
2191 ht amd64
2182 libc-bin amd64
2161 git-man all
2103 strace amd64
2078 libxml2 amd64
2032 libisl23 amd64
2009 universal-ctags amd64
@ -586,6 +593,7 @@ Packages sorted by Installed-Size:
197 libgcc-s1 amd64
196 libxcb1 amd64
196 libelf1t64 amd64
187 libunwind8 amd64
187 dash amd64
185 libgpg-error0 amd64
184 libx32gcc-s1 amd64

View File

@ -66,13 +66,14 @@ set_cmake_bool_vars() {
}
set -x
if [[ "${emu[0]}" == *valgrind* ]]; then true; # valgrind is SLOW
[[ -n $UPX_CONFIG_DISABLE_EXHAUSTIVE_TESTS ]] || UPX_CONFIG_DISABLE_EXHAUSTIVE_TESTS=ON
fi
set_cmake_bool_vars OFF UPX_CONFIG_DISABLE_SELF_PACK_TEST
set_cmake_bool_vars OFF UPX_CONFIG_DISABLE_EXHAUSTIVE_TESTS
set_cmake_bool_vars OFF UPX_CONFIG_DISABLE_RUN_UNPACKED_TEST
set_cmake_bool_vars OFF UPX_CONFIG_DISABLE_RUN_PACKED_TEST
if [[ "${emu[0]}" == *valgrind* ]]; then # valgrind is SLOW
set_cmake_bool_vars ON UPX_CONFIG_DISABLE_EXHAUSTIVE_TESTS
else
set_cmake_bool_vars OFF UPX_CONFIG_DISABLE_EXHAUSTIVE_TESTS
fi
export UPX="--prefer-ucl --no-color --no-progress"
export UPX_DEBUG_DISABLE_GITREV_WARNING=1

View File

@ -250,39 +250,85 @@ struct Z2_X2 : public X2 {
// util
**************************************************************************/
TEST_CASE("upx::atomic_exchange") {
{
upx_int8_t x = -1;
upx_int8_t y = upx::atomic_exchange(&x, (upx_int8_t) 2);
CHECK_EQ(x, 2);
CHECK_EQ(y, -1);
}
{
upx_int16_t x = -1;
upx_int16_t y = upx::atomic_exchange(&x, (upx_int16_t) 2);
CHECK_EQ(x, 2);
CHECK_EQ(y, -1);
}
{
upx_int32_t x = -1;
upx_int32_t y = upx::atomic_exchange(&x, (upx_int32_t) 2);
CHECK_EQ(x, 2);
CHECK_EQ(y, -1);
}
{
size_t x = (size_t) 0 - 1;
size_t y = upx::atomic_exchange(&x, (size_t) 2);
CHECK_EQ(x, 2);
CHECK_EQ(y, (size_t) 0 - 1);
}
{
int dummy = -1;
int *x = &dummy;
int *y = upx::atomic_exchange(&x, (int *) nullptr);
CHECK_EQ(x, nullptr);
CHECK_EQ(y, &dummy);
CHECK_EQ(dummy, -1);
}
}
TEST_CASE("upx::ObjectDeleter 1") {
LE16 *o = nullptr; // object
LE32 *a = nullptr; // array
LE64 *m = nullptr; // malloc
{
upx::ObjectDeleter<LE16 **> o_deleter{&o, 1};
auto o_deleter = upx::ObjectDeleter(&o, 1);
o = new LE16;
assert(o != nullptr);
upx::ArrayDeleter<LE32 **> a_deleter{&a, 1};
auto a_deleter = upx::ArrayDeleter(&a, 1);
a = New(LE32, 1);
assert(a != nullptr);
auto m_deleter = upx::MallocDeleter(&m, 1);
m = (LE64 *) ::malloc(sizeof(LE64));
assert(m != nullptr);
}
assert(o == nullptr);
assert(a == nullptr);
assert(m == nullptr);
// test "const" versions
{
const upx::ObjectDeleter<LE16 **const> o_deleter{&o, 1};
const auto o_deleter = upx::ObjectDeleter(&o, 1);
o = new LE16;
assert(o != nullptr);
const upx::ArrayDeleter<LE32 **const> a_deleter{&a, 1};
const auto a_deleter = upx::ArrayDeleter(&a, 1);
a = New(LE32, 1);
assert(a != nullptr);
const auto m_deleter = upx::MallocDeleter(&m, 1);
m = (LE64 *) ::malloc(sizeof(LE64));
assert(m != nullptr);
}
assert(o == nullptr);
assert(a == nullptr);
assert(m == nullptr);
}
TEST_CASE("upx::ObjectDeleter 2") {
constexpr size_t N = 2;
BE16 *o[N]; // multiple objects
BE32 *a[N]; // multiple arrays
BE64 *m[N]; // multiple mallocs
{
upx::ObjectDeleter<BE16 **> o_deleter{o, 0};
upx::ArrayDeleter<BE32 **> a_deleter{a, 0};
auto o_deleter = upx::ObjectDeleter(o, 0);
auto a_deleter = upx::ArrayDeleter(a, 0);
auto m_deleter = upx::MallocDeleter(m, 0);
for (size_t i = 0; i < N; i++) {
o[i] = new BE16;
assert(o[i] != nullptr);
@ -290,11 +336,15 @@ TEST_CASE("upx::ObjectDeleter 2") {
a[i] = New(BE32, 1 + i);
assert(a[i] != nullptr);
a_deleter.count += 1;
m[i] = (BE64 *) ::malloc(sizeof(BE64));
assert(m[i] != nullptr);
m_deleter.count += 1;
}
}
for (size_t i = 0; i < N; i++) {
assert(o[i] == nullptr);
assert(a[i] == nullptr);
assert(m[i] == nullptr);
}
}

View File

@ -30,7 +30,7 @@
static unsigned hex(uchar c) { return (c & 0xf) + (c > '9' ? 9 : 0); }
static bool update_capacity(unsigned size, unsigned *capacity) {
static bool grow_capacity(unsigned size, unsigned *capacity) {
if (size < *capacity)
return false;
if (*capacity == 0)
@ -41,16 +41,20 @@ static bool update_capacity(unsigned size, unsigned *capacity) {
}
template <class T>
static noinline T **realloc_array(T **array, size_t capacity) {
static T **realloc_array(T **array, size_t capacity) may_throw {
assert_noexcept(capacity > 0);
// NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
void *p = realloc(array, mem_size(sizeof(T *), capacity));
assert_noexcept(p != nullptr);
return static_cast<T **>(p);
}
template <class T>
static noinline void free_array(T **array, size_t count) {
for (size_t i = 0; i < count; i++)
delete array[i];
static void free_array(T **array, size_t count) noexcept {
for (size_t i = 0; i < count; i++) {
T *item = upx::atomic_exchange(&array[i], (T *) nullptr);
delete item;
}
::free(array); // NOLINT(bugprone-multi-level-implicit-pointer-conversion)
}
@ -61,11 +65,11 @@ static noinline void free_array(T **array, size_t count) {
ElfLinker::Section::Section(const char *n, const void *i, unsigned s, unsigned a)
: name(nullptr), output(nullptr), size(s), offset(0), p2align(a), next(nullptr) {
name = strdup(n);
assert(name != nullptr);
assert_noexcept(name != nullptr);
input = ::malloc(s + 1);
assert(input != nullptr);
assert_noexcept(input != nullptr);
if (s != 0) {
assert(i != nullptr);
assert_noexcept(i != nullptr);
memcpy(input, i, s);
}
((char *) input)[s] = 0;
@ -83,8 +87,8 @@ ElfLinker::Section::~Section() noexcept {
ElfLinker::Symbol::Symbol(const char *n, Section *s, upx_uint64_t o)
: name(nullptr), section(s), offset(o) {
name = strdup(n);
assert(name != nullptr);
assert(section != nullptr);
assert_noexcept(name != nullptr);
assert_noexcept(section != nullptr);
}
ElfLinker::Symbol::~Symbol() noexcept { ::free(name); }
@ -96,7 +100,7 @@ ElfLinker::Symbol::~Symbol() noexcept { ::free(name); }
ElfLinker::Relocation::Relocation(const Section *s, unsigned o, const char *t, const Symbol *v,
upx_uint64_t a)
: section(s), offset(o), type(t), value(v), add(a) {
assert(section != nullptr);
assert_noexcept(section != nullptr);
}
/*************************************************************************
@ -150,7 +154,7 @@ void ElfLinker::init(const void *pdata_v, int plen, unsigned pxtra) {
input[inputlen] = 0; // NUL terminate
output_capacity = (inputlen ? (inputlen + pxtra) : 0x4000);
assert(output_capacity <= (1 << 16)); // LE16 l_info.l_size
assert(output_capacity < (1 << 16)); // LE16 l_info.l_size
output = New(byte, output_capacity);
outputlen = 0;
NO_printf("\nElfLinker::init %d @%p\n", output_capacity, output);
@ -180,8 +184,9 @@ void ElfLinker::init(const void *pdata_v, int plen, unsigned pxtra) {
}
void ElfLinker::preprocessSections(char *start, char const *end) {
assert_noexcept(nsections == 0);
char *nextl;
for (nsections = 0; start < end; start = 1 + nextl) {
for (; start < end; start = 1 + nextl) {
nextl = strchr(start, '\n');
assert(nextl != nullptr);
*nextl = '\0'; // a record is a line
@ -201,8 +206,9 @@ void ElfLinker::preprocessSections(char *start, char const *end) {
}
void ElfLinker::preprocessSymbols(char *start, char const *end) {
assert_noexcept(nsymbols == 0);
char *nextl;
for (nsymbols = 0; start < end; start = 1 + nextl) {
for (; start < end; start = 1 + nextl) {
nextl = strchr(start, '\n');
assert(nextl != nullptr);
*nextl = '\0'; // a record is a line
@ -237,9 +243,10 @@ void ElfLinker::preprocessSymbols(char *start, char const *end) {
}
void ElfLinker::preprocessRelocations(char *start, char const *end) {
assert_noexcept(nrelocations == 0);
Section *section = nullptr;
char *nextl;
for (nrelocations = 0; start < end; start = 1 + nextl) {
for (; start < end; start = 1 + nextl) {
nextl = strchr(start, '\n');
assert(nextl != nullptr);
*nextl = '\0'; // a record is a line
@ -314,13 +321,11 @@ ElfLinker::Section *ElfLinker::addSection(const char *sname, const void *sdata,
NO_printf("addSection: %s len=%d align=%d\n", sname, slen, p2align);
if (!sdata && (!strcmp("ABS*", sname) || !strcmp("UND*", sname)))
return nullptr;
if (update_capacity(nsections, &nsections_capacity))
sections = realloc_array(sections, nsections_capacity);
assert(sections);
assert(sname);
assert(sname[0]);
assert(sname && sname[0]);
assert(sname[strlen(sname) - 1] != ':');
assert(findSection(sname, false) == nullptr);
if (grow_capacity(nsections, &nsections_capacity))
sections = realloc_array(sections, nsections_capacity);
Section *sec = new Section(sname, sdata, slen, p2align);
sec->sort_id = nsections;
sections[nsections++] = sec;
@ -330,13 +335,11 @@ ElfLinker::Section *ElfLinker::addSection(const char *sname, const void *sdata,
ElfLinker::Symbol *ElfLinker::addSymbol(const char *name, const char *section,
upx_uint64_t offset) {
NO_printf("addSymbol: %s %s 0x%llx\n", name, section, offset);
if (update_capacity(nsymbols, &nsymbols_capacity))
symbols = realloc_array(symbols, nsymbols_capacity);
assert(symbols != nullptr);
assert(name);
assert(name[0]);
assert(name && name[0]);
assert(name[strlen(name) - 1] != ':');
assert(findSymbol(name, false) == nullptr);
if (grow_capacity(nsymbols, &nsymbols_capacity))
symbols = realloc_array(symbols, nsymbols_capacity);
Symbol *sym = new Symbol(name, findSection(section), offset);
symbols[nsymbols++] = sym;
return sym;
@ -344,9 +347,8 @@ ElfLinker::Symbol *ElfLinker::addSymbol(const char *name, const char *section,
ElfLinker::Relocation *ElfLinker::addRelocation(const char *section, unsigned off, const char *type,
const char *symbol, upx_uint64_t add) {
if (update_capacity(nrelocations, &nrelocations_capacity))
if (grow_capacity(nrelocations, &nrelocations_capacity))
relocations = realloc_array(relocations, nrelocations_capacity);
assert(relocations != nullptr);
Relocation *rel = new Relocation(findSection(section), off, type, findSymbol(symbol), add);
relocations[nrelocations++] = rel;
return rel;

View File

@ -512,7 +512,7 @@ void PeFile32::processRelocs() // pass1
infoWarning("skipping unsupported relocation type %d (%d)", ic, counts[ic]);
LE32 *fix[4];
upx::ArrayDeleter<LE32 **const> fix_deleter{fix, 0}; // don't leak memory
auto fix_deleter = upx::ArrayDeleter(fix, 0); // don't leak memory
for (unsigned ic = 0; ic <= IMAGE_REL_BASED_HIGHLOW; ic++) {
fix[ic] = New(LE32, counts[ic]);
fix_deleter.count += 1;
@ -614,7 +614,7 @@ void PeFile64::processRelocs() // pass1
infoWarning("skipping unsupported relocation type %d (%d)", ic, counts[ic]);
LE32 *fix[16];
upx::ArrayDeleter<LE32 **const> fix_deleter{fix, 0}; // don't leak memory
auto fix_deleter = upx::ArrayDeleter(fix, 0); // don't leak memory
for (unsigned ic = 0; ic < 16; ic++) {
fix[ic] = New(LE32, counts[ic]);
fix_deleter.count += 1;
@ -1926,7 +1926,7 @@ void PeFile::processResources(Resource *res) {
SPAN_S_VAR(byte, ores, oresources + res->dirsize());
char *keep_icons = nullptr; // icon ids in the first icon group
upx::ArrayDeleter<char **const> keep_icons_deleter{&keep_icons, 1}; // don't leak memory
const auto keep_icons_deleter = upx::ArrayDeleter(&keep_icons, 1); // don't leak memory
unsigned iconsin1stdir = 0;
if (opt->win32_pe.compress_icons == 2)
while (res->next()) // there is no rewind() in Resource

View File

@ -26,6 +26,7 @@
#pragma once
// #include <atomic>
// #include <cstddef>
// #include <new>
// #include <type_traits>
@ -186,37 +187,76 @@ forceinline Result ptr_static_cast(const From *ptr) noexcept {
return static_cast<Result>(static_cast<const void *>(ptr));
}
#if WITH_THREADS
// cast "T *" to "std::atomic<T> *"
template <class T>
forceinline std::atomic<T> *ptr_std_atomic_cast(T *ptr) noexcept {
// TODO later: make sure that this cast is indeed legal
std::atomic<T> *result = ptr_static_cast<std::atomic<T> *>(ptr);
static_assert(sizeof(*result) == sizeof(*ptr));
static_assert(alignof(*result) == alignof(*ptr));
return result;
}
#endif // WITH_THREADS
// atomic_exchange
template <class T>
forceinline T atomic_exchange(T *ptr, T new_value) noexcept {
static_assert(std::is_standard_layout_v<T>);
static_assert(std::is_trivially_copyable_v<T>);
#if __has_builtin(__atomic_exchange_n) && defined(__ATOMIC_SEQ_CST)
return __atomic_exchange_n(ptr, new_value, __ATOMIC_SEQ_CST);
#elif __has_builtin(__sync_swap)
return __sync_swap(ptr, new_value);
#elif WITH_THREADS
return std::atomic_exchange(ptr_std_atomic_cast(ptr), new_value);
#else
T old_value = *ptr;
*ptr = new_value;
return old_value;
#endif
}
// helper classes so we don't leak memory on exceptions
template <class T> // T is "SomeType **"
template <class T>
struct ObjectDeleter final {
static_assert(std::is_pointer_v<T>);
static_assert(std::is_pointer_v<std::remove_pointer_t<T> >);
T items; // public
size_t count; // public
T **items; // public
std::size_t count; // public
explicit ObjectDeleter(T **p, std::size_t n) noexcept : items(p), count(n) {}
~ObjectDeleter() noexcept { delete_items(); }
void delete_items() noexcept {
for (size_t i = 0; i < count; i++) {
auto item = items[i];
items[i] = nullptr;
for (std::size_t i = 0; i < count; i++) {
T *item = atomic_exchange(&items[i], (T *) nullptr);
delete item; // single object delete
}
}
};
template <class T> // T is "SomeType **"
template <class T>
struct ArrayDeleter final {
static_assert(std::is_pointer_v<T>);
static_assert(std::is_pointer_v<std::remove_pointer_t<T> >);
T items; // public
size_t count; // public
T **items; // public
std::size_t count; // public
explicit ArrayDeleter(T **p, std::size_t n) noexcept : items(p), count(n) {}
~ArrayDeleter() noexcept { delete_items(); }
void delete_items() noexcept {
for (size_t i = 0; i < count; i++) {
auto item = items[i];
items[i] = nullptr;
for (std::size_t i = 0; i < count; i++) {
T *item = atomic_exchange(&items[i], (T *) nullptr);
delete[] item; // array delete
}
}
};
template <class T>
struct MallocDeleter final {
T **items; // public
std::size_t count; // public
explicit MallocDeleter(T **p, std::size_t n) noexcept : items(p), count(n) {}
~MallocDeleter() noexcept { delete_items(); }
void delete_items() noexcept {
for (std::size_t i = 0; i < count; i++) {
T *item = atomic_exchange(&items[i], (T *) nullptr);
::free(item); // free memory from malloc()
}
}
};
class noncopyable {
protected:
@ -242,10 +282,10 @@ constexpr bool string_gt(const char *a, const char *b) { return string_lt(b, a);
constexpr bool string_le(const char *a, const char *b) { return !string_lt(b, a); }
constexpr bool string_ge(const char *a, const char *b) { return !string_lt(a, b); }
constexpr bool mem_eq(const char *a, const char *b, size_t n) {
constexpr bool mem_eq(const char *a, const char *b, std::size_t n) {
return n == 0 || (*a == *b && mem_eq(a + 1, b + 1, n - 1));
}
constexpr bool mem_eq(const unsigned char *a, const unsigned char *b, size_t n) {
constexpr bool mem_eq(const unsigned char *a, const unsigned char *b, std::size_t n) {
return n == 0 || (*a == *b && mem_eq(a + 1, b + 1, n - 1));
}
} // namespace compile_time

View File

@ -136,19 +136,19 @@ static_assert(sizeof(void *) == sizeof(long));
#include <mutex>
#endif
// sanitizers
// sanitizers: ASAN, MSAN, UBSAN
#if !defined(__SANITIZE_ADDRESS__) && defined(__has_feature)
#if __has_feature(address_sanitizer)
#if __has_feature(address_sanitizer) // ASAN
#define __SANITIZE_ADDRESS__ 1
#endif
#endif
#if !defined(__SANITIZE_MEMORY__) && defined(__has_feature)
#if __has_feature(memory_sanitizer)
#if __has_feature(memory_sanitizer) // MSAN
#define __SANITIZE_MEMORY__ 1
#endif
#endif
#if !defined(__SANITIZE_UNDEFINED_BEHAVIOR__) && defined(__has_feature)
#if __has_feature(undefined_behavior_sanitizer)
#if __has_feature(undefined_behavior_sanitizer) // UBSAN
#define __SANITIZE_UNDEFINED_BEHAVIOR__ 1
#endif
#endif