diff --git a/misc/analyze/clang-tidy/run-clang-tidy.py b/misc/analyze/clang-tidy/run-clang-tidy.py index 1e616b64..cebcbf74 100755 --- a/misc/analyze/clang-tidy/run-clang-tidy.py +++ b/misc/analyze/clang-tidy/run-clang-tidy.py @@ -389,6 +389,8 @@ def main(): clang_apply_replacements_binary = find_binary( args.clang_apply_replacements_binary, "clang-apply-replacements", build_path ) + + if args.fix or (yaml and args.export_fixes): tmpdir = tempfile.mkdtemp() # Load the database and extract all files. diff --git a/src/check/dt_check.cpp b/src/check/dt_check.cpp index 254fc3fa..5e3220d3 100644 --- a/src/check/dt_check.cpp +++ b/src/check/dt_check.cpp @@ -100,21 +100,6 @@ ACC_COMPILE_TIME_ASSERT_HEADER((std::is_same::value)) ACC_COMPILE_TIME_ASSERT_HEADER((std::is_same::value)) ACC_COMPILE_TIME_ASSERT_HEADER((std::is_same::value)) -ACC_COMPILE_TIME_ASSERT_HEADER((is_same_all_v) ) -ACC_COMPILE_TIME_ASSERT_HEADER((is_same_all_v) ) -ACC_COMPILE_TIME_ASSERT_HEADER((is_same_all_v) ) -ACC_COMPILE_TIME_ASSERT_HEADER((!is_same_all_v) ) -ACC_COMPILE_TIME_ASSERT_HEADER((!is_same_all_v) ) -ACC_COMPILE_TIME_ASSERT_HEADER((!is_same_all_v) ) - -ACC_COMPILE_TIME_ASSERT_HEADER((!is_same_any_v) ) -ACC_COMPILE_TIME_ASSERT_HEADER((is_same_any_v) ) -ACC_COMPILE_TIME_ASSERT_HEADER((is_same_any_v) ) -ACC_COMPILE_TIME_ASSERT_HEADER((is_same_any_v) ) -ACC_COMPILE_TIME_ASSERT_HEADER((!is_same_any_v) ) -ACC_COMPILE_TIME_ASSERT_HEADER((!is_same_any_v) ) -ACC_COMPILE_TIME_ASSERT_HEADER((!is_same_any_v) ) - ACC_COMPILE_TIME_ASSERT_HEADER(no_bswap16(0x04030201) == 0x0201) ACC_COMPILE_TIME_ASSERT_HEADER(no_bswap32(0x04030201) == 0x04030201) ACC_COMPILE_TIME_ASSERT_HEADER(no_bswap64(0x0807060504030201ull) == 0x0807060504030201ull) @@ -128,43 +113,6 @@ ACC_COMPILE_TIME_ASSERT_HEADER(bswap64(bswap64(0xf8f7f6f5f4f3f2f1ull)) == no_bswap64(0xf8f7f6f5f4f3f2f1ull)) #endif -ACC_COMPILE_TIME_ASSERT_HEADER(usizeof(int) == sizeof(int)) -ACC_COMPILE_TIME_ASSERT_HEADER(usizeof('a') == sizeof(char)) -ACC_COMPILE_TIME_ASSERT_HEADER(usizeof("") == 1) -ACC_COMPILE_TIME_ASSERT_HEADER(usizeof("a") == 2) -ACC_COMPILE_TIME_ASSERT_HEADER(usizeof(0) == sizeof(int)) -ACC_COMPILE_TIME_ASSERT_HEADER(usizeof(0L) == sizeof(long)) -ACC_COMPILE_TIME_ASSERT_HEADER(usizeof(0LL) == sizeof(long long)) -ACC_COMPILE_TIME_ASSERT_HEADER(usizeof(nullptr) == sizeof(void *)) -ACC_COMPILE_TIME_ASSERT_HEADER(usizeof(sizeof(0)) == sizeof(size_t)) -ACC_COMPILE_TIME_ASSERT_HEADER(usizeof(usizeof(0)) == sizeof(unsigned)) - -ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_len("") == 0) -ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_len("a") == 1) -ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_len("ab") == 2) -ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_len("abc") == 3) - -ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_eq("", "")) -ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_eq("a", "")) -ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_eq("", "a")) -ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_eq("abc", "abc")) -ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_eq("ab", "abc")) -ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_eq("abc", "ab")) - -ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_lt("", "")) -ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_lt("a", "")) -ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_lt("", "a")) -ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_lt("abc", "abc")) -ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_lt("ab", "abc")) -ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_lt("abc", "ab")) -ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_lt("abc", "aba")) -ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_lt("abc", "abz")) - -ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_ne("abc", "abz")) -ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_gt("abc", "abz")) -ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_ge("abc", "abz")) -ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_le("abc", "abz")) - ACC_COMPILE_TIME_ASSERT_HEADER(CHAR_BIT == 8) #if 0 // does not work with MSVC #if '\0' - 1 < 0 @@ -594,20 +542,6 @@ TEST_CASE("assert_noexcept") { assert_noexcept(!ptr3); } -TEST_CASE("noncopyable") { - struct Test : private noncopyable { - int v = 1; - }; - Test t = {}; - CHECK(t.v == 1); -#if (ACC_CC_MSC) // MSVC thinks that Test is not std::is_trivially_copyable; true or compiler bug? - t.v = 0; -#else - mem_clear(&t); -#endif - CHECK(t.v == 0); -} - TEST_CASE("acc_vget") { CHECK_EQ(acc_vget_int(0, 0), 0); CHECK_EQ(acc_vget_long(1, -1), 1); diff --git a/src/check/dt_cxxlib.cpp b/src/check/dt_cxxlib.cpp new file mode 100644 index 00000000..dd36a38c --- /dev/null +++ b/src/check/dt_cxxlib.cpp @@ -0,0 +1,186 @@ +/* dt_cxxlib.cpp -- doctest check + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2023 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + UPX and the UCL library are free software; you can redistribute them + and/or modify them under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer + + */ + +#include "../conf.h" + +/************************************************************************* +// compile-time checks +**************************************************************************/ + +ACC_COMPILE_TIME_ASSERT_HEADER((upx::is_same_all_v) ) +ACC_COMPILE_TIME_ASSERT_HEADER((upx::is_same_all_v) ) +ACC_COMPILE_TIME_ASSERT_HEADER((upx::is_same_all_v) ) +ACC_COMPILE_TIME_ASSERT_HEADER((!upx::is_same_all_v) ) +ACC_COMPILE_TIME_ASSERT_HEADER((!upx::is_same_all_v) ) +ACC_COMPILE_TIME_ASSERT_HEADER((!upx::is_same_all_v) ) + +ACC_COMPILE_TIME_ASSERT_HEADER((!upx::is_same_any_v) ) +ACC_COMPILE_TIME_ASSERT_HEADER((upx::is_same_any_v) ) +ACC_COMPILE_TIME_ASSERT_HEADER((upx::is_same_any_v) ) +ACC_COMPILE_TIME_ASSERT_HEADER((upx::is_same_any_v) ) +ACC_COMPILE_TIME_ASSERT_HEADER((!upx::is_same_any_v) ) +ACC_COMPILE_TIME_ASSERT_HEADER((!upx::is_same_any_v) ) +ACC_COMPILE_TIME_ASSERT_HEADER((!upx::is_same_any_v) ) + +ACC_COMPILE_TIME_ASSERT_HEADER(usizeof(int) == sizeof(int)) +ACC_COMPILE_TIME_ASSERT_HEADER(usizeof('a') == sizeof(char)) +ACC_COMPILE_TIME_ASSERT_HEADER(usizeof("") == 1) +ACC_COMPILE_TIME_ASSERT_HEADER(usizeof("a") == 2) +ACC_COMPILE_TIME_ASSERT_HEADER(usizeof(0) == sizeof(int)) +ACC_COMPILE_TIME_ASSERT_HEADER(usizeof(0L) == sizeof(long)) +ACC_COMPILE_TIME_ASSERT_HEADER(usizeof(0LL) == sizeof(long long)) +ACC_COMPILE_TIME_ASSERT_HEADER(usizeof(nullptr) == sizeof(void *)) +ACC_COMPILE_TIME_ASSERT_HEADER(usizeof(sizeof(0)) == sizeof(size_t)) +ACC_COMPILE_TIME_ASSERT_HEADER(usizeof(usizeof(0)) == sizeof(unsigned)) + +namespace compile_time = upx::compile_time; +ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_len("") == 0) +ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_len("a") == 1) +ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_len("ab") == 2) +ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_len("abc") == 3) + +ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_eq("", "")) +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_eq("a", "")) +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_eq("", "a")) +ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_eq("abc", "abc")) +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_eq("ab", "abc")) +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_eq("abc", "ab")) + +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_lt("", "")) +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_lt("a", "")) +ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_lt("", "a")) +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_lt("abc", "abc")) +ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_lt("ab", "abc")) +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_lt("abc", "ab")) +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_lt("abc", "aba")) +ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_lt("abc", "abz")) + +ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_ne("abc", "abz")) +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_gt("abc", "abz")) +ACC_COMPILE_TIME_ASSERT_HEADER(!compile_time::string_ge("abc", "abz")) +ACC_COMPILE_TIME_ASSERT_HEADER(compile_time::string_le("abc", "abz")) + +/************************************************************************* +// util +**************************************************************************/ + +TEST_CASE("noncopyable") { + struct Test : private upx::noncopyable { + int v = 1; + }; + Test t = {}; + CHECK(t.v == 1); +#if (ACC_CC_MSC) // MSVC thinks that Test is not std::is_trivially_copyable; true or compiler bug? + t.v = 0; +#else + mem_clear(&t); +#endif + CHECK(t.v == 0); +} + +/************************************************************************* +// TriBool +**************************************************************************/ + +namespace { +template +struct TestTriBool { + static void test(bool expect_true, int x) noexcept { + static_assert(std::is_class::value); + static_assert(std::is_nothrow_default_constructible::value); + static_assert(std::is_nothrow_destructible::value); + static_assert(std::is_standard_layout::value); + static_assert(std::is_trivially_copyable::value); + static_assert(T(false) == T::False); + static_assert(T(true) == T::True); + static_assert(T(T::False) == T::False); + static_assert(T(T::True) == T::True); + static_assert(T(T::Third) == T::Third); + constexpr T array[] = {false, true, T::Third}; + static_assert(array[0].isStrictFalse()); + static_assert(array[1].isStrictTrue()); + static_assert(array[2].isThird()); + T a; + CHECK(!a); + CHECK(a.isStrictFalse()); + CHECK(!a.isStrictTrue()); + CHECK(a.isStrictBool()); + CHECK(!a.isThird()); + a = false; + CHECK(!a); + CHECK(a.isStrictFalse()); + CHECK(!a.isStrictTrue()); + CHECK(a.isStrictBool()); + CHECK(!a.isThird()); + a = true; + CHECK(a); + CHECK(!a.isStrictFalse()); + CHECK(a.isStrictTrue()); + CHECK(a.isStrictBool()); + CHECK(!a.isThird()); + a = T::Third; + if (expect_true) + CHECK(a); + else + CHECK(!a); + CHECK(!a.isStrictFalse()); + CHECK(!a.isStrictTrue()); + CHECK(!a.isStrictBool()); + CHECK(a.isThird()); + a = x; + if (expect_true) + CHECK(a); + else + CHECK(!a); + CHECK(!a.isStrictFalse()); + CHECK(!a.isStrictTrue()); + CHECK(!a.isStrictBool()); + CHECK(a.isThird()); + mem_clear(&a); + CHECK(a.isStrictFalse()); + } +}; +} // namespace + +TEST_CASE("TriBool") { + using upx::TriBool, upx::tribool; + // + static_assert(!tribool(false)); + static_assert(tribool(true)); + static_assert(!tribool(tribool::Third)); + TestTriBool::test(false, -1); + // + TestTriBool >::test(false, -1); + TestTriBool >::test(false, -1); + // + TestTriBool >::test(true, 2); + TestTriBool >::test(true, 2); + TestTriBool >::test(true, 2); + TestTriBool >::test(true, 2); + TestTriBool >::test(true, 2); +} + +/* vim:set ts=4 sw=4 et: */ diff --git a/src/conf.h b/src/conf.h index 3e240d5b..266f585b 100644 --- a/src/conf.h +++ b/src/conf.h @@ -34,6 +34,9 @@ #include "headers.h" #include "version.h" +// reserve name "upx" for namespace +namespace upx {} + ACC_COMPILE_TIME_ASSERT_HEADER(CHAR_BIT == 8) ACC_COMPILE_TIME_ASSERT_HEADER(sizeof(short) == 2) ACC_COMPILE_TIME_ASSERT_HEADER(sizeof(int) == 4) @@ -85,30 +88,12 @@ inline void upx_std_call_once(upx_std_once_flag &flag, NoexceptCallable &&f) { } #endif // WITH_THREADS -// upx_std_is_bounded_array: same as C++20 std::is_bounded_array -template -struct upx_std_is_bounded_array : public std::false_type {}; -template -struct upx_std_is_bounded_array : public std::true_type {}; -template -inline constexpr bool upx_std_is_bounded_array_v = upx_std_is_bounded_array::value; - // upx_is_integral is overloaded for BE16 & friends; see bele.h template struct upx_is_integral : public std::is_integral {}; template inline constexpr bool upx_is_integral_v = upx_is_integral::value; -// util: is_same_all and is_same_any means std::is_same for multiple types -template -struct is_same_all : public std::conjunction...> {}; -template -inline constexpr bool is_same_all_v = is_same_all::value; -template -struct is_same_any : public std::disjunction...> {}; -template -inline constexpr bool is_same_any_v = is_same_any::value; - #if (ACC_ARCH_M68K && ACC_OS_TOS && ACC_CC_GNUC) && defined(__MINT__) // horrible hack for broken compiler #define upx_fake_alignas_1 __attribute__((__aligned__(1),__packed__)) @@ -379,13 +364,6 @@ inline const T& UPX_MAX(const T& a, const T& b) { if (a < b) return b; return a; template inline const T& UPX_MIN(const T& a, const T& b) { if (a < b) return a; return b; } -template -struct UnsignedSizeOf { - static_assert(Size >= 1 && Size <= UPX_RSIZE_MAX_MEM); - static constexpr unsigned value = unsigned(Size); -}; -#define usizeof(expr) (UnsignedSizeOf::value) - template inline void mem_clear(T *object) noexcept { static_assert(std::is_class_v); // UPX convention @@ -427,42 +405,14 @@ noinline void throwAssertFailed(const char *expr, const char *file, int line, co #define assert_noexcept assert #endif -class noncopyable { -protected: - inline noncopyable() noexcept {} - inline ~noncopyable() noexcept = default; -private: - noncopyable(const noncopyable &) noexcept DELETED_FUNCTION; // copy constructor - noncopyable& operator=(const noncopyable &) noexcept DELETED_FUNCTION; // copy assignment - noncopyable(noncopyable &&) noexcept DELETED_FUNCTION; // move constructor - noncopyable& operator=(noncopyable &&) noexcept DELETED_FUNCTION; // move assignment -}; +#include "util/cxxlib.h" +using upx::is_same_any_v; +using upx::noncopyable; +using upx::tribool; +using upx::OptVar; +#define usizeof(expr) (upx::UnsignedSizeOf::value) -namespace compile_time { -constexpr size_t string_len(const char *a) { - return *a == '\0' ? 0 : 1 + string_len(a + 1); -} -constexpr bool string_eq(const char *a, const char *b) { - return *a == *b && (*a == '\0' || string_eq(a + 1, b + 1)); -} -constexpr bool string_lt(const char *a, const char *b) { - return (uchar)*a < (uchar)*b || (*a != '\0' && *a == *b && string_lt(a + 1, b + 1)); -} -constexpr bool string_ne(const char *a, const char *b) { - return !string_eq(a, b); -} -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); -} -} // namespace compile_time - /************************************************************************* // constants **************************************************************************/ @@ -632,52 +582,6 @@ struct upx_callback_t { // compression - config_t **************************************************************************/ -template -struct OptVar final { - static_assert(std::is_integral_v); - typedef T value_type; - static constexpr T default_value = default_value_; - static constexpr T min_value = min_value_; - static constexpr T max_value = max_value_; - static_assert(min_value <= default_value && default_value <= max_value); - - static void assertValue(const T &v) noexcept { - // info: this generates annoying warnings "unsigned >= 0 is always true" - //assert_noexcept(v >= min_value); - assert_noexcept(v == min_value || v >= min_value + 1); - assert_noexcept(v <= max_value); - } - void assertValue() const noexcept { - assertValue(v); - } - - OptVar() noexcept : v(default_value), is_set(false) { } - OptVar& operator= (const T &other) noexcept { - assertValue(other); - v = other; - is_set = true; - return *this; - } - - void reset() noexcept { v = default_value; is_set = false; } - operator T () const noexcept { return v; } - - T v; - bool is_set; -}; - - -// optional assignments -template -inline void oassign(OptVar &self, const OptVar &other) noexcept { - if (other.is_set) { self.v = other.v; self.is_set = true; } -} -template -inline void oassign(T &v, const OptVar &other) noexcept { - if (other.is_set) { v = other.v; } -} - - struct lzma_compress_config_t { typedef OptVar pos_bits_t; // pb typedef OptVar lit_pos_bits_t; // lp diff --git a/src/filter.h b/src/filter.h index cb826d89..435d6c43 100644 --- a/src/filter.h +++ b/src/filter.h @@ -96,7 +96,7 @@ private: // This class is private to Filter - don't look. **************************************************************************/ -class FilterImpl { +class FilterImpl final { friend class Filter; private: diff --git a/src/main.cpp b/src/main.cpp index 72d6ce46..87de79cb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -680,7 +680,7 @@ static int do_option(int optc, const char *arg) { case 632: opt->win32_pe.compress_resources = 1; if (mfx_optarg && mfx_optarg[0]) { - int value; + int value = 0; getoptvar(&value, 0, 1, arg); opt->win32_pe.compress_resources = bool(value); } diff --git a/src/options.h b/src/options.h index cce2894e..8aa3863e 100644 --- a/src/options.h +++ b/src/options.h @@ -163,8 +163,8 @@ struct Options final { struct { int compress_exports; int compress_icons; - TriBool compress_resources; - TriBool compress_rt[25]; // 25 == RT_LAST + upx::TriBool compress_resources; + upx::TriBool compress_rt[25]; // 25 == RT_LAST int strip_relocs; const char *keep_resource; } win32_pe; diff --git a/src/packer.cpp b/src/packer.cpp index bc8d24d6..1a5a7eb4 100644 --- a/src/packer.cpp +++ b/src/packer.cpp @@ -44,8 +44,8 @@ PackerBase::PackerBase(InputFile *f) : fi(f), file_size(f ? f->st.st_size : 0) { Packer::Packer(InputFile *f) : PackerBase(f) { uip = new UiPacker(this); } Packer::~Packer() noexcept { - owner_delete(uip); - owner_delete(linker); + upx::owner_delete(uip); + upx::owner_delete(linker); assert_noexcept(linker == nullptr); } @@ -778,7 +778,7 @@ static const char *getIdentstr(unsigned *size, int small) { } void Packer::initLoader(const void *pdata, int plen, int small, int pextra) { - owner_delete(linker); + upx::owner_delete(linker); linker = newLinker(); assert(bele == linker->bele); linker->init(pdata, plen, pextra); diff --git a/src/packmast.cpp b/src/packmast.cpp index b3acb13c..60b55c64 100644 --- a/src/packmast.cpp +++ b/src/packmast.cpp @@ -40,8 +40,8 @@ #include "p_com.h" #include "p_djgpp2.h" #include "p_exe.h" -#include "p_lx_exc.h" #include "p_lx_elf.h" +#include "p_lx_exc.h" #include "p_lx_interp.h" #include "p_lx_sh.h" #include "p_mach.h" @@ -54,8 +54,8 @@ #include "p_w32pe_i386.h" #include "p_w64pe_amd64.h" #include "p_w64pe_arm64.h" -#include "p_wince_arm.h" #include "p_wcle.h" +#include "p_wince_arm.h" /************************************************************************* // @@ -74,7 +74,7 @@ PackMaster::PackMaster(InputFile *f, Options *o) noexcept : fi(f) { } PackMaster::~PackMaster() noexcept { - owner_delete(packer); + upx::owner_delete(packer); // restore global options if (saved_opt != nullptr) { #if WITH_THREADS @@ -101,7 +101,7 @@ static tribool try_can_pack(PackerBase *pb, void *user) may_throw { f->seek(0, SEEK_SET); return true; // success } - if (r.isOther()) // aka "-1" + if (r.isThird()) // aka "-1" return r; // canPack() says the format is recognized and we should fail early } catch (const IOException &) { // ignored @@ -119,7 +119,7 @@ static tribool try_can_unpack(PackerBase *pb, void *user) may_throw { f->seek(0, SEEK_SET); return true; // success } - if (r.isOther()) // aka "-1" + if (r.isThird()) // aka "-1" return r; // canUnpack() says the format is recognized and we should fail early } catch (const IOException &) { // ignored @@ -145,7 +145,7 @@ PackerBase *PackMaster::visitAllPackers(visit_func_t func, InputFile *f, const O tribool r = func(pb.get(), user); \ if (r) \ return pb.release(); /* success */ \ - if (r.isOther()) \ + if (r.isThird()) \ return nullptr; /* stop and fail early */ \ ACC_BLOCK_END diff --git a/src/pefile.cpp b/src/pefile.cpp index 46cfa8a2..e9ed8add 100644 --- a/src/pefile.cpp +++ b/src/pefile.cpp @@ -1795,14 +1795,14 @@ void PeFile::processResources(Resource *res) { return; // setup default options for resource compression - if (opt->win32_pe.compress_resources.isOther()) + if (opt->win32_pe.compress_resources.isThird()) opt->win32_pe.compress_resources = !isefi; if (!opt->win32_pe.compress_resources) { opt->win32_pe.compress_icons = false; for (int i = 0; i < RT_LAST; i++) opt->win32_pe.compress_rt[i] = false; } - if (opt->win32_pe.compress_rt[RT_STRING].isOther()) { + if (opt->win32_pe.compress_rt[RT_STRING].isThird()) { // by default, don't compress RT_STRINGs of screensavers (".scr") opt->win32_pe.compress_rt[RT_STRING] = true; if (fn_has_ext(fi->getName(), "scr")) diff --git a/src/ui.cpp b/src/ui.cpp index ff471244..d1233ae0 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -186,7 +186,7 @@ UiPacker::UiPacker(const PackerBase *pb_) : pb(pb_) { UiPacker::~UiPacker() noexcept { cb.reset(); // owner - owner_delete(s); + upx::owner_delete(s); } /************************************************************************* diff --git a/src/util/cxxlib.h b/src/util/cxxlib.h new file mode 100644 index 00000000..21cd1be4 --- /dev/null +++ b/src/util/cxxlib.h @@ -0,0 +1,256 @@ +/* cxxlib.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2023 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + UPX and the UCL library are free software; you can redistribute them + and/or modify them under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer + + */ + +#pragma once + +// #include +// #include + +namespace upx { + +/************************************************************************* +// type_traits +**************************************************************************/ + +// is_bounded_array: same as C++20 std::is_bounded_array +template +struct is_bounded_array : public std::false_type {}; +template +struct is_bounded_array : public std::true_type {}; +template +inline constexpr bool is_bounded_array_v = is_bounded_array::value; + +// util: is_same_all and is_same_any means std::is_same for multiple types +template +struct is_same_all : public std::conjunction...> {}; +template +inline constexpr bool is_same_all_v = is_same_all::value; +template +struct is_same_any : public std::disjunction...> {}; +template +inline constexpr bool is_same_any_v = is_same_any::value; + +/************************************************************************* +// util +**************************************************************************/ + +template +struct UnsignedSizeOf { + static_assert(Size >= 1 && Size <= UPX_RSIZE_MAX_MEM); + static constexpr unsigned value = unsigned(Size); +}; + +class noncopyable { +protected: + inline noncopyable() noexcept {} + inline ~noncopyable() noexcept = default; +private: + noncopyable(const noncopyable &) noexcept DELETED_FUNCTION; // copy constructor + noncopyable &operator=(const noncopyable &) noexcept DELETED_FUNCTION; // copy assignment + noncopyable(noncopyable &&) noexcept DELETED_FUNCTION; // move constructor + noncopyable &operator=(noncopyable &&) noexcept DELETED_FUNCTION; // move assignment +}; + +namespace compile_time { +constexpr size_t string_len(const char *a) { return *a == '\0' ? 0 : 1 + string_len(a + 1); } +constexpr bool string_eq(const char *a, const char *b) { + return *a == *b && (*a == '\0' || string_eq(a + 1, b + 1)); +} +constexpr bool string_lt(const char *a, const char *b) { + return (uchar) *a < (uchar) *b || (*a != '\0' && *a == *b && string_lt(a + 1, b + 1)); +} +constexpr bool string_ne(const char *a, const char *b) { return !string_eq(a, b); } +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); } +} // namespace compile_time + +/************************************************************************* +// TriBool - tri-state bool +// an enum with an underlying type and 3 values +// bool() checks for > 0, so ThirdValue determines if Third is false or true +**************************************************************************/ + +template // ThirdValue is false by default +struct TriBool final { + // types + typedef T underlying_type; + static_assert(std::is_integral_v); + typedef decltype(T(0) + T(0)) promoted_type; + static_assert(std::is_integral_v); + static_assert(ThirdValue != 0 && ThirdValue != 1); + enum value_type : underlying_type { False = 0, True = 1, Third = ThirdValue }; + // constructors + forceinline constexpr TriBool() noexcept : value(False) {} + forceinline constexpr TriBool(value_type x) noexcept : value(x) {} + constexpr TriBool(promoted_type x) noexcept : value(x == 0 ? False : (x == 1 ? True : Third)) {} + forceinline ~TriBool() noexcept = default; + // access + constexpr value_type getValue() const noexcept { return value; } + // checks for > 0, so ThirdValue determines if Third is false (the default) or true + explicit constexpr operator bool() const noexcept { return value > False; } + // query; this is NOT the same as operator bool() + constexpr bool isStrictFalse() const noexcept { return value == False; } + constexpr bool isStrictTrue() const noexcept { return value == True; } + constexpr bool isStrictBool() const noexcept { return value == False || value == True; } + constexpr bool isThird() const noexcept { return value != False && value != True; } + constexpr bool operator==(TriBool other) const noexcept { return value == other.value; } + constexpr bool operator==(value_type other) const noexcept { return value == other; } + constexpr bool operator==(promoted_type other) const noexcept { return value == other; } + + // "third" can mean many things, depending on usage context, so provide some alternative names: + // constexpr bool isDefault() const noexcept { return isThird(); } // might be misleading + constexpr bool isIndeterminate() const noexcept { return isThird(); } + constexpr bool isOther() const noexcept { return isThird(); } + constexpr bool isUndecided() const noexcept { return isThird(); } + // constexpr bool isUnset() const noexcept { return isThird(); } // might be misleading + + // protected: + value_type value; +}; + +typedef TriBool<> tribool; + +/************************************************************************* +// OptVar and oassign +**************************************************************************/ + +template +struct OptVar final { + static_assert(std::is_integral_v); + typedef T value_type; + static constexpr T default_value = default_value_; + static constexpr T min_value = min_value_; + static constexpr T max_value = max_value_; + static_assert(min_value <= default_value && default_value <= max_value); + + static void assertValue(const T &value) noexcept { + // info: this generates annoying warnings "unsigned >= 0 is always true" + // assert_noexcept(value >= min_value); + assert_noexcept(value == min_value || value >= min_value + 1); + assert_noexcept(value <= max_value); + } + void assertValue() const noexcept { assertValue(value); } + + constexpr OptVar() noexcept : value(default_value), is_set(false) {} + OptVar &operator=(const T &other) noexcept { + assertValue(other); + value = other; + is_set = true; + return *this; + } + + void reset() noexcept { + value = default_value; + is_set = false; + } + constexpr operator T() const noexcept { return value; } + + // protected: + T value; + bool is_set; +}; + +// optional assignments +template +inline void oassign(OptVar &self, const OptVar &other) noexcept { + if (other.is_set) { + self.value = other.value; + self.is_set = true; + } +} +template +inline void oassign(T &v, const OptVar &other) noexcept { + if (other.is_set) { + v = other.value; + } +} + +/************************************************************************* +// OwningPointer(T) +// simple pointer type alias to explicitly mark ownership of objects; purely +// cosmetic to improve source code readability, no real functionality +**************************************************************************/ + +#if 0 + +// this works +#define OwningPointer(T) T * + +#elif !(DEBUG) + +// this also works +template +using OwningPointer = T *; +#define OwningPointer(T) upx::OwningPointer + +#else + +// also works: a trivial class with just a number of no-ops +template +struct OwningPointer final { + static_assert(std::is_class_v); // UPX convention + typedef typename std::add_lvalue_reference::type reference; + typedef typename std::add_lvalue_reference::type const_reference; + typedef typename std::add_pointer::type pointer; + typedef typename std::add_pointer::type const_pointer; + pointer ptr; + inline OwningPointer(pointer p) noexcept : ptr(p) {} + inline operator pointer() noexcept { return ptr; } + inline operator const_pointer() const noexcept { return ptr; } + inline reference operator*() noexcept { return *ptr; } + inline const_reference operator*() const noexcept { return *ptr; } + inline pointer operator->() noexcept { return ptr; } + inline const_pointer operator->() const noexcept { return ptr; } +}; +// must overload mem_clear() +template +inline void mem_clear(OwningPointer object) noexcept { + mem_clear((T *) object); +} +#define OwningPointer(T) upx::OwningPointer + +#endif + +template +inline void owner_delete(OwningPointer(T)(&object)) noexcept { + static_assert(std::is_class_v); // UPX convention + static_assert(std::is_nothrow_destructible_v); + if (object != nullptr) { + delete (T *) object; + object = nullptr; + } +} + +// disable some overloads +#if defined(__clang__) || __GNUC__ != 7 +template +inline void owner_delete(T (&array)[]) noexcept DELETED_FUNCTION; +#endif +template +inline void owner_delete(T (&array)[N]) noexcept DELETED_FUNCTION; + +} // namespace upx diff --git a/src/util/raw_bytes.h b/src/util/raw_bytes.h index c4676aa1..a0d07d43 100644 --- a/src/util/raw_bytes.h +++ b/src/util/raw_bytes.h @@ -35,7 +35,7 @@ // default: for any regular pointer, raw_bytes() is just the pointer itself template -inline typename std::enable_if::value && !upx_std_is_bounded_array::value && +inline typename std::enable_if::value && !upx::is_bounded_array::value && (upx_is_integral::type>::value || std::is_void::type>::value), T>::type @@ -52,7 +52,7 @@ raw_bytes(T ptr, size_t size_in_bytes) { // default: for any regular pointer, raw_index_bytes() is just "pointer + index" // NOTE: index == number of elements, *NOT* size in bytes! template -inline typename std::enable_if::value && !upx_std_is_bounded_array::value && +inline typename std::enable_if::value && !upx::is_bounded_array::value && upx_is_integral::type>::value, T>::type raw_index_bytes(T ptr, size_t index, size_t size_in_bytes) { diff --git a/src/util/util.cpp b/src/util/util.cpp index ae94684e..bfa6879e 100644 --- a/src/util/util.cpp +++ b/src/util/util.cpp @@ -787,66 +787,4 @@ TEST_CASE("get_ratio") { CHECK(get_ratio(2 * UPX_RSIZE_MAX, 1024ull * UPX_RSIZE_MAX) == 9999999); } -namespace { -template -struct TestTriBool { - static void test(bool expect_true, int x) noexcept { - CHECK(T(false) == T::False); - CHECK(T(true) == T::True); - CHECK(T(T::Other) == T::Other); - T a; - CHECK(!a); - CHECK(a.isStrictFalse()); - CHECK(!a.isStrictTrue()); - CHECK(a.isStrictBool()); - CHECK(!a.isOther()); - a = false; - CHECK(!a); - CHECK(a.isStrictFalse()); - CHECK(!a.isStrictTrue()); - CHECK(a.isStrictBool()); - CHECK(!a.isOther()); - a = true; - CHECK(a); - CHECK(!a.isStrictFalse()); - CHECK(a.isStrictTrue()); - CHECK(a.isStrictBool()); - CHECK(!a.isOther()); - a = T::Other; - if (expect_true) - CHECK(a); - else - CHECK(!a); - CHECK(!a.isStrictFalse()); - CHECK(!a.isStrictTrue()); - CHECK(!a.isStrictBool()); - CHECK(a.isOther()); - a = x; - if (expect_true) - CHECK(a); - else - CHECK(!a); - CHECK(!a.isStrictFalse()); - CHECK(!a.isStrictTrue()); - CHECK(!a.isStrictBool()); - CHECK(a.isOther()); - } -}; -} // namespace - -TEST_CASE("TriBool") { - static_assert(!tribool(false)); - static_assert(tribool(true)); - static_assert(!tribool(tribool::Other)); - TestTriBool::test(false, -1); - // - TestTriBool >::test(false, -1); - TestTriBool >::test(false, -1); - // - TestTriBool >::test(true, 2); - TestTriBool >::test(true, 2); - TestTriBool >::test(true, 2); - TestTriBool >::test(true, 2); -} - /* vim:set ts=4 sw=4 et: */ diff --git a/src/util/util.h b/src/util/util.h index 5c8c3852..3618a769 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -130,113 +130,6 @@ void upx_memswap(void *a, void *b, size_t n); void upx_stable_sort(void *array, size_t n, size_t element_size, int (*compare)(const void *, const void *)); -/************************************************************************* -// OwningPointer(T) -// simple pointer type alias to explicitly mark ownership of objects; purely -// cosmetic to improve source code readability, no real functionality -**************************************************************************/ - -#if 0 - -// this works -#define OwningPointer(T) T * - -#elif !(DEBUG) - -// this also works -template -using OwningPointer = T *; -#define OwningPointer(T) OwningPointer - -#else - -// also works: a trivial class with just a number of no-ops -template -struct OwningPointer final { - static_assert(std::is_class_v); // UPX convention - typedef typename std::add_lvalue_reference::type reference; - typedef typename std::add_lvalue_reference::type const_reference; - typedef typename std::add_pointer::type pointer; - typedef typename std::add_pointer::type const_pointer; - pointer ptr; - inline OwningPointer(pointer p) noexcept : ptr(p) {} - inline operator pointer() noexcept { return ptr; } - inline operator const_pointer() const noexcept { return ptr; } - inline reference operator*() noexcept { return *ptr; } - inline const_reference operator*() const noexcept { return *ptr; } - inline pointer operator->() noexcept { return ptr; } - inline const_pointer operator->() const noexcept { return ptr; } -}; -// must overload mem_clear() -template -inline void mem_clear(OwningPointer object) noexcept { - mem_clear((T *) object); -} -#define OwningPointer(T) OwningPointer - -#endif - -template -inline void owner_delete(OwningPointer(T)(&object)) noexcept { - static_assert(std::is_class_v); // UPX convention - static_assert(std::is_nothrow_destructible_v); - if (object != nullptr) { - delete (T *) object; - object = nullptr; - } -} - -// disable some overloads -#if defined(__clang__) || __GNUC__ != 7 -template -inline void owner_delete(T (&array)[]) noexcept DELETED_FUNCTION; -#endif -template -inline void owner_delete(T (&array)[N]) noexcept DELETED_FUNCTION; - -/************************************************************************* -// TriBool - tri-state bool -**************************************************************************/ - -template // an enum with an underlying type and 3 values -class TriBool { -public: - // types - typedef T underlying_type; - static_assert(std::is_integral_v); - typedef decltype(T(0) + T(0)) promoted_type; - static_assert(std::is_integral_v); - static_assert(TOther != 0 && TOther != 1); - enum value_type : underlying_type { False = 0, True = 1, Other = TOther }; - // constructors - forceinline constexpr TriBool() noexcept = default; - forceinline ~TriBool() noexcept = default; - constexpr TriBool(value_type x) noexcept : value(x) {} - constexpr TriBool(promoted_type x) noexcept : value(x == 0 ? False : (x == 1 ? True : Other)) {} - // access - constexpr value_type getValue() const noexcept { return value; } - // checks for > 0, so TOther determines if Other is false (the default) or true - explicit constexpr operator bool() const noexcept { return value > False; } - // query; this is NOT the same as operator bool() - constexpr bool isStrictFalse() const noexcept { return value == False; } - constexpr bool isStrictTrue() const noexcept { return value == True; } - constexpr bool isStrictBool() const noexcept { return value == False || value == True; } - constexpr bool isOther() const noexcept { return value != False && value != True; } - // "other" can mean many things, depending on usage context, so provide some alternative names: - // constexpr bool isDefault() const noexcept { return isOther(); } // might be misleading - constexpr bool isIndeterminate() const noexcept { return isOther(); } - constexpr bool isUndecided() const noexcept { return isOther(); } - // constexpr bool isUnset() const noexcept { return isOther(); } // might be misleading - constexpr bool operator==(TriBool other) const noexcept { return value == other.value; } - constexpr bool operator==(value_type other) const noexcept { return value == other; } - constexpr bool operator==(promoted_type other) const noexcept { return value == other; } -protected: - // value - value_type value = False; -}; - -typedef TriBool<> tribool; - /************************************************************************* // misc. support functions **************************************************************************/