From ca11bac6aef8d67679c8d14bfc143cd36278905d Mon Sep 17 00:00:00 2001 From: Eta <24918963+Eta0@users.noreply.github.com> Date: Sun, 16 Nov 2025 18:54:22 -0600 Subject: [PATCH 01/11] fix: Avoid violating strict aliasing rules in `LzFind.c` --- src/LzFind.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/LzFind.c b/src/LzFind.c index 98d0ac6e..3d514176 100755 --- a/src/LzFind.c +++ b/src/LzFind.c @@ -173,7 +173,8 @@ static unsigned short * GetMatches2(UInt32 lenLimit, UInt32 curMatch, UInt32 pos len0 = rle_len; } unsigned char ref_byte = *cur; - unsigned starter = *(unsigned*)(cur - 1); + uint32_t starter; + memcpy(&starter, cur - 1, sizeof(starter)); uint64_t starter_full = (uint64_t)starter + (((uint64_t)starter) << 32); const unsigned char* min = pos > 65535 ? cur - 32767 : cur - (pos - 32768); @@ -229,7 +230,10 @@ static unsigned short * GetMatches2(UInt32 lenLimit, UInt32 curMatch, UInt32 pos const unsigned char* _min = min; unsigned cnt = 0; if (_min < pb - (rle_len - len)) {_min = pb - (rle_len - len);} - while (rle_pos > _min && *(uint64_t*)(rle_pos - 8) == starter_full) {rle_pos-=8; cnt+=8;} + while (rle_pos > _min && memcmp(rle_pos - 8, &starter_full, sizeof(starter_full)) == 0) { + rle_pos-=8; + cnt+=8; + } if (cnt) { if (cnt + len > rle_len - 1) {cnt = rle_len - 1 - len;} curMatch_rle -= cnt; @@ -314,7 +318,7 @@ static void SkipMatches2(UInt32 *son, UInt32 _cyclicBufferPos) #ifdef __SSE4_2__ #include "nmmintrin.h" -#define HASH(cur) unsigned v = 0xffffff & *(const unsigned*)cur; UInt32 hashValue = _mm_crc32_u32(0, v) & LZFIND_HASH_MASK; +#define HASH(cur) unsigned v; memcpy(&v, cur, sizeof(v)); v &= 0xffffff; UInt32 hashValue = _mm_crc32_u32(0, v) & LZFIND_HASH_MASK; #else #define HASH(cur) UInt32 hashValue = ((cur[2] | ((UInt32)cur[0] << 8)) ^ crc[cur[1]]) & LZFIND_HASH_MASK; #endif From b5fbaad662dcdefc82b62e4c14b7530a52801baf Mon Sep 17 00:00:00 2001 From: Eta <24918963+Eta0@users.noreply.github.com> Date: Sun, 16 Nov 2025 20:07:06 -0600 Subject: [PATCH 02/11] style: Switch `sizeof` calculation to use `UInt32` alias for `unsigned` For consistency, since the thing being set is defined using UInt32. --- src/LzFind.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LzFind.c b/src/LzFind.c index 3d514176..25be1190 100755 --- a/src/LzFind.c +++ b/src/LzFind.c @@ -24,7 +24,7 @@ void MatchFinder_Create(CMatchFinder *p) } p->son = p->hash + LZFIND_HASH_SIZE; - memset(p->hash, 0, LZFIND_HASH_SIZE * sizeof(unsigned)); + memset(p->hash, 0, LZFIND_HASH_SIZE * sizeof(UInt32)); p->cyclicBufferPos = 0; p->pos = ZOPFLI_WINDOW_SIZE; } From 2485c128472029f0a81acca1e5acd7349565dd17 Mon Sep 17 00:00:00 2001 From: Eta <24918963+Eta0@users.noreply.github.com> Date: Sun, 16 Nov 2025 20:56:32 -0600 Subject: [PATCH 03/11] fix: Include `` in `main.h` explicitly --- src/main.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.h b/src/main.h index 85459ff3..f04f62d1 100755 --- a/src/main.h +++ b/src/main.h @@ -9,6 +9,7 @@ #include #include #include +#include #include //Compile support for folder input. Requires std::filesystem introduced in C++17. From 5aa1946722cf5218f4dc0bb17e376afced3d2e3e Mon Sep 17 00:00:00 2001 From: Eta <24918963+Eta0@users.noreply.github.com> Date: Sun, 16 Nov 2025 20:58:21 -0600 Subject: [PATCH 04/11] build: Enable interprocedural optimization for release builds --- src/CMakeLists.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 70e540d9..4277de79 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,19 @@ if(EXISTS "${CMAKE_SOURCE_DIR}/../.git" AND NOT EXISTS "${CMAKE_SOURCE_DIR}/../s message (FATAL_ERROR "Submodules are not initialized. Run \n\tgit submodule update --init --recursive\n within the repository") endif() +# Enable LTO for release builds +if(NOT CMAKE_BUILD_TYPE MATCHES Debug) + include(CheckIPOSupported) + check_ipo_supported(RESULT IPO_SUPPORTED OUTPUT IPO_ERROR) + + if(IPO_SUPPORTED) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + message(STATUS "IPO / LTO enabled") + else() + message(STATUS "IPO / LTO not supported: <${IPO_ERROR}>") + endif() +endif() + add_executable(ect main.cpp gztools.cpp From ab8138ed41f0163a8b3e06357d6a76526c87fc59 Mon Sep 17 00:00:00 2001 From: Eta <24918963+Eta0@users.noreply.github.com> Date: Sun, 16 Nov 2025 20:59:05 -0600 Subject: [PATCH 05/11] build: Ensure static linking with MinGW --- src/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4277de79..5ac6d2c5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -51,6 +51,11 @@ add_executable(ect::ect ALIAS ect) if(MINGW) add_compile_options($<$:-mno-ms-bitfields>) add_compile_options($<$:-mno-ms-bitfields>) + + # Ensure static linkage. + target_link_options(ect PRIVATE -static -static-libgcc -static-libstdc++) + set(CMAKE_LINK_SEARCH_START_STATIC TRUE) + set(CMAKE_LINK_SEARCH_END_STATIC TRUE) endif() if(NOT MSVC) From 59b6a334da5ca99d76ed692b256b3fba8b7e77bd Mon Sep 17 00:00:00 2001 From: Eta <24918963+Eta0@users.noreply.github.com> Date: Sun, 16 Nov 2025 21:01:22 -0600 Subject: [PATCH 06/11] build: Help subprojects find `zlib` when its binaries don't exist yet --- src/CMakeLists.txt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5ac6d2c5..1549f044 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -142,9 +142,16 @@ endif() # Enable ZLIB_CONST for everything add_definitions(-DZLIB_CONST) -# Ensure that zlib target points to our custom zlib zlib -set(ZLIB_ROOT "zlib/") -find_package(ZLIB) +# Set CMake variables so that other libraries like libpng and mozjpeg +# can find our custom zlib when calling find_package(ZLIB REQUIRED). +# find_package() fails (at configuration time) otherwise because the +# zlib static library object doesn't exist until build time. +set(ZLIB_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/zlib/") +set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/zlib") +# ZLIB_LIBRARY needs to be set to where the built library will end up +set(ZLIB_LIBRARY "zlib/${CMAKE_STATIC_LIBRARY_PREFIX}zlib${CMAKE_STATIC_LIBRARY_SUFFIX}") +# Now this check should pass +find_package(ZLIB REQUIRED) option(ECT_MULTITHREADING "Enable multithreaded processing support" ON) option(ECT_MP3_SUPPORT "Enable MP3 support (not currently working)" OFF) From 89a48eb97d6e7b97fdfb8899dd7c7db899be597b Mon Sep 17 00:00:00 2001 From: Eta <24918963+Eta0@users.noreply.github.com> Date: Sun, 16 Nov 2025 23:22:05 -0600 Subject: [PATCH 07/11] feat: Support Unicode paths and long paths in Windows builds --- src/CMakeLists.txt | 11 +++++++++++ src/ect.manifest | 16 ++++++++++++++++ src/ect.manifest.rc | 2 ++ 3 files changed, 29 insertions(+) create mode 100644 src/ect.manifest create mode 100644 src/ect.manifest.rc diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1549f044..51603203 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -63,6 +63,17 @@ if(NOT MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-sign-compare -Wno-unused -Wno-unused-parameter") endif() +if(WIN32) + # Add a manifest. On Windows, this will enable proper handling of Unicode paths, + # as well as paths longer than 260 characters. It also configures UAC handling. + target_sources(ect PRIVATE ect.manifest.rc) + + # Disable CMake's automatic manifest generation on MSVC since we provide our own. + if(MSVC) + target_link_options(ect PRIVATE "/MANIFEST:NO") + endif() +endif() + # Use -Ofast in release builds # TODO: This is now deprecated in clang and likely makes little difference, switch back to -O3. foreach(var CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO) diff --git a/src/ect.manifest b/src/ect.manifest new file mode 100644 index 00000000..966207b9 --- /dev/null +++ b/src/ect.manifest @@ -0,0 +1,16 @@ + + + + + + + + + + + + UTF-8 + true + + + diff --git a/src/ect.manifest.rc b/src/ect.manifest.rc new file mode 100644 index 00000000..2703c7c1 --- /dev/null +++ b/src/ect.manifest.rc @@ -0,0 +1,2 @@ +#include +1 RT_MANIFEST "ect.manifest" From f42973d89656b8696605747235a9344e3b239db3 Mon Sep 17 00:00:00 2001 From: Eta <24918963+Eta0@users.noreply.github.com> Date: Sun, 16 Nov 2025 23:47:17 -0600 Subject: [PATCH 08/11] fix: Restore MSVC support by avoiding incompatible attribute usage --- src/zopfli/match.h | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/zopfli/match.h b/src/zopfli/match.h index a88ff5ee..2870b5c7 100644 --- a/src/zopfli/match.h +++ b/src/zopfli/match.h @@ -17,13 +17,24 @@ */ #include +#if defined(__GNUC__) || defined(__clang__) + #define _RESTRICT __restrict__ +#elif defined(_MSC_VER) + #define _RESTRICT __restrict +#else + #define _RESTRICT +#endif + #ifdef __GNUC__ __attribute__ ((always_inline, hot)) +#elif defined(_MSC_VER) +__forceinline #endif -static inline const unsigned char* GetMatch(const unsigned char* __restrict__ scan, - const unsigned char* __restrict__ match, - const unsigned char* __restrict__ end +static inline const unsigned char* GetMatch(const unsigned char* _RESTRICT scan, + const unsigned char* _RESTRICT match, + const unsigned char* _RESTRICT end , const unsigned char* safe_end) { +#undef _RESTRICT #ifdef __GNUC__ /* Optimized Function based on cloudflare's zlib fork. * Note that this may read up to 15 bytes beyond end, From 41321b873569d97fdc961d88bca1503a1eb1cec1 Mon Sep 17 00:00:00 2001 From: Eta <24918963+Eta0@users.noreply.github.com> Date: Mon, 17 Nov 2025 00:25:22 -0600 Subject: [PATCH 09/11] fix: Detect `` availability better when compiling with MSVC --- src/main.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main.h b/src/main.h index f04f62d1..f84b63c3 100755 --- a/src/main.h +++ b/src/main.h @@ -13,10 +13,12 @@ #include //Compile support for folder input. Requires std::filesystem introduced in C++17. -#if __cplusplus >= 201703L +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L && _MSC_VER >= 1913) +#if __has_include() #define FS_SUPPORTED #include #endif +#endif struct ECTOptions{ unsigned Mode; From 7b9b64278cc1f735fe16218fed4342d18c5b442b Mon Sep 17 00:00:00 2001 From: Eta <24918963+Eta0@users.noreply.github.com> Date: Mon, 17 Nov 2025 00:36:42 -0600 Subject: [PATCH 10/11] feat: Support using "--" as an end-of-options marker Following Unix argument passing conventions, specifying "--" by itself as a command line argument signifies that every argument that follows should be treated as positional rather than as an option, even if it starts with a dash. This allows for easily passing filenames as arguments that have names beginning with dashes. --- src/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index ff1b2ddb..6f45d097 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -549,9 +549,11 @@ int main(int argc, const char * argv[]) { std::vector args; int files = 0; if (argc >= 2){ + bool positional_only_mode = false; for (int i = 1; i < argc; i++) { int strlen = strnlen(argv[i], 64); //File names may be longer and are unaffected by this check - if (strncmp(argv[i], "-", 1) != 0){ + if (!positional_only_mode && strcmp(argv[i], "--") == 0) {positional_only_mode = true;} + else if (positional_only_mode || strncmp(argv[i], "-", 1) != 0){ args.push_back(i); files++; } From 7c62f2a80f49bf2811228b8d9ffaaeed43036e0c Mon Sep 17 00:00:00 2001 From: Eta <24918963+Eta0@users.noreply.github.com> Date: Mon, 17 Nov 2025 03:04:58 -0600 Subject: [PATCH 11/11] style: Clarify a comment about manifest generation --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 51603203..4af39851 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -68,7 +68,7 @@ if(WIN32) # as well as paths longer than 260 characters. It also configures UAC handling. target_sources(ect PRIVATE ect.manifest.rc) - # Disable CMake's automatic manifest generation on MSVC since we provide our own. + # Disable MSVC's automatic manifest generation since we provide our own. if(MSVC) target_link_options(ect PRIVATE "/MANIFEST:NO") endif()