Version in base suite: 24.09+dfsg-8 Base version: 7zip_24.09+dfsg-8 Target version: 7zip_25.01+dfsg-1~deb13u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/7/7zip/7zip_24.09+dfsg-8.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/7/7zip/7zip_25.01+dfsg-1~deb13u1.dsc Asm/x86/Sort.asm | 860 +++++++ C/7zVersion.h | 10 C/BwtSort.c | 468 ++-- C/BwtSort.h | 7 C/Compiler.h | 12 C/CpuArch.h | 8 C/HuffEnc.c | 388 ++- C/HuffEnc.h | 8 C/LzFind.c | 26 C/LzFindMt.c | 10 C/LzFindMt.h | 6 C/Lzma2Enc.c | 4 C/Lzma2Enc.h | 1 C/LzmaEnc.c | 6 C/LzmaEnc.h | 4 C/MtCoder.c | 61 C/MtCoder.h | 7 C/Sha512.c | 167 + C/Sort.c | 355 ++- C/Sort.h | 7 C/Threads.c | 237 ++ C/Threads.h | 12 C/Util/Lzma/LzmaUtil.dsp | 4 C/Util/LzmaLib/LzmaLib.dsp | 8 C/Xz.h | 12 C/XzCrc64Opt.c | 4 C/XzDec.c | 29 C/XzEnc.c | 8 C/XzEnc.h | 3 C/XzIn.c | 265 +- CPP/7zip/7zip_gcc.mak | 6 CPP/7zip/Archive/7z/7zCompressionMode.h | 2 CPP/7zip/Archive/7z/7zHandlerOut.cpp | 30 CPP/7zip/Archive/ArHandler.cpp | 14 CPP/7zip/Archive/Bz2Handler.cpp | 8 CPP/7zip/Archive/ComHandler.cpp | 48 CPP/7zip/Archive/Common/HandlerOut.cpp | 8 CPP/7zip/Archive/Common/HandlerOut.h | 33 CPP/7zip/Archive/Common/ItemNameUtils.cpp | 35 CPP/7zip/Archive/Common/ItemNameUtils.h | 3 CPP/7zip/Archive/CpioHandler.cpp | 42 CPP/7zip/Archive/DmgHandler.cpp | 6 CPP/7zip/Archive/FatHandler.cpp | 801 ++++--- CPP/7zip/Archive/Nsis/NsisIn.cpp | 8 CPP/7zip/Archive/NtfsHandler.cpp | 2 CPP/7zip/Archive/PeHandler.cpp | 4 CPP/7zip/Archive/Rar/Rar5Handler.cpp | 13 CPP/7zip/Archive/Rar/Rar5Handler.h | 8 CPP/7zip/Archive/Rar/RarHandler.cpp | 4 CPP/7zip/Archive/RpmHandler.cpp | 6 CPP/7zip/Archive/VmdkHandler.cpp | 9 CPP/7zip/Archive/Wim/WimIn.cpp | 2 CPP/7zip/Archive/XarHandler.cpp | 23 CPP/7zip/Archive/XzHandler.cpp | 14 CPP/7zip/Archive/Zip/ZipUpdate.cpp | 47 CPP/7zip/Bundles/Alone/Alone.dsp | 20 CPP/7zip/Bundles/Alone/makefile | 3 CPP/7zip/Bundles/Alone7z/makefile | 2 CPP/7zip/Bundles/Format7z/makefile | 2 CPP/7zip/Bundles/Format7zF/Arc.mak | 2 CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp | 4 CPP/7zip/Common/InBuffer.h | 10 CPP/7zip/Common/MethodProps.cpp | 34 CPP/7zip/Common/MethodProps.h | 6 CPP/7zip/Common/OutBuffer.h | 29 CPP/7zip/Compress/BZip2Const.h | 2 CPP/7zip/Compress/BZip2Encoder.cpp | 715 ++++-- CPP/7zip/Compress/BZip2Encoder.h | 136 - CPP/7zip/Compress/BitlEncoder.h | 1 CPP/7zip/Compress/BitmEncoder.h | 61 CPP/7zip/Compress/DeflateDecoder.cpp | 16 CPP/7zip/Compress/DeflateEncoder.cpp | 100 CPP/7zip/Compress/Lzma2Encoder.cpp | 10 CPP/7zip/Compress/LzmaEncoder.cpp | 18 CPP/7zip/Compress/Mtf8.h | 13 CPP/7zip/Crypto/MyAes.cpp | 27 CPP/7zip/ICoder.h | 3 CPP/7zip/Sort.mak | 6 CPP/7zip/UI/Agent/AgentProxy.cpp | 2 CPP/7zip/UI/Client7z/makefile.gcc | 2 CPP/7zip/UI/Common/ArchiveCommandLine.cpp | 82 CPP/7zip/UI/Common/ArchiveExtractCallback.cpp | 1128 ++++++---- CPP/7zip/UI/Common/ArchiveExtractCallback.h | 233 +- CPP/7zip/UI/Common/Bench.cpp | 87 CPP/7zip/UI/Common/EnumDirItems.cpp | 55 CPP/7zip/UI/Common/Extract.cpp | 2 CPP/7zip/UI/Common/ExtractingFilePath.cpp | 2 CPP/7zip/UI/Common/HashCalc.cpp | 313 ++ CPP/7zip/UI/Common/HashCalc.h | 21 CPP/7zip/UI/Common/LoadCodecs.cpp | 6 CPP/7zip/UI/Common/Update.cpp | 4 CPP/7zip/UI/Common/Update.h | 2 CPP/7zip/UI/Common/UpdateCallback.cpp | 110 CPP/7zip/UI/Console/Main.cpp | 9 CPP/7zip/UI/Console/makefile | 2 CPP/7zip/UI/Explorer/makefile | 2 CPP/7zip/UI/Far/Plugin.cpp | 9 CPP/7zip/UI/Far/makefile | 2 CPP/7zip/UI/FileManager/FM.cpp | 2 CPP/7zip/UI/FileManager/LangUtils.cpp | 12 CPP/7zip/UI/FileManager/LinkDialog.cpp | 30 CPP/7zip/UI/FileManager/Panel.h | 4 CPP/7zip/UI/FileManager/PanelCopy.cpp | 2 CPP/7zip/UI/FileManager/PanelFolderChange.cpp | 14 CPP/7zip/UI/FileManager/PanelOperations.cpp | 4 CPP/7zip/UI/FileManager/RootFolder.cpp | 4 CPP/7zip/UI/FileManager/makefile | 2 CPP/7zip/UI/GUI/BenchmarkDialog.cpp | 4 CPP/7zip/UI/GUI/BenchmarkDialog.rc | 2 CPP/7zip/UI/GUI/CompressDialog.cpp | 23 CPP/7zip/UI/GUI/makefile | 3 CPP/Build.mak | 8 CPP/Common/MyString.cpp | 29 CPP/Common/MyString.h | 13 CPP/Common/MyXml.cpp | 4 CPP/Common/Sha3Reg.cpp | 2 CPP/Common/Wildcard.cpp | 13 CPP/Windows/FileDir.cpp | 73 CPP/Windows/FileDir.h | 18 CPP/Windows/FileFind.cpp | 9 CPP/Windows/FileIO.h | 43 CPP/Windows/FileLink.cpp | 244 +- CPP/Windows/FileName.cpp | 71 CPP/Windows/FileName.h | 13 CPP/Windows/System.cpp | 128 - CPP/Windows/System.h | 61 CPP/Windows/Thread.h | 8 CPP/Windows/TimeUtils.cpp | 3 DOC/7zip.wxs | 4 DOC/License.txt | 6 DOC/readme.txt | 25 DOC/src-history.txt | 22 debian/changelog | 7 debian/patches/0001-Accept-Debian-build-flags.patch | 2 debian/patches/0002-Use-getcwd-3-POSIX-extension-to-avoid-PATH_MAX-macro.patch | 4 debian/patches/0005-Add-note-for-unexpected-recursive-operations-behavio.patch | 2 debian/patches/0006-Use-c-flags-for-asmc.patch | 2 debian/patches/0007-Add-fpic-for-Asmc-options.patch | 2 138 files changed, 5934 insertions(+), 2454 deletions(-) diff -Nru 7zip-24.09+dfsg/Asm/x86/Sort.asm 7zip-25.01+dfsg/Asm/x86/Sort.asm --- 7zip-24.09+dfsg/Asm/x86/Sort.asm 1970-01-01 00:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/Asm/x86/Sort.asm 2025-01-08 08:00:00.000000000 +0000 @@ -0,0 +1,860 @@ +; SortTest.asm -- ASM version of HeapSort() function +; Igor Pavlov : Public domain + +include ../../../../Asm/x86/7zAsm.asm + +MY_ASM_START + +ifndef Z7_SORT_ASM_USE_SEGMENT +if (IS_LINUX gt 0) + ; Z7_SORT_ASM_USE_SEGMENT equ 1 +else + ; Z7_SORT_ASM_USE_SEGMENT equ 1 +endif +endif + +ifdef Z7_SORT_ASM_USE_SEGMENT +_TEXT$Z7_SORT SEGMENT ALIGN(64) 'CODE' +MY_ALIGN macro num:req + align num +endm +else +MY_ALIGN macro num:req + ; We expect that ".text" is aligned for 16-bytes. + ; So we don't need large alignment inside our function. + align 16 +endm +endif + + +MY_ALIGN_16 macro + MY_ALIGN 16 +endm + +MY_ALIGN_32 macro + MY_ALIGN 32 +endm + +MY_ALIGN_64 macro + MY_ALIGN 64 +endm + +ifdef x64 + +NUM_PREFETCH_LEVELS equ 3 ; to prefetch 1x 64-bytes line (is good for most cases) +; NUM_PREFETCH_LEVELS equ 4 ; to prefetch 2x 64-bytes lines (better for big arrays) + +acc equ x0 +k equ r0 +k_x equ x0 + +p equ r1 + +s equ r2 +s_x equ x2 + +a0 equ x3 +t0 equ a0 + +a3 equ x5 +qq equ a3 + +a1 equ x6 +t1 equ a1 +t1_r equ r6 + +a2 equ x7 +t2 equ a2 + +i equ r8 +e0 equ x8 + +e1 equ x9 + +num_last equ r10 +num_last_x equ x10 + +next4_lim equ r11 +pref_lim equ r12 + + + +SORT_2_WITH_TEMP_REG macro b0, b1, temp_reg + mov temp_reg, b0 + cmp b0, b1 + cmovae b0, b1 ; min + cmovae b1, temp_reg ; max +endm + +SORT macro b0, b1 + SORT_2_WITH_TEMP_REG b0, b1, acc +endm + +LOAD macro dest:req, index:req + mov dest, [p + 4 * index] +endm + +STORE macro reg:req, index:req + mov [p + 4 * index], reg +endm + + +if (NUM_PREFETCH_LEVELS gt 3) + num_prefetches equ (1 SHL (NUM_PREFETCH_LEVELS - 3)) +else + num_prefetches equ 1 +endif + +PREFETCH_OP macro offs + cur_offset = 7 * 4 ; it's average offset in 64-bytes cache line. + ; cur_offset = 0 ; we can use zero offset, if we are sure that array is aligned for 64-bytes. + rept num_prefetches + if 1 + prefetcht0 byte ptr [p + offs + cur_offset] + else + mov pref_x, dword ptr [p + offs + cur_offset] + endif + cur_offset = cur_offset + 64 + endm +endm + +PREFETCH_MY macro +if 1 + if 1 + shl k, NUM_PREFETCH_LEVELS + 3 + else + ; we delay prefetch instruction to improve main loads + shl k, NUM_PREFETCH_LEVELS + shl k, 3 + ; shl k, 0 + endif + PREFETCH_OP k +elseif 1 + shl k, 3 + PREFETCH_OP k * (1 SHL NUM_PREFETCH_LEVELS) ; change it +endif +endm + + +STEP_1 macro exit_label, prefetch_macro +use_cmov_1 equ 1 ; set 1 for cmov, but it's slower in some cases + ; set 0 for LOAD after adc s, 0 + cmp t0, t1 + if use_cmov_1 + cmovb t0, t1 + ; STORE t0, k + endif + adc s, 0 + if use_cmov_1 eq 0 + LOAD t0, s + endif + cmp qq, t0 + jae exit_label + if 1 ; use_cmov_1 eq 0 + STORE t0, k + endif + prefetch_macro + mov t0, [p + s * 8] + mov t1, [p + s * 8 + 4] + mov k, s + add s, s ; slower for some cpus + ; lea s, dword ptr [s + s] ; slower for some cpus + ; shl s, 1 ; faster for some cpus + ; lea s, dword ptr [s * 2] ; faster for some cpus + rept 0 ; 1000 for debug : 0 for normal + ; number of calls in generate_stage : ~0.6 of number of items + shl k, 0 + endm +endm + + +STEP_2 macro exit_label, prefetch_macro +use_cmov_2 equ 0 ; set 1 for cmov, but it's slower in some cases + ; set 0 for LOAD after adc s, 0 + cmp t0, t1 + if use_cmov_2 + mov t2, t0 + cmovb t2, t1 + ; STORE t2, k + endif + mov t0, [p + s * 8] + mov t1, [p + s * 8 + 4] + cmovb t0, [p + s * 8 + 8] + cmovb t1, [p + s * 8 + 12] + adc s, 0 + if use_cmov_2 eq 0 + LOAD t2, s + endif + cmp qq, t2 + jae exit_label + if 1 ; use_cmov_2 eq 0 + STORE t2, k + endif + prefetch_macro + mov k, s + ; add s, s + ; lea s, [s + s] + shl s, 1 + ; lea s, [s * 2] +endm + + +MOVE_SMALLEST_UP macro STEP, use_prefetch, num_unrolls + LOCAL exit_1, exit_2, leaves, opt_loop, last_nodes + + ; s == k * 2 + ; t0 == (p)[s] + ; t1 == (p)[s + 1] + cmp k, next4_lim + jae leaves + + rept num_unrolls + STEP exit_2 + cmp k, next4_lim + jae leaves + endm + + if use_prefetch + prefetch_macro equ PREFETCH_MY + pref_lim_2 equ pref_lim + ; lea pref_lim, dword ptr [num_last + 1] + ; shr pref_lim, NUM_PREFETCH_LEVELS + 1 + cmp k, pref_lim_2 + jae last_nodes + else + prefetch_macro equ + pref_lim_2 equ next4_lim + endif + +MY_ALIGN_16 +opt_loop: + STEP exit_2, prefetch_macro + cmp k, pref_lim_2 + jb opt_loop + +last_nodes: + ; k >= pref_lim_2 + ; 2 cases are possible: + ; case-1: num_after_prefetch_levels == 0 && next4_lim = pref_lim_2 + ; case-2: num_after_prefetch_levels == NUM_PREFETCH_LEVELS - 1 && + ; next4_lim = pref_lim_2 / (NUM_PREFETCH_LEVELS - 1) + if use_prefetch + yyy = NUM_PREFETCH_LEVELS - 1 + while yyy + yyy = yyy - 1 + STEP exit_2 + if yyy + cmp k, next4_lim + jae leaves + endif + endm + endif + +leaves: + ; k >= next4_lim == (num_last + 1) / 4 must be provided by previous code. + ; we have 2 nodes in (s) level : always + ; we can have some nodes in (s * 2) level : low probability case + ; we have no nodes in (s * 4) level + ; s == k * 2 + ; t0 == (p)[s] + ; t1 == (p)[s + 1] + cmp t0, t1 + cmovb t0, t1 + adc s, 0 + STORE t0, k + + ; t0 == (p)[s] + ; s / 2 == k : (s) is index of max item from (p)[k * 2], (p)[k * 2 + 1] + ; we have 3 possible cases here: + ; s * 2 > num_last : (s) node has no childs + ; s * 2 == num_last : (s) node has 1 leaf child that is last item of array + ; s * 2 < num_last : (s) node has 2 leaf childs. We provide (s * 4 > num_last) + ; we check for (s * 2 > num_last) before "cmp qq, t0" check, because + ; we will replace conditional jump with cmov instruction later. + lea t1_r, dword ptr [s + s] + cmp t1_r, num_last + ja exit_1 ; if (s * 2 > num_last), we have no childs : it's high probability branch + + ; it's low probability branch + ; s * 2 <= num_last + cmp qq, t0 + jae exit_2 + + ; qq < t0, so we go to next level + ; we check 1 or 2 childs in next level + mov t0, [p + s * 8] + mov k, s + mov s, t1_r + cmp t1_r, num_last + je @F ; (s == num_last) means that we have single child in tree + + ; (s < num_last) : so we must read both childs and select max of them. + mov t1, [p + k * 8 + 4] + cmp t0, t1 + cmovb t0, t1 + adc s, 0 +@@: + STORE t0, k +exit_1: + ; t0 == (p)[s], s / 2 == k : (s) is index of max item from (p)[k * 2], (p)[k * 2 + 1] + cmp qq, t0 + cmovb k, s +exit_2: + STORE qq, k +endm + + + + +ifdef Z7_SORT_ASM_USE_SEGMENT +; MY_ALIGN_64 +else + MY_ALIGN_16 +endif + +MY_PROC HeapSort, 2 + +if (IS_LINUX gt 0) + mov p, REG_ABI_PARAM_0 ; r1 <- r7 : linux +endif + mov num_last, REG_ABI_PARAM_1 ; r10 <- r6 : linux + ; r10 <- r2 : win64 + cmp num_last, 2 + jb end_1 + + ; MY_PUSH_PRESERVED_ABI_REGS + MY_PUSH_PRESERVED_ABI_REGS_UP_TO_INCLUDING_R11 + push r12 + + cmp num_last, 4 + ja sort_5 + + LOAD a0, 0 + LOAD a1, 1 + SORT a0, a1 + cmp num_last, 3 + jb end_2 + + LOAD a2, 2 + je sort_3 + + LOAD a3, 3 + SORT a2, a3 + SORT a1, a3 + STORE a3, 3 +sort_3: + SORT a0, a2 + SORT a1, a2 + STORE a2, 2 + jmp end_2 + +sort_5: + ; (num_last > 4) is required here + ; if (num_last >= 6) : we will use optimized loop for leaf nodes loop_down_1 + mov next4_lim, num_last + shr next4_lim, 2 + + dec num_last + mov k, num_last + shr k, 1 + mov i, num_last + shr i, 2 + test num_last, 1 + jnz size_even + + ; ODD number of items. So we compare parent with single child + LOAD t1, num_last + LOAD t0, k + SORT_2_WITH_TEMP_REG t1, t0, t2 + STORE t1, num_last + STORE t0, k + dec k + +size_even: + cmp k, i + jbe loop_down ; jump for num_last == 4 case + +if 0 ; 1 for debug + mov r15, k + mov r14d, 1 ; 100 +loop_benchmark: +endif + ; optimized loop for leaf nodes: + mov t0, [p + k * 8] + mov t1, [p + k * 8 + 4] + +MY_ALIGN_16 +loop_down_1: + ; we compare parent with max of childs: + ; lea s, dword ptr [2 * k] + mov s, k + cmp t0, t1 + cmovb t0, t1 + adc s, s + LOAD t2, k + STORE t0, k + cmp t2, t0 + cmovae s, k + dec k + ; we preload next items before STORE operation for calculated address + mov t0, [p + k * 8] + mov t1, [p + k * 8 + 4] + STORE t2, s + cmp k, i + jne loop_down_1 + +if 0 ; 1 for debug + mov k, r15 + dec r14d + jnz loop_benchmark + ; jmp end_debug +endif + +MY_ALIGN_16 +loop_down: + mov t0, [p + i * 8] + mov t1, [p + i * 8 + 4] + LOAD qq, i + mov k, i + lea s, dword ptr [i + i] + ; jmp end_debug + DOWN_use_prefetch equ 0 + DOWN_num_unrolls equ 0 + MOVE_SMALLEST_UP STEP_1, DOWN_use_prefetch, DOWN_num_unrolls + sub i, 1 + jnb loop_down + + ; jmp end_debug + LOAD e0, 0 + LOAD e1, 1 + + LEVEL_3_LIMIT equ 8 ; 8 is default, but 7 also can work + + cmp num_last, LEVEL_3_LIMIT + 1 + jb main_loop_sort_5 + +MY_ALIGN_16 +main_loop_sort: + ; num_last > LEVEL_3_LIMIT + ; p[size--] = p[0]; + LOAD qq, num_last + STORE e0, num_last + mov e0, e1 + + mov next4_lim, num_last + shr next4_lim, 2 + mov pref_lim, num_last + shr pref_lim, NUM_PREFETCH_LEVELS + 1 + + dec num_last +if 0 ; 1 for debug + ; that optional optimization can improve the performance, if there are identical items in array + ; 3 times improvement : if all items in array are identical + ; 20% improvement : if items are different for 1 bit only + ; 1-10% improvement : if items are different for (2+) bits + ; no gain : if items are different + cmp qq, e1 + jae next_iter_main +endif + LOAD e1, 2 + LOAD t0, 3 + mov k_x, 2 + cmp e1, t0 + cmovb e1, t0 + mov t0, [p + 4 * (4 + 0)] + mov t1, [p + 4 * (4 + 1)] + cmovb t0, [p + 4 * (4 + 2)] + cmovb t1, [p + 4 * (4 + 3)] + adc k_x, 0 + ; (qq <= e1), because the tree is correctly sorted + ; also here we could check (qq >= e1) or (qq == e1) for faster exit + lea s, dword ptr [k + k] + MAIN_use_prefetch equ 1 + MAIN_num_unrolls equ 0 + MOVE_SMALLEST_UP STEP_2, MAIN_use_prefetch, MAIN_num_unrolls + +next_iter_main: + cmp num_last, LEVEL_3_LIMIT + jne main_loop_sort + + ; num_last == LEVEL_3_LIMIT +main_loop_sort_5: + ; 4 <= num_last <= LEVEL_3_LIMIT + ; p[size--] = p[0]; + LOAD qq, num_last + STORE e0, num_last + mov e0, e1 + dec num_last_x + + LOAD e1, 2 + LOAD t0, 3 + mov k_x, 2 + cmp e1, t0 + cmovb e1, t0 + adc k_x, 0 + + lea s_x, dword ptr [k * 2] + cmp s_x, num_last_x + ja exit_2 + + mov t0, [p + k * 8] + je exit_1 + + ; s < num_last + mov t1, [p + k * 8 + 4] + cmp t0, t1 + cmovb t0, t1 + adc s_x, 0 +exit_1: + STORE t0, k + cmp qq, t0 + cmovb k_x, s_x +exit_2: + STORE qq, k + cmp num_last_x, 3 + jne main_loop_sort_5 + + ; num_last == 3 (real_size == 4) + LOAD a0, 2 + LOAD a1, 3 + STORE e1, 2 + STORE e0, 3 + SORT a0, a1 +end_2: + STORE a0, 0 + STORE a1, 1 +; end_debug: + ; MY_POP_PRESERVED_ABI_REGS + pop r12 + MY_POP_PRESERVED_ABI_REGS_UP_TO_INCLUDING_R11 +end_1: +MY_ENDP + + + +else +; ------------ x86 32-bit ------------ + +ifdef x64 +IS_CDECL = 0 +endif + +acc equ x0 +k equ r0 +k_x equ acc + +p equ r1 + +num_last equ r2 +num_last_x equ x2 + +a0 equ x3 +t0 equ a0 + +a3 equ x5 +i equ r5 +e0 equ a3 + +a1 equ x6 +qq equ a1 + +a2 equ x7 +s equ r7 +s_x equ a2 + + +SORT macro b0, b1 + cmp b1, b0 + jae @F + if 1 + xchg b0, b1 + else + mov acc, b0 + mov b0, b1 ; min + mov b1, acc ; max + endif +@@: +endm + +LOAD macro dest:req, index:req + mov dest, [p + 4 * index] +endm + +STORE macro reg:req, index:req + mov [p + 4 * index], reg +endm + + +STEP_1 macro exit_label + mov t0, [p + k * 8] + cmp t0, [p + k * 8 + 4] + adc s, 0 + LOAD t0, s + STORE t0, k ; we lookahed stooring for most expected branch + cmp qq, t0 + jae exit_label + ; STORE t0, k ; use if + mov k, s + add s, s + ; lea s, dword ptr [s + s] + ; shl s, 1 + ; lea s, dword ptr [s * 2] +endm + +STEP_BRANCH macro exit_label + mov t0, [p + k * 8] + cmp t0, [p + k * 8 + 4] + jae @F + inc s + mov t0, [p + k * 8 + 4] +@@: + cmp qq, t0 + jae exit_label + STORE t0, k + mov k, s + add s, s +endm + + + +MOVE_SMALLEST_UP macro STEP, num_unrolls, exit_2 + LOCAL leaves, opt_loop, single + + ; s == k * 2 + rept num_unrolls + cmp s, num_last + jae leaves + STEP_1 exit_2 + endm + cmp s, num_last + jb opt_loop + +leaves: + ; (s >= num_last) + jne exit_2 +single: + ; (s == num_last) + mov t0, [p + k * 8] + cmp qq, t0 + jae exit_2 + STORE t0, k + mov k, s + jmp exit_2 + +MY_ALIGN_16 +opt_loop: + STEP exit_2 + cmp s, num_last + jb opt_loop + je single +exit_2: + STORE qq, k +endm + + + + +ifdef Z7_SORT_ASM_USE_SEGMENT +; MY_ALIGN_64 +else + MY_ALIGN_16 +endif + +MY_PROC HeapSort, 2 + ifdef x64 + if (IS_LINUX gt 0) + mov num_last, REG_ABI_PARAM_1 ; r2 <- r6 : linux + mov p, REG_ABI_PARAM_0 ; r1 <- r7 : linux + endif + elseif (IS_CDECL gt 0) + mov num_last, [r4 + REG_SIZE * 2] + mov p, [r4 + REG_SIZE * 1] + endif + cmp num_last, 2 + jb end_1 + MY_PUSH_PRESERVED_ABI_REGS_UP_TO_INCLUDING_R11 + + cmp num_last, 4 + ja sort_5 + + LOAD a0, 0 + LOAD a1, 1 + SORT a0, a1 + cmp num_last, 3 + jb end_2 + + LOAD a2, 2 + je sort_3 + + LOAD a3, 3 + SORT a2, a3 + SORT a1, a3 + STORE a3, 3 +sort_3: + SORT a0, a2 + SORT a1, a2 + STORE a2, 2 + jmp end_2 + +sort_5: + ; num_last > 4 + lea i, dword ptr [num_last - 2] + dec num_last + test i, 1 + jz loop_down + + ; single child + mov t0, [p + num_last * 4] + mov qq, [p + num_last * 2] + dec i + cmp qq, t0 + jae loop_down + + mov [p + num_last * 2], t0 + mov [p + num_last * 4], qq + +MY_ALIGN_16 +loop_down: + mov t0, [p + i * 4] + cmp t0, [p + i * 4 + 4] + mov k, i + mov qq, [p + i * 2] + adc k, 0 + LOAD t0, k + cmp qq, t0 + jae down_next + mov [p + i * 2], t0 + lea s, dword ptr [k + k] + + DOWN_num_unrolls equ 0 + MOVE_SMALLEST_UP STEP_1, DOWN_num_unrolls, down_exit_label +down_next: + sub i, 2 + jnb loop_down + ; jmp end_debug + + LOAD e0, 0 + +MY_ALIGN_16 +main_loop_sort: + ; num_last > 3 + mov t0, [p + 2 * 4] + cmp t0, [p + 3 * 4] + LOAD qq, num_last + STORE e0, num_last + LOAD e0, 1 + mov s_x, 2 + mov k_x, 1 + adc s, 0 + LOAD t0, s + dec num_last + cmp qq, t0 + jae main_exit_label + STORE t0, 1 + mov k, s + add s, s + if 1 + ; for branch data prefetch mode : + ; it's faster for large arrays : larger than (1 << 13) items. + MAIN_num_unrolls equ 10 + STEP_LOOP equ STEP_BRANCH + else + MAIN_num_unrolls equ 0 + STEP_LOOP equ STEP_1 + endif + + MOVE_SMALLEST_UP STEP_LOOP, MAIN_num_unrolls, main_exit_label + + ; jmp end_debug + cmp num_last, 3 + jne main_loop_sort + + ; num_last == 3 (real_size == 4) + LOAD a0, 2 + LOAD a1, 3 + LOAD a2, 1 + STORE e0, 3 ; e0 is alias for a3 + STORE a2, 2 + SORT a0, a1 +end_2: + STORE a0, 0 + STORE a1, 1 +; end_debug: + MY_POP_PRESERVED_ABI_REGS_UP_TO_INCLUDING_R11 +end_1: +MY_ENDP + +endif + +ifdef Z7_SORT_ASM_USE_SEGMENT +_TEXT$Z7_SORT ENDS +endif + +if 0 +LEA_IS_D8 (R64) [R2 * 4 + 16] + Lat : TP + 2 : 1 : adl-e + 2 : 3 p056 adl-p + 1 : 2 : p15 hsw-rocket + 1 : 2 : p01 snb-ivb + 1 : 1 : p1 conroe-wsm + 1 : 4 : zen3,zen4 + 2 : 4 : zen1,zen2 + +LEA_B_IS (R64) [R2 + R3 * 4] + Lat : TP + 1 : 1 : adl-e + 2 : 3 p056 adl-p + 1 : 2 : p15 hsw-rocket + 1 : 2 : p01 snb-ivb + 1 : 1 : p1 nhm-wsm + 1 : 1 : p0 conroe-wsm + 1 : 4 : zen3,zen4 + 2 :2,4 : zen1,zen2 + +LEA_B_IS_D8 (R64) [R2 + R3 * 4 + 16] + Lat : TP + 2 : 1 : adl-e + 2 : 3 p056 adl-p + 1 : 2 : p15 ice-rocket + 3 : 1 : p1/p15 hsw-rocket + 3 : 1 : p01 snb-ivb + 1 : 1 : p1 nhm-wsm + 1 : 1 : p0 conroe-wsm + 2,1 : 2 : zen3,zen4 + 2 : 2 : zen1,zen2 + +CMOVB (R64, R64) + Lat : TP + 1,2 : 2 : adl-e + 1 : 2 p06 adl-p + 1 : 2 : p06 bwd-rocket + 1,2 : 2 : p0156+p06 hsw + 1,2 :1.5 : p015+p05 snb-ivb + 1,2 : 1 : p015+p05 nhm + 1 : 1 : 2*p015 conroe + 1 : 2 : zen3,zen4 + 1 : 4 : zen1,zen2 + +ADC (R64, 0) + Lat : TP + 1,2 : 2 : adl-e + 1 : 2 p06 adl-p + 1 : 2 : p06 bwd-rocket + 1 :1.5 : p0156+p06 hsw + 1 :1.5 : p015+p05 snb-ivb + 2 : 1 : 2*p015 conroe-wstm + 1 : 2 : zen1,zen2,zen3,zen4 + +PREFETCHNTA : fetch data into non-temporal cache close to the processor, minimizing cache pollution. + L1 : Pentium3 + L2 : NetBurst + L1, not L2: Core duo, Core 2, Atom processors + L1, not L2, may fetch into L3 with fast replacement: Nehalem, Westmere, Sandy Bridge, ... + NEHALEM: Fills L1/L3, L1 LRU is not updated + L3 with fast replacement: Xeon Processors based on Nehalem, Westmere, Sandy Bridge, ... +PREFETCHT0 : fetch data into all cache levels. +PREFETCHT1 : fetch data into L2 and L3 +endif + +end diff -Nru 7zip-24.09+dfsg/C/7zVersion.h 7zip-25.01+dfsg/C/7zVersion.h --- 7zip-24.09+dfsg/C/7zVersion.h 2024-11-29 11:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/7zVersion.h 2025-08-03 06:00:00.000000000 +0000 @@ -1,7 +1,7 @@ -#define MY_VER_MAJOR 24 -#define MY_VER_MINOR 9 +#define MY_VER_MAJOR 25 +#define MY_VER_MINOR 1 #define MY_VER_BUILD 0 -#define MY_VERSION_NUMBERS "24.09" +#define MY_VERSION_NUMBERS "25.01" #define MY_VERSION MY_VERSION_NUMBERS #ifdef MY_CPU_NAME @@ -10,12 +10,12 @@ #define MY_VERSION_CPU MY_VERSION #endif -#define MY_DATE "2024-11-29" +#define MY_DATE "2025-08-03" #undef MY_COPYRIGHT #undef MY_VERSION_COPYRIGHT_DATE #define MY_AUTHOR_NAME "Igor Pavlov" #define MY_COPYRIGHT_PD "Igor Pavlov : Public domain" -#define MY_COPYRIGHT_CR "Copyright (c) 1999-2024 Igor Pavlov" +#define MY_COPYRIGHT_CR "Copyright (c) 1999-2025 Igor Pavlov" #ifdef USE_COPYRIGHT_CR #define MY_COPYRIGHT MY_COPYRIGHT_CR diff -Nru 7zip-24.09+dfsg/C/BwtSort.c 7zip-25.01+dfsg/C/BwtSort.c --- 7zip-24.09+dfsg/C/BwtSort.c 2023-04-02 11:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/BwtSort.c 2025-01-13 13:00:00.000000000 +0000 @@ -1,5 +1,5 @@ /* BwtSort.c -- BWT block sorting -2023-04-02 : Igor Pavlov : Public domain */ +: Igor Pavlov : Public domain */ #include "Precomp.h" @@ -7,6 +7,44 @@ #include "Sort.h" /* #define BLOCK_SORT_USE_HEAP_SORT */ +// #define BLOCK_SORT_USE_HEAP_SORT + +#ifdef BLOCK_SORT_USE_HEAP_SORT + +#define HeapSortRefDown(p, vals, n, size, temp) \ + { size_t k = n; UInt32 val = vals[temp]; for (;;) { \ + size_t s = k << 1; \ + if (s > size) break; \ + if (s < size && vals[p[s + 1]] > vals[p[s]]) s++; \ + if (val >= vals[p[s]]) break; \ + p[k] = p[s]; k = s; \ + } p[k] = temp; } + +void HeapSortRef(UInt32 *p, UInt32 *vals, size_t size) +{ + if (size <= 1) + return; + p--; + { + size_t i = size / 2; + do + { + UInt32 temp = p[i]; + HeapSortRefDown(p, vals, i, size, temp); + } + while (--i != 0); + } + do + { + UInt32 temp = p[size]; + p[size--] = p[1]; + HeapSortRefDown(p, vals, 1, size, temp); + } + while (size > 1); +} + +#endif // BLOCK_SORT_USE_HEAP_SORT + /* Don't change it !!! */ #define kNumHashBytes 2 @@ -27,26 +65,27 @@ #else -#define kNumBitsMax 20 -#define kIndexMask ((1 << kNumBitsMax) - 1) -#define kNumExtraBits (32 - kNumBitsMax) -#define kNumExtra0Bits (kNumExtraBits - 2) -#define kNumExtra0Mask ((1 << kNumExtra0Bits) - 1) +#define kNumBitsMax 20 +#define kIndexMask (((UInt32)1 << kNumBitsMax) - 1) +#define kNumExtraBits (32 - kNumBitsMax) +#define kNumExtra0Bits (kNumExtraBits - 2) +#define kNumExtra0Mask ((1 << kNumExtra0Bits) - 1) #define SetFinishedGroupSize(p, size) \ - { *(p) |= ((((size) - 1) & kNumExtra0Mask) << kNumBitsMax); \ + { *(p) |= ((((UInt32)(size) - 1) & kNumExtra0Mask) << kNumBitsMax); \ if ((size) > (1 << kNumExtra0Bits)) { \ - *(p) |= 0x40000000; *((p) + 1) |= ((((size) - 1)>> kNumExtra0Bits) << kNumBitsMax); } } \ + *(p) |= 0x40000000; \ + *((p) + 1) |= (((UInt32)(size) - 1) >> kNumExtra0Bits) << kNumBitsMax; } } \ -static void SetGroupSize(UInt32 *p, UInt32 size) +static void SetGroupSize(UInt32 *p, size_t size) { if (--size == 0) return; - *p |= 0x80000000 | ((size & kNumExtra0Mask) << kNumBitsMax); + *p |= 0x80000000 | (((UInt32)size & kNumExtra0Mask) << kNumBitsMax); if (size >= (1 << kNumExtra0Bits)) { *p |= 0x40000000; - p[1] |= ((size >> kNumExtra0Bits) << kNumBitsMax); + p[1] |= (((UInt32)size >> kNumExtra0Bits) << kNumBitsMax); } } @@ -59,12 +98,14 @@ */ static -UInt32 +unsigned Z7_FASTCALL -SortGroup(UInt32 BlockSize, UInt32 NumSortedBytes, UInt32 groupOffset, UInt32 groupSize, int NumRefBits, UInt32 *Indices - #ifndef BLOCK_SORT_USE_HEAP_SORT - , UInt32 left, UInt32 range - #endif +SortGroup(size_t BlockSize, size_t NumSortedBytes, + size_t groupOffset, size_t groupSize, + unsigned NumRefBits, UInt32 *Indices +#ifndef BLOCK_SORT_USE_HEAP_SORT + , size_t left, size_t range +#endif ) { UInt32 *ind2 = Indices + groupOffset; @@ -79,90 +120,93 @@ return 0; } Groups = Indices + BlockSize + BS_TEMP_SIZE; - if (groupSize <= ((UInt32)1 << NumRefBits) - #ifndef BLOCK_SORT_USE_HEAP_SORT + if (groupSize <= ((size_t)1 << NumRefBits) +#ifndef BLOCK_SORT_USE_HEAP_SORT && groupSize <= range - #endif +#endif ) { UInt32 *temp = Indices + BlockSize; - UInt32 j; - UInt32 mask, thereAreGroups, group, cg; + size_t j, group; + UInt32 mask, cg; + unsigned thereAreGroups; { UInt32 gPrev; UInt32 gRes = 0; { - UInt32 sp = ind2[0] + NumSortedBytes; - if (sp >= BlockSize) sp -= BlockSize; + size_t sp = ind2[0] + NumSortedBytes; + if (sp >= BlockSize) + sp -= BlockSize; gPrev = Groups[sp]; - temp[0] = (gPrev << NumRefBits); + temp[0] = gPrev << NumRefBits; } for (j = 1; j < groupSize; j++) { - UInt32 sp = ind2[j] + NumSortedBytes; + size_t sp = ind2[j] + NumSortedBytes; UInt32 g; - if (sp >= BlockSize) sp -= BlockSize; + if (sp >= BlockSize) + sp -= BlockSize; g = Groups[sp]; - temp[j] = (g << NumRefBits) | j; + temp[j] = (g << NumRefBits) | (UInt32)j; gRes |= (gPrev ^ g); } if (gRes == 0) { - #ifndef BLOCK_SORT_EXTERNAL_FLAGS +#ifndef BLOCK_SORT_EXTERNAL_FLAGS SetGroupSize(ind2, groupSize); - #endif +#endif return 1; } } HeapSort(temp, groupSize); - mask = (((UInt32)1 << NumRefBits) - 1); + mask = ((UInt32)1 << NumRefBits) - 1; thereAreGroups = 0; group = groupOffset; - cg = (temp[0] >> NumRefBits); + cg = temp[0] >> NumRefBits; temp[0] = ind2[temp[0] & mask]; { - #ifdef BLOCK_SORT_EXTERNAL_FLAGS +#ifdef BLOCK_SORT_EXTERNAL_FLAGS UInt32 *Flags = Groups + BlockSize; - #else - UInt32 prevGroupStart = 0; - #endif +#else + size_t prevGroupStart = 0; +#endif for (j = 1; j < groupSize; j++) { - UInt32 val = temp[j]; - UInt32 cgCur = (val >> NumRefBits); + const UInt32 val = temp[j]; + const UInt32 cgCur = val >> NumRefBits; if (cgCur != cg) { cg = cgCur; group = groupOffset + j; - #ifdef BLOCK_SORT_EXTERNAL_FLAGS +#ifdef BLOCK_SORT_EXTERNAL_FLAGS { - UInt32 t = group - 1; - Flags[t >> kNumFlagsBits] &= ~(1 << (t & kFlagsMask)); + const size_t t = group - 1; + Flags[t >> kNumFlagsBits] &= ~((UInt32)1 << (t & kFlagsMask)); } - #else +#else SetGroupSize(temp + prevGroupStart, j - prevGroupStart); prevGroupStart = j; - #endif +#endif } else thereAreGroups = 1; { - UInt32 ind = ind2[val & mask]; - temp[j] = ind; - Groups[ind] = group; + const UInt32 ind = ind2[val & mask]; + temp[j] = ind; + Groups[ind] = (UInt32)group; } } - #ifndef BLOCK_SORT_EXTERNAL_FLAGS +#ifndef BLOCK_SORT_EXTERNAL_FLAGS SetGroupSize(temp + prevGroupStart, j - prevGroupStart); - #endif +#endif } for (j = 0; j < groupSize; j++) @@ -172,37 +216,42 @@ /* Check that all strings are in one group (cannot sort) */ { - UInt32 group, j; - UInt32 sp = ind2[0] + NumSortedBytes; if (sp >= BlockSize) sp -= BlockSize; + UInt32 group; + size_t j; + size_t sp = ind2[0] + NumSortedBytes; + if (sp >= BlockSize) + sp -= BlockSize; group = Groups[sp]; for (j = 1; j < groupSize; j++) { - sp = ind2[j] + NumSortedBytes; if (sp >= BlockSize) sp -= BlockSize; + sp = ind2[j] + NumSortedBytes; + if (sp >= BlockSize) + sp -= BlockSize; if (Groups[sp] != group) break; } if (j == groupSize) { - #ifndef BLOCK_SORT_EXTERNAL_FLAGS +#ifndef BLOCK_SORT_EXTERNAL_FLAGS SetGroupSize(ind2, groupSize); - #endif +#endif return 1; } } - #ifndef BLOCK_SORT_USE_HEAP_SORT +#ifndef BLOCK_SORT_USE_HEAP_SORT { /* ---------- Range Sort ---------- */ - UInt32 i; - UInt32 mid; + size_t i; + size_t mid; for (;;) { - UInt32 j; + size_t j; if (range <= 1) { - #ifndef BLOCK_SORT_EXTERNAL_FLAGS +#ifndef BLOCK_SORT_EXTERNAL_FLAGS SetGroupSize(ind2, groupSize); - #endif +#endif return 1; } mid = left + ((range + 1) >> 1); @@ -210,7 +259,7 @@ i = 0; do { - UInt32 sp = ind2[i] + NumSortedBytes; if (sp >= BlockSize) sp -= BlockSize; + size_t sp = ind2[i] + NumSortedBytes; if (sp >= BlockSize) sp -= BlockSize; if (Groups[sp] >= mid) { for (j--; j > i; j--) @@ -238,51 +287,53 @@ break; } - #ifdef BLOCK_SORT_EXTERNAL_FLAGS +#ifdef BLOCK_SORT_EXTERNAL_FLAGS { - UInt32 t = (groupOffset + i - 1); + const size_t t = groupOffset + i - 1; UInt32 *Flags = Groups + BlockSize; - Flags[t >> kNumFlagsBits] &= ~(1 << (t & kFlagsMask)); + Flags[t >> kNumFlagsBits] &= ~((UInt32)1 << (t & kFlagsMask)); } - #endif +#endif { - UInt32 j; + size_t j; for (j = i; j < groupSize; j++) - Groups[ind2[j]] = groupOffset + i; + Groups[ind2[j]] = (UInt32)(groupOffset + i); } { - UInt32 res = SortGroup(BlockSize, NumSortedBytes, groupOffset, i, NumRefBits, Indices, left, mid - left); - return res | SortGroup(BlockSize, NumSortedBytes, groupOffset + i, groupSize - i, NumRefBits, Indices, mid, range - (mid - left)); + unsigned res = SortGroup(BlockSize, NumSortedBytes, groupOffset, i, NumRefBits, Indices, left, mid - left); + return res | SortGroup(BlockSize, NumSortedBytes, groupOffset + i, groupSize - i, NumRefBits, Indices, mid, range - (mid - left)); } } - #else +#else // BLOCK_SORT_USE_HEAP_SORT /* ---------- Heap Sort ---------- */ { - UInt32 j; + size_t j; for (j = 0; j < groupSize; j++) { - UInt32 sp = ind2[j] + NumSortedBytes; if (sp >= BlockSize) sp -= BlockSize; - ind2[j] = sp; + size_t sp = ind2[j] + NumSortedBytes; + if (sp >= BlockSize) + sp -= BlockSize; + ind2[j] = (UInt32)sp; } HeapSortRef(ind2, Groups, groupSize); /* Write Flags */ { - UInt32 sp = ind2[0]; + size_t sp = ind2[0]; UInt32 group = Groups[sp]; - #ifdef BLOCK_SORT_EXTERNAL_FLAGS +#ifdef BLOCK_SORT_EXTERNAL_FLAGS UInt32 *Flags = Groups + BlockSize; - #else - UInt32 prevGroupStart = 0; - #endif +#else + size_t prevGroupStart = 0; +#endif for (j = 1; j < groupSize; j++) { @@ -290,149 +341,210 @@ if (Groups[sp] != group) { group = Groups[sp]; - #ifdef BLOCK_SORT_EXTERNAL_FLAGS +#ifdef BLOCK_SORT_EXTERNAL_FLAGS { - UInt32 t = groupOffset + j - 1; + const size_t t = groupOffset + j - 1; Flags[t >> kNumFlagsBits] &= ~(1 << (t & kFlagsMask)); } - #else +#else SetGroupSize(ind2 + prevGroupStart, j - prevGroupStart); prevGroupStart = j; - #endif +#endif } } - #ifndef BLOCK_SORT_EXTERNAL_FLAGS +#ifndef BLOCK_SORT_EXTERNAL_FLAGS SetGroupSize(ind2 + prevGroupStart, j - prevGroupStart); - #endif +#endif } { /* Write new Groups values and Check that there are groups */ - UInt32 thereAreGroups = 0; + unsigned thereAreGroups = 0; for (j = 0; j < groupSize; j++) { - UInt32 group = groupOffset + j; - #ifndef BLOCK_SORT_EXTERNAL_FLAGS + size_t group = groupOffset + j; +#ifndef BLOCK_SORT_EXTERNAL_FLAGS UInt32 subGroupSize = ((ind2[j] & ~0xC0000000) >> kNumBitsMax); - if ((ind2[j] & 0x40000000) != 0) + if (ind2[j] & 0x40000000) subGroupSize += ((ind2[(size_t)j + 1] >> kNumBitsMax) << kNumExtra0Bits); subGroupSize++; for (;;) { - UInt32 original = ind2[j]; - UInt32 sp = original & kIndexMask; - if (sp < NumSortedBytes) sp += BlockSize; sp -= NumSortedBytes; - ind2[j] = sp | (original & ~kIndexMask); - Groups[sp] = group; + const UInt32 original = ind2[j]; + size_t sp = original & kIndexMask; + if (sp < NumSortedBytes) + sp += BlockSize; + sp -= NumSortedBytes; + ind2[j] = (UInt32)sp | (original & ~kIndexMask); + Groups[sp] = (UInt32)group; if (--subGroupSize == 0) break; j++; thereAreGroups = 1; } - #else +#else UInt32 *Flags = Groups + BlockSize; for (;;) { - UInt32 sp = ind2[j]; if (sp < NumSortedBytes) sp += BlockSize; sp -= NumSortedBytes; - ind2[j] = sp; - Groups[sp] = group; + size_t sp = ind2[j]; + if (sp < NumSortedBytes) + sp += BlockSize; + sp -= NumSortedBytes; + ind2[j] = (UInt32)sp; + Groups[sp] = (UInt32)group; if ((Flags[(groupOffset + j) >> kNumFlagsBits] & (1 << ((groupOffset + j) & kFlagsMask))) == 0) break; j++; thereAreGroups = 1; } - #endif +#endif } return thereAreGroups; } } - #endif +#endif // BLOCK_SORT_USE_HEAP_SORT } + /* conditions: blockSize > 0 */ -UInt32 BlockSort(UInt32 *Indices, const Byte *data, UInt32 blockSize) +UInt32 BlockSort(UInt32 *Indices, const Byte *data, size_t blockSize) { UInt32 *counters = Indices + blockSize; - UInt32 i; + size_t i; UInt32 *Groups; - #ifdef BLOCK_SORT_EXTERNAL_FLAGS +#ifdef BLOCK_SORT_EXTERNAL_FLAGS UInt32 *Flags; - #endif +#endif - /* Radix-Sort for 2 bytes */ +/* Radix-Sort for 2 bytes */ +// { UInt32 yyy; for (yyy = 0; yyy < 100; yyy++) { for (i = 0; i < kNumHashValues; i++) counters[i] = 0; - for (i = 0; i < blockSize - 1; i++) - counters[((UInt32)data[i] << 8) | data[(size_t)i + 1]]++; - counters[((UInt32)data[i] << 8) | data[0]]++; + { + const Byte *data2 = data; + size_t a = data[(size_t)blockSize - 1]; + const Byte *data_lim = data + blockSize; + if (blockSize >= 4) + { + data_lim -= 3; + do + { + size_t b; + b = data2[0]; counters[(a << 8) | b]++; + a = data2[1]; counters[(b << 8) | a]++; + b = data2[2]; counters[(a << 8) | b]++; + a = data2[3]; counters[(b << 8) | a]++; + data2 += 4; + } + while (data2 < data_lim); + data_lim += 3; + } + while (data2 != data_lim) + { + size_t b = *data2++; + counters[(a << 8) | b]++; + a = b; + } + } +// }} Groups = counters + BS_TEMP_SIZE; - #ifdef BLOCK_SORT_EXTERNAL_FLAGS +#ifdef BLOCK_SORT_EXTERNAL_FLAGS Flags = Groups + blockSize; - { - UInt32 numWords = (blockSize + kFlagsMask) >> kNumFlagsBits; - for (i = 0; i < numWords; i++) - Flags[i] = kAllFlags; - } - #endif + { + const size_t numWords = (blockSize + kFlagsMask) >> kNumFlagsBits; + for (i = 0; i < numWords; i++) + Flags[i] = kAllFlags; + } +#endif { UInt32 sum = 0; for (i = 0; i < kNumHashValues; i++) { - UInt32 groupSize = counters[i]; - if (groupSize > 0) + const UInt32 groupSize = counters[i]; + counters[i] = sum; + sum += groupSize; +#ifdef BLOCK_SORT_EXTERNAL_FLAGS + if (groupSize) { - #ifdef BLOCK_SORT_EXTERNAL_FLAGS - UInt32 t = sum + groupSize - 1; - Flags[t >> kNumFlagsBits] &= ~(1 << (t & kFlagsMask)); - #endif - sum += groupSize; + const UInt32 t = sum - 1; + Flags[t >> kNumFlagsBits] &= ~((UInt32)1 << (t & kFlagsMask)); } - counters[i] = sum - groupSize; +#endif } + } - for (i = 0; i < blockSize - 1; i++) - Groups[i] = counters[((UInt32)data[i] << 8) | data[(size_t)i + 1]]; - Groups[i] = counters[((UInt32)data[i] << 8) | data[0]]; + for (i = 0; i < blockSize - 1; i++) + Groups[i] = counters[((unsigned)data[i] << 8) | data[(size_t)i + 1]]; + Groups[i] = counters[((unsigned)data[i] << 8) | data[0]]; + + { +#define SET_Indices(a, b, i) \ + { UInt32 c; \ + a = (a << 8) | (b); \ + c = counters[a]; \ + Indices[c] = (UInt32)i++; \ + counters[a] = c + 1; \ + } - for (i = 0; i < blockSize - 1; i++) - Indices[counters[((UInt32)data[i] << 8) | data[(size_t)i + 1]]++] = i; - Indices[counters[((UInt32)data[i] << 8) | data[0]]++] = i; - - #ifndef BLOCK_SORT_EXTERNAL_FLAGS + size_t a = data[0]; + const Byte *data_ptr = data + 1; + i = 0; + if (blockSize >= 3) + { + blockSize -= 2; + do + { + size_t b; + b = data_ptr[0]; SET_Indices(a, b, i) + a = data_ptr[1]; SET_Indices(b, a, i) + data_ptr += 2; + } + while (i < blockSize); + blockSize += 2; + } + if (i < blockSize - 1) { + SET_Indices(a, data[(size_t)i + 1], i) + a = (Byte)a; + } + SET_Indices(a, data[0], i) + } + +#ifndef BLOCK_SORT_EXTERNAL_FLAGS + { UInt32 prev = 0; for (i = 0; i < kNumHashValues; i++) { - UInt32 prevGroupSize = counters[i] - prev; + const UInt32 prevGroupSize = counters[i] - prev; if (prevGroupSize == 0) continue; SetGroupSize(Indices + prev, prevGroupSize); prev = counters[i]; } - } - #endif } +#endif { - int NumRefBits; - UInt32 NumSortedBytes; - for (NumRefBits = 0; ((blockSize - 1) >> NumRefBits) != 0; NumRefBits++); + unsigned NumRefBits; + size_t NumSortedBytes; + for (NumRefBits = 0; ((blockSize - 1) >> NumRefBits) != 0; NumRefBits++) + {} NumRefBits = 32 - NumRefBits; if (NumRefBits > kNumRefBitsMax) - NumRefBits = kNumRefBitsMax; + NumRefBits = kNumRefBitsMax; for (NumSortedBytes = kNumHashBytes; ; NumSortedBytes <<= 1) { - #ifndef BLOCK_SORT_EXTERNAL_FLAGS - UInt32 finishedGroupSize = 0; - #endif - UInt32 newLimit = 0; +#ifndef BLOCK_SORT_EXTERNAL_FLAGS + size_t finishedGroupSize = 0; +#endif + size_t newLimit = 0; for (i = 0; i < blockSize;) { - UInt32 groupSize; - #ifdef BLOCK_SORT_EXTERNAL_FLAGS + size_t groupSize; +#ifdef BLOCK_SORT_EXTERNAL_FLAGS if ((Flags[i >> kNumFlagsBits] & (1 << (i & kFlagsMask))) == 0) { @@ -441,56 +553,56 @@ } for (groupSize = 1; (Flags[(i + groupSize) >> kNumFlagsBits] & (1 << ((i + groupSize) & kFlagsMask))) != 0; - groupSize++); - + groupSize++) + {} groupSize++; - #else +#else - groupSize = ((Indices[i] & ~0xC0000000) >> kNumBitsMax); + groupSize = (Indices[i] & ~0xC0000000) >> kNumBitsMax; { - BoolInt finishedGroup = ((Indices[i] & 0x80000000) == 0); - if ((Indices[i] & 0x40000000) != 0) - { - groupSize += ((Indices[(size_t)i + 1] >> kNumBitsMax) << kNumExtra0Bits); - Indices[(size_t)i + 1] &= kIndexMask; - } - Indices[i] &= kIndexMask; - groupSize++; - if (finishedGroup || groupSize == 1) - { - Indices[i - finishedGroupSize] &= kIndexMask; - if (finishedGroupSize > 1) - Indices[(size_t)(i - finishedGroupSize) + 1] &= kIndexMask; + const BoolInt finishedGroup = ((Indices[i] & 0x80000000) == 0); + if (Indices[i] & 0x40000000) { - UInt32 newGroupSize = groupSize + finishedGroupSize; - SetFinishedGroupSize(Indices + i - finishedGroupSize, newGroupSize) - finishedGroupSize = newGroupSize; + groupSize += ((Indices[(size_t)i + 1] >> kNumBitsMax) << kNumExtra0Bits); + Indices[(size_t)i + 1] &= kIndexMask; } - i += groupSize; - continue; - } - finishedGroupSize = 0; + Indices[i] &= kIndexMask; + groupSize++; + if (finishedGroup || groupSize == 1) + { + Indices[i - finishedGroupSize] &= kIndexMask; + if (finishedGroupSize > 1) + Indices[(size_t)(i - finishedGroupSize) + 1] &= kIndexMask; + { + const size_t newGroupSize = groupSize + finishedGroupSize; + SetFinishedGroupSize(Indices + i - finishedGroupSize, newGroupSize) + finishedGroupSize = newGroupSize; + } + i += groupSize; + continue; + } + finishedGroupSize = 0; } - #endif +#endif if (NumSortedBytes >= blockSize) { - UInt32 j; + size_t j; for (j = 0; j < groupSize; j++) { - UInt32 t = (i + j); + size_t t = i + j; /* Flags[t >> kNumFlagsBits] &= ~(1 << (t & kFlagsMask)); */ - Groups[Indices[t]] = t; + Groups[Indices[t]] = (UInt32)t; } } else if (SortGroup(blockSize, NumSortedBytes, i, groupSize, NumRefBits, Indices - #ifndef BLOCK_SORT_USE_HEAP_SORT - , 0, blockSize - #endif - ) != 0) + #ifndef BLOCK_SORT_USE_HEAP_SORT + , 0, blockSize + #endif + )) newLimit = i + groupSize; i += groupSize; } @@ -498,19 +610,19 @@ break; } } - #ifndef BLOCK_SORT_EXTERNAL_FLAGS +#ifndef BLOCK_SORT_EXTERNAL_FLAGS for (i = 0; i < blockSize;) { - UInt32 groupSize = ((Indices[i] & ~0xC0000000) >> kNumBitsMax); - if ((Indices[i] & 0x40000000) != 0) + size_t groupSize = (Indices[i] & ~0xC0000000) >> kNumBitsMax; + if (Indices[i] & 0x40000000) { - groupSize += ((Indices[(size_t)i + 1] >> kNumBitsMax) << kNumExtra0Bits); + groupSize += (Indices[(size_t)i + 1] >> kNumBitsMax) << kNumExtra0Bits; Indices[(size_t)i + 1] &= kIndexMask; } Indices[i] &= kIndexMask; groupSize++; i += groupSize; } - #endif +#endif return Groups[0]; } diff -Nru 7zip-24.09+dfsg/C/BwtSort.h 7zip-25.01+dfsg/C/BwtSort.h --- 7zip-24.09+dfsg/C/BwtSort.h 2023-03-03 10:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/BwtSort.h 2024-12-18 18:00:00.000000000 +0000 @@ -1,5 +1,5 @@ /* BwtSort.h -- BWT block sorting -2023-03-03 : Igor Pavlov : Public domain */ +: Igor Pavlov : Public domain */ #ifndef ZIP7_INC_BWT_SORT_H #define ZIP7_INC_BWT_SORT_H @@ -10,16 +10,17 @@ /* use BLOCK_SORT_EXTERNAL_FLAGS if blockSize can be > 1M */ /* #define BLOCK_SORT_EXTERNAL_FLAGS */ +// #define BLOCK_SORT_EXTERNAL_FLAGS #ifdef BLOCK_SORT_EXTERNAL_FLAGS -#define BLOCK_SORT_EXTERNAL_SIZE(blockSize) ((((blockSize) + 31) >> 5)) +#define BLOCK_SORT_EXTERNAL_SIZE(blockSize) (((blockSize) + 31) >> 5) #else #define BLOCK_SORT_EXTERNAL_SIZE(blockSize) 0 #endif #define BLOCK_SORT_BUF_SIZE(blockSize) ((blockSize) * 2 + BLOCK_SORT_EXTERNAL_SIZE(blockSize) + (1 << 16)) -UInt32 BlockSort(UInt32 *indices, const Byte *data, UInt32 blockSize); +UInt32 BlockSort(UInt32 *indices, const Byte *data, size_t blockSize); EXTERN_C_END diff -Nru 7zip-24.09+dfsg/C/Compiler.h 7zip-25.01+dfsg/C/Compiler.h --- 7zip-24.09+dfsg/C/Compiler.h 2024-03-01 15:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/Compiler.h 2025-06-30 17:00:00.000000000 +0000 @@ -1,5 +1,5 @@ /* Compiler.h : Compiler specific defines and pragmas -2024-01-22 : Igor Pavlov : Public domain */ +: Igor Pavlov : Public domain */ #ifndef ZIP7_INC_COMPILER_H #define ZIP7_INC_COMPILER_H @@ -183,6 +183,16 @@ #define Z7_ATTRIB_NO_VECTORIZE #endif +#if defined(Z7_MSC_VER_ORIGINAL) && (Z7_MSC_VER_ORIGINAL >= 1920) + #define Z7_PRAGMA_OPTIMIZE_FOR_CODE_SIZE _Pragma("optimize ( \"s\", on )") + #define Z7_PRAGMA_OPTIMIZE_DEFAULT _Pragma("optimize ( \"\", on )") +#else + #define Z7_PRAGMA_OPTIMIZE_FOR_CODE_SIZE + #define Z7_PRAGMA_OPTIMIZE_DEFAULT +#endif + + + #if defined(MY_CPU_X86_OR_AMD64) && ( \ defined(__clang__) && (__clang_major__ >= 4) \ || defined(__GNUC__) && (__GNUC__ >= 5)) diff -Nru 7zip-24.09+dfsg/C/CpuArch.h 7zip-25.01+dfsg/C/CpuArch.h --- 7zip-24.09+dfsg/C/CpuArch.h 2024-11-13 08:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/CpuArch.h 2025-04-05 08:00:00.000000000 +0000 @@ -47,6 +47,12 @@ #define MY_CPU_SIZEOF_POINTER 4 #endif +#if defined(__SSE2__) \ + || defined(MY_CPU_AMD64) \ + || defined(_M_IX86_FP) && (_M_IX86_FP >= 2) +#define MY_CPU_SSE2 +#endif + #if defined(_M_ARM64) \ || defined(_M_ARM64EC) \ @@ -571,10 +577,12 @@ #define Z7_CONV_BE_TO_NATIVE_CONST32(v) (v) #define Z7_CONV_LE_TO_NATIVE_CONST32(v) Z7_BSWAP32_CONST(v) #define Z7_CONV_NATIVE_TO_BE_32(v) (v) +// #define Z7_GET_NATIVE16_FROM_2_BYTES(b0, b1) ((b1) | ((b0) << 8)) #elif defined(MY_CPU_LE) #define Z7_CONV_BE_TO_NATIVE_CONST32(v) Z7_BSWAP32_CONST(v) #define Z7_CONV_LE_TO_NATIVE_CONST32(v) (v) #define Z7_CONV_NATIVE_TO_BE_32(v) Z7_BSWAP32(v) +// #define Z7_GET_NATIVE16_FROM_2_BYTES(b0, b1) ((b0) | ((b1) << 8)) #else #error Stop_Compiling_Unknown_Endian_CONV #endif diff -Nru 7zip-24.09+dfsg/C/HuffEnc.c 7zip-25.01+dfsg/C/HuffEnc.c --- 7zip-24.09+dfsg/C/HuffEnc.c 2023-09-07 11:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/HuffEnc.c 2025-03-20 14:00:00.000000000 +0000 @@ -1,60 +1,125 @@ /* HuffEnc.c -- functions for Huffman encoding -2023-09-07 : Igor Pavlov : Public domain */ +Igor Pavlov : Public domain */ #include "Precomp.h" +#include + #include "HuffEnc.h" #include "Sort.h" +#include "CpuArch.h" -#define kMaxLen 16 -#define NUM_BITS 10 -#define MASK ((1u << NUM_BITS) - 1) - -#define NUM_COUNTERS 64 - -#define HUFFMAN_SPEED_OPT +#define kMaxLen Z7_HUFFMAN_LEN_MAX +#define NUM_BITS 10 +#define MASK ((1u << NUM_BITS) - 1) +#define FREQ_MASK (~(UInt32)MASK) +#define NUM_COUNTERS (48 * 2) + +#if 1 && (defined(MY_CPU_LE) || defined(MY_CPU_BE)) +#if defined(MY_CPU_LE) + #define HI_HALF_OFFSET 1 +#else + #define HI_HALF_OFFSET 0 +#endif +#define LOAD_PARENT(p) ((unsigned)*((const UInt16 *)(p) + HI_HALF_OFFSET)) +#define STORE_PARENT(p, fb, val) *((UInt16 *)(p) + HI_HALF_OFFSET) = (UInt16)(val); +#define STORE_PARENT_DIRECT(p, fb, hi) STORE_PARENT(p, fb, hi) +#define UPDATE_E(eHi) eHi++; +#else +#define LOAD_PARENT(p) ((unsigned)(*(p) >> NUM_BITS)) +#define STORE_PARENT_DIRECT(p, fb, hi) *(p) = ((fb) & MASK) | (hi); // set parent field +#define STORE_PARENT(p, fb, val) STORE_PARENT_DIRECT(p, fb, ((UInt32)(val) << NUM_BITS)) +#define UPDATE_E(eHi) eHi += 1 << NUM_BITS; +#endif -void Huffman_Generate(const UInt32 *freqs, UInt32 *p, Byte *lens, UInt32 numSymbols, UInt32 maxLen) +void Huffman_Generate(const UInt32 *freqs, UInt32 *p, Byte *lens, unsigned numSymbols, unsigned maxLen) { - UInt32 num = 0; - /* if (maxLen > 10) maxLen = 10; */ +#if NUM_COUNTERS > 2 + unsigned counters[NUM_COUNTERS]; +#endif +#if 1 && NUM_COUNTERS > (kMaxLen + 4) * 2 + #define lenCounters (counters) + #define codes (counters + kMaxLen + 4) +#else + unsigned lenCounters[kMaxLen + 1]; + UInt32 codes[kMaxLen + 1]; +#endif + + unsigned num; { - UInt32 i; - - #ifdef HUFFMAN_SPEED_OPT + unsigned i; + // UInt32 sum = 0; + +#if NUM_COUNTERS > 2 - UInt32 counters[NUM_COUNTERS]; +#define CTR_ITEM_FOR_FREQ(freq) \ + counters[(freq) >= NUM_COUNTERS - 1 ? NUM_COUNTERS - 1 : (unsigned)(freq)] + for (i = 0; i < NUM_COUNTERS; i++) counters[i] = 0; - for (i = 0; i < numSymbols; i++) + memset(lens, 0, numSymbols); { - UInt32 freq = freqs[i]; - counters[(freq < NUM_COUNTERS - 1) ? freq : NUM_COUNTERS - 1]++; + const UInt32 *fp = freqs + numSymbols; +#define NUM_UNROLLS 1 +#if NUM_UNROLLS > 1 // use 1 if odd (numSymbols) is possisble + if (numSymbols & 1) + { + UInt32 f; + f = *--fp; CTR_ITEM_FOR_FREQ(f)++; + // sum += f; + } +#endif + do + { + UInt32 f; + fp -= NUM_UNROLLS; + f = fp[0]; CTR_ITEM_FOR_FREQ(f)++; + // sum += f; +#if NUM_UNROLLS > 1 + f = fp[1]; CTR_ITEM_FOR_FREQ(f)++; + // sum += f; +#endif + } + while (fp != freqs); } - - for (i = 1; i < NUM_COUNTERS; i++) +#if 0 + printf("\nsum=%8u numSymbols =%3u ctrs:", sum, numSymbols); { - UInt32 temp = counters[i]; - counters[i] = num; - num += temp; + unsigned k = 0; + for (k = 0; k < NUM_COUNTERS; k++) + printf(" %u", counters[k]); } - - for (i = 0; i < numSymbols; i++) +#endif + + num = counters[1]; + counters[1] = 0; + for (i = 2; i != NUM_COUNTERS; i += 2) { - UInt32 freq = freqs[i]; - if (freq == 0) - lens[i] = 0; - else - p[counters[((freq < NUM_COUNTERS - 1) ? freq : NUM_COUNTERS - 1)]++] = i | (freq << NUM_BITS); + unsigned c; + c = (counters )[i]; (counters )[i] = num; num += c; + c = (counters + 1)[i]; (counters + 1)[i] = num; num += c; + } + counters[0] = num; // we want to write (freq==0) symbols to the end of (p) array + { + i = 0; + do + { + const UInt32 f = freqs[i]; +#if 0 + if (f == 0) lens[i] = 0; else +#endif + p[CTR_ITEM_FOR_FREQ(f)++] = i | (f << NUM_BITS); + } + while (++i != numSymbols); } - counters[0] = 0; HeapSort(p + counters[NUM_COUNTERS - 2], counters[NUM_COUNTERS - 1] - counters[NUM_COUNTERS - 2]); - #else - +#else // NUM_COUNTERS <= 2 + + num = 0; for (i = 0; i < numSymbols; i++) { - UInt32 freq = freqs[i]; + const UInt32 freq = freqs[i]; if (freq == 0) lens[i] = 0; else @@ -62,17 +127,27 @@ } HeapSort(p, num); - #endif +#endif } - if (num < 2) + if (num <= 2) { unsigned minCode = 0; unsigned maxCode = 1; - if (num == 1) + if (num) { - maxCode = (unsigned)p[0] & MASK; - if (maxCode == 0) + maxCode = (unsigned)p[(size_t)num - 1] & MASK; + if (num == 2) + { + minCode = (unsigned)p[0] & MASK; + if (minCode > maxCode) + { + const unsigned temp = minCode; + minCode = maxCode; + maxCode = temp; + } + } + else if (maxCode == 0) maxCode++; } p[minCode] = 0; @@ -80,69 +155,191 @@ lens[minCode] = lens[maxCode] = 1; return; } - { - UInt32 b, e, i; - - i = b = e = 0; - do - { - UInt32 n, m, freq; - n = (i != num && (b == e || (p[i] >> NUM_BITS) <= (p[b] >> NUM_BITS))) ? i++ : b++; - freq = (p[n] & ~MASK); - p[n] = (p[n] & MASK) | (e << NUM_BITS); - m = (i != num && (b == e || (p[i] >> NUM_BITS) <= (p[b] >> NUM_BITS))) ? i++ : b++; - freq += (p[m] & ~MASK); - p[m] = (p[m] & MASK) | (e << NUM_BITS); - p[e] = (p[e] & MASK) | freq; - e++; - } - while (num - e > 1); - + unsigned i; + for (i = 0; i <= kMaxLen; i++) + lenCounters[i] = 0; + lenCounters[1] = 2; // by default root node has 2 child leaves at level 1. + } + // if (num != 2) + { + // num > 2 + // the binary tree will contain (num - 1) internal nodes. + // p[num - 2] will be root node of binary tree. + UInt32 *b; + UInt32 *n; + // first node will have two leaf childs: p[0] and p[1]: + // p[0] += p[1] & FREQ_MASK; // set frequency sum of child leafs + // if (pi == n) exit(0); + // if (pi != n) { - UInt32 lenCounters[kMaxLen + 1]; - for (i = 0; i <= kMaxLen; i++) - lenCounters[i] = 0; - - p[--e] &= MASK; - lenCounters[1] = 2; - while (e != 0) - { - UInt32 len = (p[p[--e] >> NUM_BITS] >> NUM_BITS) + 1; - p[e] = (p[e] & MASK) | (len << NUM_BITS); - if (len >= maxLen) - for (len = maxLen - 1; lenCounters[len] == 0; len--); - lenCounters[len]--; - lenCounters[(size_t)len + 1] += 2; - } - + UInt32 fb = (p[1] & FREQ_MASK) + p[0]; + UInt32 f = p[2] & FREQ_MASK; + const UInt32 *pi = p + 2; + UInt32 *e = p; + UInt32 eHi = 0; + n = p + num; + b = p; + // p[0] = fb; + for (;;) { - UInt32 len; - i = 0; - for (len = maxLen; len != 0; len--) + // (b <= e) + UInt32 sum; + e++; + UPDATE_E(eHi) + + // (b < e) + + // p range : high bits + // [0, b) : parent : processed nodes that have parent and childs + // [b, e) : FREQ : non-processed nodes that have no parent but have childs + // [e, pi) : FREQ : processed leaves for which parent node was created + // [pi, n) : FREQ : non-processed leaves for which parent node was not created + + // first child + // note : (*b < f) is same result as ((*b & FREQ_MASK) < f) + if (fb < f) + { + // node freq is smaller + sum = fb & FREQ_MASK; + STORE_PARENT_DIRECT (b, fb, eHi) + b++; + fb = *b; + if (b == e) + { + if (++pi == n) + break; + sum += f; + fb &= MASK; + fb |= sum; + *e = fb; + f = *pi & FREQ_MASK; + continue; + } + } + else if (++pi == n) + { + STORE_PARENT_DIRECT (b, fb, eHi) + b++; + break; + } + else + { + sum = f; + f = *pi & FREQ_MASK; + } + + // (b < e) + + // second child + if (fb < f) { - UInt32 k; - for (k = lenCounters[len]; k != 0; k--) - lens[p[i++] & MASK] = (Byte)len; + sum += fb; + sum &= FREQ_MASK; + STORE_PARENT_DIRECT (b, fb, eHi) + b++; + *e = (*e & MASK) | sum; // set frequency sum + // (b <= e) is possible here + fb = *b; + } + else if (++pi == n) + break; + else + { + sum += f; + f = *pi & FREQ_MASK; + *e = (*e & MASK) | sum; // set frequency sum } } - + } + + // printf("\nnum-e=%3u, numSymbols=%3u, num=%3u, b=%3u", n - e, numSymbols, n - p, b - p); + { + n -= 2; + *n &= MASK; // root node : we clear high bits (zero bits mean level == 0) + if (n != b) { - UInt32 nextCodes[kMaxLen + 1]; + // We go here, if we have some number of non-created nodes up to root. + // We process them in simplified code: + // position of parent for each pair of nodes is known. + // n[-2], n[-1] : current pair of child nodes + // (p1) : parent node for current pair. + UInt32 *p1 = n; + do { - UInt32 code = 0; - UInt32 len; - for (len = 1; len <= kMaxLen; len++) - nextCodes[len] = code = (code + lenCounters[(size_t)len - 1]) << 1; + const unsigned len = LOAD_PARENT(p1) + 1; + p1--; + (lenCounters )[len] -= 2; // we remove 2 leaves from level (len) + (lenCounters + 1)[len] += 2 * 2; // we add 4 leaves at level (len + 1) + n -= 2; + STORE_PARENT (n , n[0], len) + STORE_PARENT (n + 1, n[1], len) } - /* if (code + lenCounters[kMaxLen] - 1 != (1 << kMaxLen) - 1) throw 1; */ - + while (n != b); + } + } + + if (b != p) + { + // we detect level of each node (realtive to root), + // and update lenCounters[]. + // We process only intermediate nodes and we don't process leaves. + do + { + // if (ii < b) : parent_bits_of (p[ii]) == index of parent node : ii < (p[ii]) + // if (ii >= b) : parent_bits_of (p[ii]) == level of this (ii) node in tree + unsigned len; + b--; + len = (unsigned)LOAD_PARENT(p + LOAD_PARENT(b)) + 1; + STORE_PARENT (b, *b, len) + if (len >= maxLen) { - UInt32 k; - for (k = 0; k < numSymbols; k++) - p[k] = nextCodes[lens[k]]++; + // We are not allowed to create node at level (maxLen) and higher, + // because all leaves must be placed to level (maxLen) or lower. + // We find nearest allowed leaf and place current node to level of that leaf: + for (len = maxLen - 1; lenCounters[len] == 0; len--) {} } + lenCounters[len]--; // we remove 1 leaf from level (len) + (lenCounters + 1)[len] += 2; // we add 2 leaves at level (len + 1) + } + while (b != p); + } + } + { + { + unsigned len = maxLen; + const UInt32 *p2 = p; + do + { + unsigned k = lenCounters[len]; + if (k) + do + lens[(unsigned)*p2++ & MASK] = (Byte)len; + while (--k); + } + while (--len); + } + codes[0] = 0; // we don't want garbage values to be written to p[] array. + // codes[1] = 0; + { + UInt32 code = 0; + unsigned len; + for (len = 0; len < kMaxLen; len++) + (codes + 1)[len] = code = (code + lenCounters[len]) << 1; + } + /* if (code + lenCounters[kMaxLen] - 1 != (1 << kMaxLen) - 1) throw 1; */ + { + const Byte * const limit = lens + numSymbols; + do + { + unsigned len; + UInt32 c; + len = lens[0]; c = codes[len]; p[0] = c; codes[len] = c + 1; + // len = lens[1]; c = codes[len]; p[1] = c; codes[len] = c + 1; + p += 1; + lens += 1; } + while (lens != limit); } } } @@ -150,5 +347,14 @@ #undef kMaxLen #undef NUM_BITS #undef MASK +#undef FREQ_MASK #undef NUM_COUNTERS -#undef HUFFMAN_SPEED_OPT +#undef CTR_ITEM_FOR_FREQ +#undef LOAD_PARENT +#undef STORE_PARENT +#undef STORE_PARENT_DIRECT +#undef UPDATE_E +#undef HI_HALF_OFFSET +#undef NUM_UNROLLS +#undef lenCounters +#undef codes diff -Nru 7zip-24.09+dfsg/C/HuffEnc.h 7zip-25.01+dfsg/C/HuffEnc.h --- 7zip-24.09+dfsg/C/HuffEnc.h 2023-03-05 17:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/HuffEnc.h 2025-03-20 14:00:00.000000000 +0000 @@ -1,5 +1,5 @@ /* HuffEnc.h -- Huffman encoding -2023-03-05 : Igor Pavlov : Public domain */ +Igor Pavlov : Public domain */ #ifndef ZIP7_INC_HUFF_ENC_H #define ZIP7_INC_HUFF_ENC_H @@ -8,14 +8,14 @@ EXTERN_C_BEGIN +#define Z7_HUFFMAN_LEN_MAX 16 /* Conditions: - num <= 1024 = 2 ^ NUM_BITS + 2 <= num <= 1024 = 2 ^ NUM_BITS Sum(freqs) < 4M = 2 ^ (32 - NUM_BITS) - maxLen <= 16 = kMaxLen + 1 <= maxLen <= 16 = Z7_HUFFMAN_LEN_MAX Num_Items(p) >= HUFFMAN_TEMP_SIZE(num) */ - void Huffman_Generate(const UInt32 *freqs, UInt32 *p, Byte *lens, UInt32 num, UInt32 maxLen); EXTERN_C_END diff -Nru 7zip-24.09+dfsg/C/LzFind.c 7zip-25.01+dfsg/C/LzFind.c --- 7zip-24.09+dfsg/C/LzFind.c 2024-03-01 07:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/LzFind.c 2025-07-25 07:00:00.000000000 +0000 @@ -1,5 +1,5 @@ /* LzFind.c -- Match finder for LZ algorithms -2024-03-01 : Igor Pavlov : Public domain */ +: Igor Pavlov : Public domain */ #include "Precomp.h" @@ -404,7 +404,7 @@ const unsigned nbMax = (p->numHashBytes == 2 ? 16 : (p->numHashBytes == 3 ? 24 : 32)); - if (numBits > nbMax) + if (numBits >= nbMax) numBits = nbMax; if (numBits >= 32) hs = (UInt32)0 - 1; @@ -416,14 +416,14 @@ hs |= (256 << kLzHash_CrcShift_2) - 1; { const UInt32 hs2 = MatchFinder_GetHashMask2(p, historySize); - if (hs > hs2) + if (hs >= hs2) hs = hs2; } hsCur = hs; if (p->expectedDataSize < historySize) { const UInt32 hs2 = MatchFinder_GetHashMask2(p, (UInt32)p->expectedDataSize); - if (hsCur > hs2) + if (hsCur >= hs2) hsCur = hs2; } } @@ -434,7 +434,7 @@ if (p->expectedDataSize < historySize) { hsCur = MatchFinder_GetHashMask(p, (UInt32)p->expectedDataSize); - if (hsCur > hs) // is it possible? + if (hsCur >= hs) // is it possible? hsCur = hs; } } @@ -598,7 +598,7 @@ #ifdef MY_CPU_X86_OR_AMD64 #if defined(__clang__) && (__clang_major__ >= 4) \ - || defined(Z7_GCC_VERSION) && (Z7_GCC_VERSION >= 40701) + || defined(Z7_GCC_VERSION) && (Z7_GCC_VERSION >= 40900) // || defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1900) #define USE_LZFIND_SATUR_SUB_128 @@ -890,7 +890,7 @@ return d; { const Byte *pb = cur - delta; - curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)]; + curMatch = son[_cyclicBufferPos - delta + (_cyclicBufferPos < delta ? _cyclicBufferSize : 0)]; if (pb[maxLen] == cur[maxLen] && *pb == *cur) { UInt32 len = 0; @@ -925,7 +925,7 @@ break; { ptrdiff_t diff; - curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)]; + curMatch = son[_cyclicBufferPos - delta + (_cyclicBufferPos < delta ? _cyclicBufferSize : 0)]; diff = (ptrdiff_t)0 - (ptrdiff_t)delta; if (cur[maxLen] == cur[(ptrdiff_t)maxLen + diff]) { @@ -972,7 +972,7 @@ // if (curMatch >= pos) { *ptr0 = *ptr1 = kEmptyHashValue; return NULL; } cmCheck = (UInt32)(pos - _cyclicBufferSize); - if ((UInt32)pos <= _cyclicBufferSize) + if ((UInt32)pos < _cyclicBufferSize) cmCheck = 0; if (cmCheck < curMatch) @@ -980,7 +980,7 @@ { const UInt32 delta = pos - curMatch; { - CLzRef *pair = son + ((size_t)(_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); + CLzRef *pair = son + ((size_t)(_cyclicBufferPos - delta + (_cyclicBufferPos < delta ? _cyclicBufferSize : 0)) << 1); const Byte *pb = cur - delta; unsigned len = (len0 < len1 ? len0 : len1); const UInt32 pair0 = pair[0]; @@ -1039,7 +1039,7 @@ UInt32 cmCheck; cmCheck = (UInt32)(pos - _cyclicBufferSize); - if ((UInt32)pos <= _cyclicBufferSize) + if ((UInt32)pos < _cyclicBufferSize) cmCheck = 0; if (// curMatch >= pos || // failure @@ -1048,7 +1048,7 @@ { const UInt32 delta = pos - curMatch; { - CLzRef *pair = son + ((size_t)(_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); + CLzRef *pair = son + ((size_t)(_cyclicBufferPos - delta + (_cyclicBufferPos < delta ? _cyclicBufferSize : 0)) << 1); const Byte *pb = cur - delta; unsigned len = (len0 < len1 ? len0 : len1); if (pb[len] == cur[len]) @@ -1595,7 +1595,7 @@ UInt32 pos = p->pos; \ UInt32 num2 = num; \ /* (p->pos == p->posLimit) is not allowed here !!! */ \ - { const UInt32 rem = p->posLimit - pos; if (num2 > rem) num2 = rem; } \ + { const UInt32 rem = p->posLimit - pos; if (num2 >= rem) num2 = rem; } \ num -= num2; \ { const UInt32 cycPos = p->cyclicBufferPos; \ son = p->son + cycPos; \ diff -Nru 7zip-24.09+dfsg/C/LzFindMt.c 7zip-25.01+dfsg/C/LzFindMt.c --- 7zip-24.09+dfsg/C/LzFindMt.c 2024-01-22 14:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/LzFindMt.c 2025-06-30 16:00:00.000000000 +0000 @@ -1,5 +1,5 @@ /* LzFindMt.c -- multithreaded Match finder for LZ algorithms -2024-01-22 : Igor Pavlov : Public domain */ +: Igor Pavlov : Public domain */ #include "Precomp.h" @@ -82,6 +82,8 @@ Z7_NO_INLINE static void MtSync_Construct(CMtSync *p) { + p->affinityGroup = -1; + p->affinityInGroup = 0; p->affinity = 0; p->wasCreated = False; p->csWasInitialized = False; @@ -259,6 +261,12 @@ // return ERROR_TOO_MANY_POSTS; // for debug // return EINVAL; // for debug +#ifdef _WIN32 + if (p->affinityGroup >= 0) + wres = Thread_Create_With_Group(&p->thread, startAddress, obj, + (unsigned)(UInt32)p->affinityGroup, (CAffinityMask)p->affinityInGroup); + else +#endif if (p->affinity != 0) wres = Thread_Create_With_Affinity(&p->thread, startAddress, obj, (CAffinityMask)p->affinity); else diff -Nru 7zip-24.09+dfsg/C/LzFindMt.h 7zip-25.01+dfsg/C/LzFindMt.h --- 7zip-24.09+dfsg/C/LzFindMt.h 2024-01-22 14:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/LzFindMt.h 2025-05-10 07:00:00.000000000 +0000 @@ -1,5 +1,5 @@ /* LzFindMt.h -- multithreaded Match finder for LZ algorithms -2024-01-22 : Igor Pavlov : Public domain */ +: Igor Pavlov : Public domain */ #ifndef ZIP7_INC_LZ_FIND_MT_H #define ZIP7_INC_LZ_FIND_MT_H @@ -12,8 +12,10 @@ typedef struct { UInt32 numProcessedBlocks; - CThread thread; + Int32 affinityGroup; + UInt64 affinityInGroup; UInt64 affinity; + CThread thread; BoolInt wasCreated; BoolInt needStart; diff -Nru 7zip-24.09+dfsg/C/Lzma2Enc.c 7zip-25.01+dfsg/C/Lzma2Enc.c --- 7zip-24.09+dfsg/C/Lzma2Enc.c 2023-04-13 08:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/Lzma2Enc.c 2025-06-30 17:00:00.000000000 +0000 @@ -1,5 +1,5 @@ /* Lzma2Enc.c -- LZMA2 Encoder -2023-04-13 : Igor Pavlov : Public domain */ +: Igor Pavlov : Public domain */ #include "Precomp.h" @@ -235,6 +235,7 @@ p->numBlockThreads_Reduced = -1; p->numBlockThreads_Max = -1; p->numTotalThreads = -1; + p->numThreadGroups = 0; } void Lzma2EncProps_Normalize(CLzma2EncProps *p) @@ -781,6 +782,7 @@ } p->mtCoder.numThreadsMax = (unsigned)p->props.numBlockThreads_Max; + p->mtCoder.numThreadGroups = p->props.numThreadGroups; p->mtCoder.expectedDataSize = p->expectedDataSize; { diff -Nru 7zip-24.09+dfsg/C/Lzma2Enc.h 7zip-25.01+dfsg/C/Lzma2Enc.h --- 7zip-24.09+dfsg/C/Lzma2Enc.h 2023-04-13 08:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/Lzma2Enc.h 2025-06-28 12:00:00.000000000 +0000 @@ -18,6 +18,7 @@ int numBlockThreads_Reduced; int numBlockThreads_Max; int numTotalThreads; + unsigned numThreadGroups; // 0 : no groups } CLzma2EncProps; void Lzma2EncProps_Init(CLzma2EncProps *p); diff -Nru 7zip-24.09+dfsg/C/LzmaEnc.c 7zip-25.01+dfsg/C/LzmaEnc.c --- 7zip-24.09+dfsg/C/LzmaEnc.c 2024-11-12 07:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/LzmaEnc.c 2025-05-11 14:00:00.000000000 +0000 @@ -62,7 +62,9 @@ p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1; p->numHashOutBits = 0; p->writeEndMark = 0; + p->affinityGroup = -1; p->affinity = 0; + p->affinityInGroup = 0; } void LzmaEncProps_Normalize(CLzmaEncProps *p) @@ -598,6 +600,10 @@ p->multiThread = (props.numThreads > 1); p->matchFinderMt.btSync.affinity = p->matchFinderMt.hashSync.affinity = props.affinity; + p->matchFinderMt.btSync.affinityGroup = + p->matchFinderMt.hashSync.affinityGroup = props.affinityGroup; + p->matchFinderMt.btSync.affinityInGroup = + p->matchFinderMt.hashSync.affinityInGroup = props.affinityInGroup; #endif return SZ_OK; diff -Nru 7zip-24.09+dfsg/C/LzmaEnc.h 7zip-25.01+dfsg/C/LzmaEnc.h --- 7zip-24.09+dfsg/C/LzmaEnc.h 2023-04-13 08:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/LzmaEnc.h 2025-05-10 07:00:00.000000000 +0000 @@ -1,5 +1,5 @@ /* LzmaEnc.h -- LZMA Encoder -2023-04-13 : Igor Pavlov : Public domain */ +: Igor Pavlov : Public domain */ #ifndef ZIP7_INC_LZMA_ENC_H #define ZIP7_INC_LZMA_ENC_H @@ -29,11 +29,13 @@ int numThreads; /* 1 or 2, default = 2 */ // int _pad; + Int32 affinityGroup; UInt64 reduceSize; /* estimated size of data that will be compressed. default = (UInt64)(Int64)-1. Encoder uses this value to reduce dictionary size */ UInt64 affinity; + UInt64 affinityInGroup; } CLzmaEncProps; void LzmaEncProps_Init(CLzmaEncProps *p); diff -Nru 7zip-24.09+dfsg/C/MtCoder.c 7zip-25.01+dfsg/C/MtCoder.c --- 7zip-24.09+dfsg/C/MtCoder.c 2023-09-07 13:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/MtCoder.c 2025-07-04 06:00:00.000000000 +0000 @@ -1,5 +1,5 @@ /* MtCoder.c -- Multi-thread Coder -2023-09-07 : Igor Pavlov : Public domain */ +: Igor Pavlov : Public domain */ #include "Precomp.h" @@ -39,14 +39,28 @@ static THREAD_FUNC_DECL ThreadFunc(void *pp); -static SRes MtCoderThread_CreateAndStart(CMtCoderThread *t) +static SRes MtCoderThread_CreateAndStart(CMtCoderThread *t +#ifdef _WIN32 + , CMtCoder * const mtc +#endif + ) { WRes wres = AutoResetEvent_OptCreate_And_Reset(&t->startEvent); + // printf("\n====== MtCoderThread_CreateAndStart : \n"); if (wres == 0) { t->stop = False; if (!Thread_WasCreated(&t->thread)) - wres = Thread_Create(&t->thread, ThreadFunc, t); + { +#ifdef _WIN32 + if (mtc->numThreadGroups) + wres = Thread_Create_With_Group(&t->thread, ThreadFunc, t, + ThreadNextGroup_GetNext(&mtc->nextGroup), // group + 0); // affinityMask + else +#endif + wres = Thread_Create(&t->thread, ThreadFunc, t); + } if (wres == 0) wres = Event_Set(&t->startEvent); } @@ -56,6 +70,7 @@ } +Z7_FORCE_INLINE static void MtCoderThread_Destruct(CMtCoderThread *t) { if (Thread_WasCreated(&t->thread)) @@ -85,7 +100,7 @@ static SRes ThreadFunc2(CMtCoderThread *t) { - CMtCoder *mtc = t->mtCoder; + CMtCoder * const mtc = t->mtCoder; for (;;) { @@ -185,7 +200,11 @@ if (mtc->numStartedThreads < mtc->numStartedThreadsLimit && mtc->expectedDataSize != readProcessed) { - res = MtCoderThread_CreateAndStart(&mtc->threads[mtc->numStartedThreads]); + res = MtCoderThread_CreateAndStart(&mtc->threads[mtc->numStartedThreads] +#ifdef _WIN32 + , mtc +#endif + ); if (res == SZ_OK) mtc->numStartedThreads++; else @@ -221,7 +240,7 @@ } { - CMtCoderBlock *block = &mtc->blocks[bi]; + CMtCoderBlock * const block = &mtc->blocks[bi]; block->res = res; block->bufIndex = bufIndex; block->finished = finished; @@ -311,7 +330,7 @@ static THREAD_FUNC_DECL ThreadFunc(void *pp) { - CMtCoderThread *t = (CMtCoderThread *)pp; + CMtCoderThread * const t = (CMtCoderThread *)pp; for (;;) { if (Event_Wait(&t->startEvent) != 0) @@ -319,7 +338,7 @@ if (t->stop) return 0; { - SRes res = ThreadFunc2(t); + const SRes res = ThreadFunc2(t); CMtCoder *mtc = t->mtCoder; if (res != SZ_OK) { @@ -328,7 +347,7 @@ #ifndef MTCODER_USE_WRITE_THREAD { - unsigned numFinished = (unsigned)InterlockedIncrement(&mtc->numFinishedThreads); + const unsigned numFinished = (unsigned)InterlockedIncrement(&mtc->numFinishedThreads); if (numFinished == mtc->numStartedThreads) if (Event_Set(&mtc->finishedEvent) != 0) return (THREAD_FUNC_RET_TYPE)SZ_ERROR_THREAD; @@ -346,6 +365,7 @@ p->blockSize = 0; p->numThreadsMax = 0; + p->numThreadGroups = 0; p->expectedDataSize = (UInt64)(Int64)-1; p->inStream = NULL; @@ -429,6 +449,8 @@ unsigned i; SRes res = SZ_OK; + // printf("\n====== MtCoder_Code : \n"); + if (numThreads > MTCODER_THREADS_MAX) numThreads = MTCODER_THREADS_MAX; numBlocksMax = MTCODER_GET_NUM_BLOCKS_FROM_THREADS(numThreads); @@ -492,11 +514,22 @@ p->numStartedThreadsLimit = numThreads; p->numStartedThreads = 0; + ThreadNextGroup_Init(&p->nextGroup, p->numThreadGroups, 0); // startGroup // for (i = 0; i < numThreads; i++) { + // here we create new thread for first block. + // And each new thread will create another new thread after block reading + // until numStartedThreadsLimit is reached. CMtCoderThread *nextThread = &p->threads[p->numStartedThreads++]; - RINOK(MtCoderThread_CreateAndStart(nextThread)) + { + const SRes res2 = MtCoderThread_CreateAndStart(nextThread +#ifdef _WIN32 + , p +#endif + ); + RINOK(res2) + } } RINOK_THREAD(Event_Set(&p->readEvent)) @@ -513,9 +546,9 @@ RINOK_THREAD(Event_Wait(&p->writeEvents[bi])) { - const CMtCoderBlock *block = &p->blocks[bi]; - unsigned bufIndex = block->bufIndex; - BoolInt finished = block->finished; + const CMtCoderBlock * const block = &p->blocks[bi]; + const unsigned bufIndex = block->bufIndex; + const BoolInt finished = block->finished; if (res == SZ_OK && block->res != SZ_OK) res = block->res; @@ -545,7 +578,7 @@ } #else { - WRes wres = Event_Wait(&p->finishedEvent); + const WRes wres = Event_Wait(&p->finishedEvent); res = MY_SRes_HRESULT_FROM_WRes(wres); } #endif diff -Nru 7zip-24.09+dfsg/C/MtCoder.h 7zip-25.01+dfsg/C/MtCoder.h --- 7zip-24.09+dfsg/C/MtCoder.h 2023-04-13 08:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/MtCoder.h 2025-05-15 06:00:00.000000000 +0000 @@ -1,5 +1,5 @@ /* MtCoder.h -- Multi-thread Coder -2023-04-13 : Igor Pavlov : Public domain */ +: Igor Pavlov : Public domain */ #ifndef ZIP7_INC_MT_CODER_H #define ZIP7_INC_MT_CODER_H @@ -16,7 +16,7 @@ #ifndef Z7_ST #define MTCODER_GET_NUM_BLOCKS_FROM_THREADS(numThreads) ((numThreads) + (numThreads) / 8 + 1) - #define MTCODER_THREADS_MAX 64 + #define MTCODER_THREADS_MAX 256 #define MTCODER_BLOCKS_MAX (MTCODER_GET_NUM_BLOCKS_FROM_THREADS(MTCODER_THREADS_MAX) + 3) #else #define MTCODER_THREADS_MAX 1 @@ -77,6 +77,7 @@ size_t blockSize; /* size of input block */ unsigned numThreadsMax; + unsigned numThreadGroups; UInt64 expectedDataSize; ISeqInStreamPtr inStream; @@ -125,6 +126,8 @@ CMtProgress mtProgress; CMtCoderBlock blocks[MTCODER_BLOCKS_MAX]; CMtCoderThread threads[MTCODER_THREADS_MAX]; + + CThreadNextGroup nextGroup; } CMtCoder; diff -Nru 7zip-24.09+dfsg/C/Sha512.c 7zip-25.01+dfsg/C/Sha512.c --- 7zip-24.09+dfsg/C/Sha512.c 2024-11-09 10:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/Sha512.c 2024-12-03 11:00:00.000000000 +0000 @@ -439,26 +439,78 @@ +// #define Z7_SHA512_PROBE_DEBUG // for debug -#if defined(_WIN32) && defined(Z7_COMPILER_SHA512_SUPPORTED) \ - && defined(MY_CPU_ARM64) // we can disable this check to debug in x64 +#if defined(Z7_SHA512_PROBE_DEBUG) || defined(Z7_COMPILER_SHA512_SUPPORTED) -#if 1 // 0 for debug +#if defined(Z7_SHA512_PROBE_DEBUG) \ + || defined(_WIN32) && defined(MY_CPU_ARM64) +#ifndef Z7_SHA512_USE_PROBE +#define Z7_SHA512_USE_PROBE +#endif +#endif -#include "7zWindows.h" -// #include -#if 0 && defined(MY_CPU_X86_OR_AMD64) -#include // for debug : for __ud2() +#ifdef Z7_SHA512_USE_PROBE + +#ifdef Z7_SHA512_PROBE_DEBUG +#include +#define PRF(x) x +#else +#define PRF(x) #endif -BoolInt CPU_IsSupported_SHA512(void) +#if 0 || !defined(_MSC_VER) // 1 || : for debug LONGJMP mode +// MINGW doesn't support __try. So we use signal() / longjmp(). +// Note: signal() / longjmp() probably is not thread-safe. +// So we must call Sha512Prepare() from main thread at program start. +#ifndef Z7_SHA512_USE_LONGJMP +#define Z7_SHA512_USE_LONGJMP +#endif +#endif + +#ifdef Z7_SHA512_USE_LONGJMP +#include +#include +static jmp_buf g_Sha512_jmp_buf; +// static int g_Sha512_Unsupported; + +#if defined(__GNUC__) && (__GNUC__ >= 8) \ + || defined(__clang__) && (__clang_major__ >= 3) + __attribute__((noreturn)) +#endif +static void Z7_CDECL Sha512_signal_Handler(int v) { + PRF(printf("======== Sha512_signal_Handler = %x\n", (unsigned)v);) + // g_Sha512_Unsupported = 1; + longjmp(g_Sha512_jmp_buf, 1); +} +#endif // Z7_SHA512_USE_LONGJMP + + +#if defined(_WIN32) +#include "7zWindows.h" +#endif + #if defined(MY_CPU_ARM64) +// #define Z7_SHA512_USE_SIMPLIFIED_PROBE // for debug +#endif + +#ifdef Z7_SHA512_USE_SIMPLIFIED_PROBE +#include +#if defined(__clang__) + __attribute__((__target__("sha3"))) +#elif !defined(_MSC_VER) + __attribute__((__target__("arch=armv8.2-a+sha3"))) +#endif +#endif +static BoolInt CPU_IsSupported_SHA512_Probe(void) +{ + PRF(printf("\n== CPU_IsSupported_SHA512_Probe\n");) +#if defined(_WIN32) && defined(MY_CPU_ARM64) // we have no SHA512 flag for IsProcessorFeaturePresent() still. if (!CPU_IsSupported_CRYPTO()) return False; -#endif - // printf("\nCPU_IsSupported_SHA512\n"); + PRF(printf("==== Registry check\n");) { // we can't read ID_AA64ISAR0_EL1 register from application. // but ID_AA64ISAR0_EL1 register is mapped to "CP 4030" registry value. @@ -486,6 +538,7 @@ // 2 : SHA256 and SHA512 implemented } } +#endif // defined(_WIN32) && defined(MY_CPU_ARM64) #if 1 // 0 for debug to disable SHA512 PROBE code @@ -509,59 +562,97 @@ Are there any ways to fix the problems with arm64-wine and x64-SDE cases? */ - // printf("\n========== CPU_IsSupported_SHA512 PROBE ========\n"); + PRF(printf("==== CPU_IsSupported_SHA512 PROBE\n");) { + BoolInt isSupported = False; +#ifdef Z7_SHA512_USE_LONGJMP + void (Z7_CDECL *signal_prev)(int); + /* + if (g_Sha512_Unsupported) + { + PRF(printf("==== g_Sha512_Unsupported\n");) + return False; + } + */ + printf("====== signal(SIGILL)\n"); + signal_prev = signal(SIGILL, Sha512_signal_Handler); + if (signal_prev == SIG_ERR) + { + PRF(printf("====== signal fail\n");) + return False; + } + // PRF(printf("==== signal_prev = %p\n", (void *)signal_prev);) + // docs: Before the specified function is executed, + // the value of func is set to SIG_DFL. + // So we can exit if (setjmp(g_Sha512_jmp_buf) != 0). + PRF(printf("====== setjmp\n");) + if (!setjmp(g_Sha512_jmp_buf)) +#else // Z7_SHA512_USE_LONGJMP + +#ifdef _MSC_VER #ifdef __clang_major__ #pragma GCC diagnostic ignored "-Wlanguage-extension-token" #endif __try +#endif +#endif // Z7_SHA512_USE_LONGJMP + { -#if 0 // 1 : for debug (reduced version to detect sha512) +#if defined(Z7_COMPILER_SHA512_SUPPORTED) +#ifdef Z7_SHA512_USE_SIMPLIFIED_PROBE + // simplified sha512 check for arm64: const uint64x2_t a = vdupq_n_u64(1); const uint64x2_t b = vsha512hq_u64(a, a, a); + PRF(printf("======== vsha512hq_u64 probe\n");) if ((UInt32)vgetq_lane_u64(b, 0) == 0x11800002) - return True; #else MY_ALIGN(16) UInt64 temp[SHA512_NUM_DIGEST_WORDS + SHA512_NUM_BLOCK_WORDS]; memset(temp, 0x5a, sizeof(temp)); -#if 0 && defined(MY_CPU_X86_OR_AMD64) - __ud2(); // for debug : that exception is not problem for SDE -#endif -#if 1 + PRF(printf("======== Sha512_UpdateBlocks_HW\n");) Sha512_UpdateBlocks_HW(temp, (const Byte *)(const void *)(temp + SHA512_NUM_DIGEST_WORDS), 1); - // printf("\n==== t = %x\n", (UInt32)temp[0]); + // PRF(printf("======== t = %x\n", (UInt32)temp[0]);) if ((UInt32)temp[0] == 0xa33cfdf7) +#endif { - // printf("\n=== PROBE SHA512: SHA512 supported\n"); - return True; + PRF(printf("======== PROBE SHA512: SHA512 is supported\n");) + isSupported = True; } +#else // Z7_COMPILER_SHA512_SUPPORTED + // for debug : we generate bad instrction or raise exception. + // __except() doesn't catch raise() calls. +#ifdef Z7_SHA512_USE_LONGJMP + PRF(printf("====== raise(SIGILL)\n");) + raise(SIGILL); +#else +#if defined(_MSC_VER) && defined(MY_CPU_X86) + __asm ud2 #endif -#endif +#endif // Z7_SHA512_USE_LONGJMP +#endif // Z7_COMPILER_SHA512_SUPPORTED } + +#ifdef Z7_SHA512_USE_LONGJMP + PRF(printf("====== restore signal SIGILL\n");) + signal(SIGILL, signal_prev); +#elif _MSC_VER __except (EXCEPTION_EXECUTE_HANDLER) { - // printf("\n==== CPU_IsSupported_SHA512 EXCEPTION_EXECUTE_HANDLER\n"); + PRF(printf("==== CPU_IsSupported_SHA512 __except(EXCEPTION_EXECUTE_HANDLER)\n");) } +#endif + PRF(printf("== return (sha512 supported) = %d\n", isSupported);) + return isSupported; } - return False; #else // without SHA512 PROBE code return True; #endif - } -#else - -BoolInt CPU_IsSupported_SHA512(void) -{ - return False; -} - -#endif -#endif // WIN32 arm64 +#endif // Z7_SHA512_USE_PROBE +#endif // defined(Z7_SHA512_PROBE_DEBUG) || defined(Z7_COMPILER_SHA512_SUPPORTED) void Sha512Prepare(void) @@ -570,10 +661,10 @@ SHA512_FUNC_UPDATE_BLOCKS f, f_hw; f = Sha512_UpdateBlocks; f_hw = NULL; -#ifdef MY_CPU_X86_OR_AMD64 - if (CPU_IsSupported_SHA512() - && CPU_IsSupported_AVX2() - ) +#ifdef Z7_SHA512_USE_PROBE + if (CPU_IsSupported_SHA512_Probe()) +#elif defined(MY_CPU_X86_OR_AMD64) + if (CPU_IsSupported_SHA512() && CPU_IsSupported_AVX2()) #else if (CPU_IsSupported_SHA512()) #endif @@ -583,6 +674,8 @@ } g_SHA512_FUNC_UPDATE_BLOCKS = f; g_SHA512_FUNC_UPDATE_BLOCKS_HW = f_hw; +#elif defined(Z7_SHA512_PROBE_DEBUG) + CPU_IsSupported_SHA512_Probe(); // for debug #endif } diff -Nru 7zip-24.09+dfsg/C/Sort.c 7zip-25.01+dfsg/C/Sort.c --- 7zip-24.09+dfsg/C/Sort.c 2014-04-05 12:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/Sort.c 2025-01-08 09:00:00.000000000 +0000 @@ -1,141 +1,268 @@ /* Sort.c -- Sort functions -2014-04-05 : Igor Pavlov : Public domain */ +: Igor Pavlov : Public domain */ #include "Precomp.h" #include "Sort.h" +#include "CpuArch.h" -#define HeapSortDown(p, k, size, temp) \ - { for (;;) { \ - size_t s = (k << 1); \ - if (s > size) break; \ - if (s < size && p[s + 1] > p[s]) s++; \ - if (temp >= p[s]) break; \ - p[k] = p[s]; k = s; \ - } p[k] = temp; } +#if ( (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) \ + || (defined(__clang__) && Z7_has_builtin(__builtin_prefetch)) \ + ) +// the code with prefetch is slow for small arrays on x86. +// So we disable prefetch for x86. +#ifndef MY_CPU_X86 + // #pragma message("Z7_PREFETCH : __builtin_prefetch") + #define Z7_PREFETCH(a) __builtin_prefetch((a)) +#endif -void HeapSort(UInt32 *p, size_t size) -{ - if (size <= 1) - return; - p--; - { - size_t i = size / 2; - do - { - UInt32 temp = p[i]; - size_t k = i; - HeapSortDown(p, k, size, temp) - } - while (--i != 0); - } - /* - do - { - size_t k = 1; - UInt32 temp = p[size]; - p[size--] = p[1]; - HeapSortDown(p, k, size, temp) - } - while (size > 1); - */ - while (size > 3) - { - UInt32 temp = p[size]; - size_t k = (p[3] > p[2]) ? 3 : 2; - p[size--] = p[1]; - p[1] = p[k]; - HeapSortDown(p, k, size, temp) - } - { - UInt32 temp = p[size]; - p[size] = p[1]; - if (size > 2 && p[2] < temp) - { - p[1] = p[2]; - p[2] = temp; - } - else - p[1] = temp; - } +#elif defined(_WIN32) // || defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "7zWindows.h" + +// NOTE: CLANG/GCC/MSVC can define different values for _MM_HINT_T0 / PF_TEMPORAL_LEVEL_1. +// For example, clang-cl can generate "prefetcht2" instruction for +// PreFetchCacheLine(PF_TEMPORAL_LEVEL_1) call. +// But we want to generate "prefetcht0" instruction. +// So for CLANG/GCC we must use __builtin_prefetch() in code branch above +// instead of PreFetchCacheLine() / _mm_prefetch(). + +// New msvc-x86 compiler generates "prefetcht0" instruction for PreFetchCacheLine() call. +// But old x86 cpus don't support "prefetcht0". +// So we will use PreFetchCacheLine(), only if we are sure that +// generated instruction is supported by all cpus of that isa. +#if defined(MY_CPU_AMD64) \ + || defined(MY_CPU_ARM64) \ + || defined(MY_CPU_IA64) +// we need to use additional braces for (a) in PreFetchCacheLine call, because +// PreFetchCacheLine macro doesn't use braces: +// #define PreFetchCacheLine(l, a) _mm_prefetch((CHAR CONST *) a, l) + // #pragma message("Z7_PREFETCH : PreFetchCacheLine") + #define Z7_PREFETCH(a) PreFetchCacheLine(PF_TEMPORAL_LEVEL_1, (a)) +#endif + +#endif // _WIN32 + + +#define PREFETCH_NO(p,k,s,size) + +#ifndef Z7_PREFETCH + #define SORT_PREFETCH(p,k,s,size) +#else + +// #define PREFETCH_LEVEL 2 // use it if cache line is 32-bytes +#define PREFETCH_LEVEL 3 // it is fast for most cases (64-bytes cache line prefetch) +// #define PREFETCH_LEVEL 4 // it can be faster for big array (128-bytes prefetch) + +#if PREFETCH_LEVEL == 0 + + #define SORT_PREFETCH(p,k,s,size) + +#else // PREFETCH_LEVEL != 0 + +/* +if defined(USE_PREFETCH_FOR_ALIGNED_ARRAY) + we prefetch one value per cache line. + Use it if array is aligned for cache line size (64 bytes) + or if array is small (less than L1 cache size). + +if !defined(USE_PREFETCH_FOR_ALIGNED_ARRAY) + we perfetch all cache lines that can be required. + it can be faster for big unaligned arrays. +*/ + #define USE_PREFETCH_FOR_ALIGNED_ARRAY + +// s == k * 2 +#if 0 && PREFETCH_LEVEL <= 3 && defined(MY_CPU_X86_OR_AMD64) + // x86 supports (lea r1*8+offset) + #define PREFETCH_OFFSET(k,s) ((s) << PREFETCH_LEVEL) +#else + #define PREFETCH_OFFSET(k,s) ((k) << (PREFETCH_LEVEL + 1)) +#endif + +#if 1 && PREFETCH_LEVEL <= 3 && defined(USE_PREFETCH_FOR_ALIGNED_ARRAY) + #define PREFETCH_ADD_OFFSET 0 +#else + // last offset that can be reqiured in PREFETCH_LEVEL step: + #define PREFETCH_RANGE ((2 << PREFETCH_LEVEL) - 1) + #define PREFETCH_ADD_OFFSET PREFETCH_RANGE / 2 +#endif + +#if PREFETCH_LEVEL <= 3 + +#ifdef USE_PREFETCH_FOR_ALIGNED_ARRAY + #define SORT_PREFETCH(p,k,s,size) \ + { const size_t s2 = PREFETCH_OFFSET(k,s) + PREFETCH_ADD_OFFSET; \ + if (s2 <= size) { \ + Z7_PREFETCH((p + s2)); \ + }} +#else /* for unaligned array */ + #define SORT_PREFETCH(p,k,s,size) \ + { const size_t s2 = PREFETCH_OFFSET(k,s) + PREFETCH_RANGE; \ + if (s2 <= size) { \ + Z7_PREFETCH((p + s2 - PREFETCH_RANGE)); \ + Z7_PREFETCH((p + s2)); \ + }} +#endif + +#else // PREFETCH_LEVEL > 3 + +#ifdef USE_PREFETCH_FOR_ALIGNED_ARRAY + #define SORT_PREFETCH(p,k,s,size) \ + { const size_t s2 = PREFETCH_OFFSET(k,s) + PREFETCH_RANGE - 16 / 2; \ + if (s2 <= size) { \ + Z7_PREFETCH((p + s2 - 16)); \ + Z7_PREFETCH((p + s2)); \ + }} +#else /* for unaligned array */ + #define SORT_PREFETCH(p,k,s,size) \ + { const size_t s2 = PREFETCH_OFFSET(k,s) + PREFETCH_RANGE; \ + if (s2 <= size) { \ + Z7_PREFETCH((p + s2 - PREFETCH_RANGE)); \ + Z7_PREFETCH((p + s2 - PREFETCH_RANGE / 2)); \ + Z7_PREFETCH((p + s2)); \ + }} +#endif + +#endif // PREFETCH_LEVEL > 3 +#endif // PREFETCH_LEVEL != 0 +#endif // Z7_PREFETCH + + +#if defined(MY_CPU_ARM64) \ + /* || defined(MY_CPU_AMD64) */ \ + /* || defined(MY_CPU_ARM) && !defined(_MSC_VER) */ + // we want to use cmov, if cmov is very fast: + // - this cmov version is slower for clang-x64. + // - this cmov version is faster for gcc-arm64 for some fast arm64 cpus. + #define Z7_FAST_CMOV_SUPPORTED +#endif + +#ifdef Z7_FAST_CMOV_SUPPORTED + // we want to use cmov here, if cmov is fast: new arm64 cpus. + // we want the compiler to use conditional move for this branch + #define GET_MAX_VAL(n0, n1, max_val_slow) if (n0 < n1) n0 = n1; +#else + // use this branch, if cpu doesn't support fast conditional move. + // it uses slow array access reading: + #define GET_MAX_VAL(n0, n1, max_val_slow) n0 = max_val_slow; +#endif + +#define HeapSortDown(p, k, size, temp, macro_prefetch) \ +{ \ + for (;;) { \ + UInt32 n0, n1; \ + size_t s = k * 2; \ + if (s >= size) { \ + if (s == size) { \ + n0 = p[s]; \ + p[k] = n0; \ + if (temp < n0) k = s; \ + } \ + break; \ + } \ + n0 = p[k * 2]; \ + n1 = p[k * 2 + 1]; \ + s += n0 < n1; \ + GET_MAX_VAL(n0, n1, p[s]) \ + if (temp >= n0) break; \ + macro_prefetch(p, k, s, size) \ + p[k] = n0; \ + k = s; \ + } \ + p[k] = temp; \ } -void HeapSort64(UInt64 *p, size_t size) + +/* +stage-1 : O(n) : + we generate intermediate partially sorted binary tree: + p[0] : it's additional item for better alignment of tree structure in memory. + p[1] + p[2] p[3] + p[4] p[5] p[6] p[7] + ... + p[x] >= p[x * 2] + p[x] >= p[x * 2 + 1] + +stage-2 : O(n)*log2(N): + we move largest item p[0] from head of tree to the end of array + and insert last item to sorted binary tree. +*/ + +// (p) must be aligned for cache line size (64-bytes) for best performance + +void Z7_FASTCALL HeapSort(UInt32 *p, size_t size) { - if (size <= 1) + if (size < 2) return; - p--; + if (size == 2) { - size_t i = size / 2; - do - { - UInt64 temp = p[i]; - size_t k = i; - HeapSortDown(p, k, size, temp) - } - while (--i != 0); - } - /* - do - { - size_t k = 1; - UInt64 temp = p[size]; - p[size--] = p[1]; - HeapSortDown(p, k, size, temp) - } - while (size > 1); - */ - while (size > 3) - { - UInt64 temp = p[size]; - size_t k = (p[3] > p[2]) ? 3 : 2; - p[size--] = p[1]; - p[1] = p[k]; - HeapSortDown(p, k, size, temp) + const UInt32 a0 = p[0]; + const UInt32 a1 = p[1]; + const unsigned k = a1 < a0; + p[k] = a0; + p[k ^ 1] = a1; + return; } { - UInt64 temp = p[size]; - p[size] = p[1]; - if (size > 2 && p[2] < temp) + // stage-1 : O(n) + // we transform array to partially sorted binary tree. + size_t i = --size / 2; + // (size) now is the index of the last item in tree, + // if (i) + { + do + { + const UInt32 temp = p[i]; + size_t k = i; + HeapSortDown(p, k, size, temp, PREFETCH_NO) + } + while (--i); + } { - p[1] = p[2]; - p[2] = temp; + const UInt32 temp = p[0]; + const UInt32 a1 = p[1]; + if (temp < a1) + { + size_t k = 1; + p[0] = a1; + HeapSortDown(p, k, size, temp, PREFETCH_NO) + } } - else - p[1] = temp; } -} - -/* -#define HeapSortRefDown(p, vals, n, size, temp) \ - { size_t k = n; UInt32 val = vals[temp]; for (;;) { \ - size_t s = (k << 1); \ - if (s > size) break; \ - if (s < size && vals[p[s + 1]] > vals[p[s]]) s++; \ - if (val >= vals[p[s]]) break; \ - p[k] = p[s]; k = s; \ - } p[k] = temp; } -void HeapSortRef(UInt32 *p, UInt32 *vals, size_t size) -{ - if (size <= 1) + if (size < 3) + { + // size == 2 + const UInt32 a0 = p[0]; + p[0] = p[2]; + p[2] = a0; return; - p--; + } + if (size != 3) { - size_t i = size / 2; + // stage-2 : O(size) * log2(size): + // we move largest item p[0] from head to the end of array, + // and insert last item to sorted binary tree. do { - UInt32 temp = p[i]; - HeapSortRefDown(p, vals, i, size, temp); + const UInt32 temp = p[size]; + size_t k = p[2] < p[3] ? 3 : 2; + p[size--] = p[0]; + p[0] = p[1]; + p[1] = p[k]; + HeapSortDown(p, k, size, temp, SORT_PREFETCH) // PREFETCH_NO } - while (--i != 0); + while (size != 3); } - do { - UInt32 temp = p[size]; - p[size--] = p[1]; - HeapSortRefDown(p, vals, 1, size, temp); + const UInt32 a2 = p[2]; + const UInt32 a3 = p[3]; + const size_t k = a2 < a3; + p[2] = p[1]; + p[3] = p[0]; + p[k] = a3; + p[k ^ 1] = a2; } - while (size > 1); } -*/ diff -Nru 7zip-24.09+dfsg/C/Sort.h 7zip-25.01+dfsg/C/Sort.h --- 7zip-24.09+dfsg/C/Sort.h 2023-03-05 18:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/Sort.h 2025-01-06 09:00:00.000000000 +0000 @@ -1,5 +1,5 @@ /* Sort.h -- Sort functions -2023-03-05 : Igor Pavlov : Public domain */ +: Igor Pavlov : Public domain */ #ifndef ZIP7_INC_SORT_H #define ZIP7_INC_SORT_H @@ -8,10 +8,7 @@ EXTERN_C_BEGIN -void HeapSort(UInt32 *p, size_t size); -void HeapSort64(UInt64 *p, size_t size); - -/* void HeapSortRef(UInt32 *p, UInt32 *vals, size_t size); */ +void Z7_FASTCALL HeapSort(UInt32 *p, size_t size); EXTERN_C_END diff -Nru 7zip-24.09+dfsg/C/Threads.c 7zip-25.01+dfsg/C/Threads.c --- 7zip-24.09+dfsg/C/Threads.c 2024-03-28 17:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/Threads.c 2025-07-05 09:00:00.000000000 +0000 @@ -1,5 +1,5 @@ /* Threads.c -- multithreading library -2024-03-28 : Igor Pavlov : Public domain */ +: Igor Pavlov : Public domain */ #include "Precomp.h" @@ -59,6 +59,100 @@ return (res != 0 ? res : res2); } +typedef struct MY_PROCESSOR_NUMBER { + WORD Group; + BYTE Number; + BYTE Reserved; +} MY_PROCESSOR_NUMBER, *MY_PPROCESSOR_NUMBER; + +typedef struct MY_GROUP_AFFINITY { +#if defined(Z7_GCC_VERSION) && (Z7_GCC_VERSION < 100000) + // KAFFINITY is not defined in old mingw + ULONG_PTR +#else + KAFFINITY +#endif + Mask; + WORD Group; + WORD Reserved[3]; +} MY_GROUP_AFFINITY, *MY_PGROUP_AFFINITY; + +typedef BOOL (WINAPI *Func_SetThreadGroupAffinity)( + HANDLE hThread, + CONST MY_GROUP_AFFINITY *GroupAffinity, + MY_PGROUP_AFFINITY PreviousGroupAffinity); + +typedef BOOL (WINAPI *Func_GetThreadGroupAffinity)( + HANDLE hThread, + MY_PGROUP_AFFINITY GroupAffinity); + +typedef BOOL (WINAPI *Func_GetProcessGroupAffinity)( + HANDLE hProcess, + PUSHORT GroupCount, + PUSHORT GroupArray); + +Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION + +#if 0 +#include +#define PRF(x) x +/* +-- + before call of SetThreadGroupAffinity() + GetProcessGroupAffinity return one group. + after call of SetThreadGroupAffinity(): + GetProcessGroupAffinity return more than group, + if SetThreadGroupAffinity() was to another group. +-- + GetProcessAffinityMask MS DOCs: + { + If the calling process contains threads in multiple groups, + the function returns zero for both affinity masks. + } + but tests in win10 with 2 groups (less than 64 cores total): + GetProcessAffinityMask() still returns non-zero affinity masks + even after SetThreadGroupAffinity() calls. +*/ +static void PrintProcess_Info() +{ + { + const + Func_GetProcessGroupAffinity fn_GetProcessGroupAffinity = + (Func_GetProcessGroupAffinity) Z7_CAST_FUNC_C GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), + "GetProcessGroupAffinity"); + if (fn_GetProcessGroupAffinity) + { + unsigned i; + USHORT GroupCounts[64]; + USHORT GroupCount = Z7_ARRAY_SIZE(GroupCounts); + BOOL boolRes = fn_GetProcessGroupAffinity(GetCurrentProcess(), + &GroupCount, GroupCounts); + printf("\n====== GetProcessGroupAffinity : " + "boolRes=%u GroupCounts = %u :", + boolRes, (unsigned)GroupCount); + for (i = 0; i < GroupCount; i++) + printf(" %u", GroupCounts[i]); + printf("\n"); + } + } + { + DWORD_PTR processAffinityMask, systemAffinityMask; + if (GetProcessAffinityMask(GetCurrentProcess(), &processAffinityMask, &systemAffinityMask)) + { + PRF(printf("\n====== GetProcessAffinityMask : " + ": processAffinityMask=%x, systemAffinityMask=%x\n", + (UInt32)processAffinityMask, (UInt32)systemAffinityMask);) + } + else + printf("\n==GetProcessAffinityMask FAIL"); + } +} +#else +#ifndef USE_THREADS_CreateThread +// #define PRF(x) +#endif +#endif + WRes Thread_Create(CThread *p, THREAD_FUNC_TYPE func, LPVOID param) { /* Windows Me/98/95: threadId parameter may not be NULL in _beginthreadex/CreateThread functions */ @@ -72,7 +166,43 @@ unsigned threadId; *p = (HANDLE)(_beginthreadex(NULL, 0, func, param, 0, &threadId)); - + +#if 0 // 1 : for debug + { + DWORD_PTR prevMask; + DWORD_PTR affinity = 1 << 0; + prevMask = SetThreadAffinityMask(*p, (DWORD_PTR)affinity); + prevMask = prevMask; + } +#endif +#if 0 // 1 : for debug + { + /* win10: new thread will be created in same group that is assigned to parent thread + but affinity mask will contain all allowed threads of that group, + even if affinity mask of parent group is not full + win11: what group it will be created, if we have set + affinity of parent thread with ThreadGroupAffinity? + */ + const + Func_GetThreadGroupAffinity fn = + (Func_GetThreadGroupAffinity) Z7_CAST_FUNC_C GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), + "GetThreadGroupAffinity"); + if (fn) + { + // BOOL wres2; + MY_GROUP_AFFINITY groupAffinity; + memset(&groupAffinity, 0, sizeof(groupAffinity)); + /* wres2 = */ fn(*p, &groupAffinity); + PRF(printf("\n==Thread_Create cur = %6u GetThreadGroupAffinity(): " + "wres2_BOOL = %u, group=%u mask=%x\n", + GetCurrentThreadId(), + wres2, + groupAffinity.Group, + (UInt32)groupAffinity.Mask);) + } + } +#endif + #endif /* maybe we must use errno here, but probably GetLastError() is also OK. */ @@ -110,7 +240,84 @@ */ } { - DWORD prevSuspendCount = ResumeThread(h); + const DWORD prevSuspendCount = ResumeThread(h); + /* ResumeThread() returns: + 0 : was_not_suspended + 1 : was_resumed + -1 : error + */ + if (prevSuspendCount == (DWORD)-1) + wres = GetError(); + } + } + + /* maybe we must use errno here, but probably GetLastError() is also OK. */ + return wres; + + #endif +} + + +WRes Thread_Create_With_Group(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, unsigned group, CAffinityMask affinityMask) +{ +#ifdef USE_THREADS_CreateThread + + UNUSED_VAR(group) + UNUSED_VAR(affinityMask) + return Thread_Create(p, func, param); + +#else + + /* Windows Me/98/95: threadId parameter may not be NULL in _beginthreadex/CreateThread functions */ + HANDLE h; + WRes wres; + unsigned threadId; + h = (HANDLE)(_beginthreadex(NULL, 0, func, param, CREATE_SUSPENDED, &threadId)); + *p = h; + wres = HandleToWRes(h); + if (h) + { + // PrintProcess_Info(); + { + const + Func_SetThreadGroupAffinity fn = + (Func_SetThreadGroupAffinity) Z7_CAST_FUNC_C GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), + "SetThreadGroupAffinity"); + if (fn) + { + // WRes wres2; + MY_GROUP_AFFINITY groupAffinity, prev_groupAffinity; + memset(&groupAffinity, 0, sizeof(groupAffinity)); + // groupAffinity.Mask must use only bits that supported by current group + // (groupAffinity.Mask = 0) means all allowed bits + groupAffinity.Mask = affinityMask; + groupAffinity.Group = (WORD)group; + // wres2 = + fn(h, &groupAffinity, &prev_groupAffinity); + /* + if (groupAffinity.Group == prev_groupAffinity.Group) + wres2 = wres2; + else + wres2 = wres2; + if (wres2 == 0) + { + wres2 = GetError(); + PRF(printf("\n==SetThreadGroupAffinity error: %u\n", wres2);) + } + else + { + PRF(printf("\n==Thread_Create_With_Group::SetThreadGroupAffinity()" + " threadId = %6u" + " group=%u mask=%x\n", + threadId, + prev_groupAffinity.Group, + (UInt32)prev_groupAffinity.Mask);) + } + */ + } + } + { + const DWORD prevSuspendCount = ResumeThread(h); /* ResumeThread() returns: 0 : was_not_suspended 1 : was_resumed @@ -297,6 +504,13 @@ return Thread_Create_With_CpuSet(p, func, param, NULL); } +/* +WRes Thread_Create_With_Group(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, unsigned group, CAffinityMask affinity) +{ + UNUSED_VAR(group) + return Thread_Create_With_Affinity(p, func, param, affinity); +} +*/ WRes Thread_Create_With_Affinity(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, CAffinityMask affinity) { @@ -577,5 +791,22 @@ return AutoResetEvent_CreateNotSignaled(p); } +void ThreadNextGroup_Init(CThreadNextGroup *p, UInt32 numGroups, UInt32 startGroup) +{ + // printf("\n====== ThreadNextGroup_Init numGroups = %x: startGroup=%x\n", numGroups, startGroup); + if (numGroups == 0) + numGroups = 1; + p->NumGroups = numGroups; + p->NextGroup = startGroup % numGroups; +} + + +UInt32 ThreadNextGroup_GetNext(CThreadNextGroup *p) +{ + const UInt32 next = p->NextGroup; + p->NextGroup = (next + 1) % p->NumGroups; + return next; +} + #undef PRF #undef Print diff -Nru 7zip-24.09+dfsg/C/Threads.h 7zip-25.01+dfsg/C/Threads.h --- 7zip-24.09+dfsg/C/Threads.h 2024-03-28 17:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/Threads.h 2025-06-30 14:00:00.000000000 +0000 @@ -1,5 +1,5 @@ /* Threads.h -- multithreading library -2024-03-28 : Igor Pavlov : Public domain */ +: Igor Pavlov : Public domain */ #ifndef ZIP7_INC_THREADS_H #define ZIP7_INC_THREADS_H @@ -140,12 +140,22 @@ WRes Thread_Wait_Close(CThread *p); #ifdef _WIN32 +WRes Thread_Create_With_Group(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, unsigned group, CAffinityMask affinityMask); #define Thread_Create_With_CpuSet(p, func, param, cs) \ Thread_Create_With_Affinity(p, func, param, *cs) #else WRes Thread_Create_With_CpuSet(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, const CCpuSet *cpuSet); #endif +typedef struct +{ + unsigned NumGroups; + unsigned NextGroup; +} CThreadNextGroup; + +void ThreadNextGroup_Init(CThreadNextGroup *p, unsigned numGroups, unsigned startGroup); +unsigned ThreadNextGroup_GetNext(CThreadNextGroup *p); + #ifdef _WIN32 diff -Nru 7zip-24.09+dfsg/C/Util/Lzma/LzmaUtil.dsp 7zip-25.01+dfsg/C/Util/Lzma/LzmaUtil.dsp --- 7zip-24.09+dfsg/C/Util/Lzma/LzmaUtil.dsp 2023-04-04 20:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/Util/Lzma/LzmaUtil.dsp 2025-02-05 06:00:00.000000000 +0000 @@ -122,6 +122,10 @@ # End Source File # Begin Source File +SOURCE=..\..\CpuArch.c +# End Source File +# Begin Source File + SOURCE=..\..\CpuArch.h # End Source File # Begin Source File diff -Nru 7zip-24.09+dfsg/C/Util/LzmaLib/LzmaLib.dsp 7zip-25.01+dfsg/C/Util/LzmaLib/LzmaLib.dsp --- 7zip-24.09+dfsg/C/Util/LzmaLib/LzmaLib.dsp 2023-04-04 13:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/Util/LzmaLib/LzmaLib.dsp 2025-02-05 06:00:00.000000000 +0000 @@ -43,7 +43,7 @@ # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LZMALIB_EXPORTS" /YX /FD /c -# ADD CPP /nologo /Gr /MT /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LZMALIB_EXPORTS" /FD /c +# ADD CPP /nologo /Gr /MT /W4 /WX /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LZMALIB_EXPORTS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -71,7 +71,7 @@ # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LZMALIB_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LZMALIB_EXPORTS" /D "COMPRESS_MF_MT" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /WX /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LZMALIB_EXPORTS" /D "COMPRESS_MF_MT" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 @@ -128,6 +128,10 @@ # End Source File # Begin Source File +SOURCE=..\..\CpuArch.c +# End Source File +# Begin Source File + SOURCE=..\..\CpuArch.h # End Source File # Begin Source File diff -Nru 7zip-24.09+dfsg/C/Xz.h 7zip-25.01+dfsg/C/Xz.h --- 7zip-24.09+dfsg/C/Xz.h 2024-01-26 14:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/Xz.h 2025-05-03 11:00:00.000000000 +0000 @@ -1,5 +1,5 @@ /* Xz.h - Xz interface -2024-01-26 : Igor Pavlov : Public domain */ +Igor Pavlov : Public domain */ #ifndef ZIP7_INC_XZ_H #define ZIP7_INC_XZ_H @@ -121,6 +121,7 @@ UInt64 startOffset; } CXzStream; +#define Xz_CONSTRUCT(p) { (p)->numBlocks = 0; (p)->blocks = NULL; (p)->flags = 0; } void Xz_Construct(CXzStream *p); void Xz_Free(CXzStream *p, ISzAllocPtr alloc); @@ -136,8 +137,13 @@ CXzStream *streams; } CXzs; +#define Xzs_CONSTRUCT(p) { (p)->num = 0; (p)->numAllocated = 0; (p)->streams = NULL; } void Xzs_Construct(CXzs *p); void Xzs_Free(CXzs *p, ISzAllocPtr alloc); +/* +Xzs_ReadBackward() must be called for empty CXzs object. +Xzs_ReadBackward() can return non empty object with (p->num != 0) even in case of error. +*/ SRes Xzs_ReadBackward(CXzs *p, ILookInStreamPtr inStream, Int64 *startOffset, ICompressProgressPtr progress, ISzAllocPtr alloc); UInt64 Xzs_GetNumBlocks(const CXzs *p); @@ -268,8 +274,8 @@ size_t outBufSize; size_t outDataWritten; // the size of data in (outBuf) that were fully unpacked - Byte shaDigest[SHA256_DIGEST_SIZE]; - Byte buf[XZ_BLOCK_HEADER_SIZE_MAX]; + UInt32 shaDigest32[SHA256_DIGEST_SIZE / 4]; + Byte buf[XZ_BLOCK_HEADER_SIZE_MAX]; // it must be aligned for 4-bytes } CXzUnpacker; /* alloc : aligned for cache line allocation is better */ diff -Nru 7zip-24.09+dfsg/C/XzCrc64Opt.c 7zip-25.01+dfsg/C/XzCrc64Opt.c --- 7zip-24.09+dfsg/C/XzCrc64Opt.c 2023-12-08 08:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/XzCrc64Opt.c 2025-01-03 20:00:00.000000000 +0000 @@ -1,5 +1,5 @@ /* XzCrc64Opt.c -- CRC64 calculation (optimized functions) -2023-12-08 : Igor Pavlov : Public domain */ +: Igor Pavlov : Public domain */ #include "Precomp.h" @@ -235,7 +235,7 @@ v = Q32BE(1, w1) ^ Q32BE(0, w0); v ^= Q32BE(3, d1) ^ Q32BE(2, d0); #endif -#elif +#else #error Stop_Compiling_Bad_CRC64_NUM_TABLES #endif p += Z7_CRC64_NUM_TABLES_USE; diff -Nru 7zip-24.09+dfsg/C/XzDec.c 7zip-25.01+dfsg/C/XzDec.c --- 7zip-24.09+dfsg/C/XzDec.c 2024-03-01 06:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/XzDec.c 2025-04-30 13:00:00.000000000 +0000 @@ -1,5 +1,5 @@ /* XzDec.c -- Xz Decode -2024-03-01 : Igor Pavlov : Public domain */ +: Igor Pavlov : Public domain */ #include "Precomp.h" @@ -59,7 +59,7 @@ for (i = 0; i < limit;) { - Byte b = p[i]; + const unsigned b = p[i]; *value |= (UInt64)(b & 0x7F) << (7 * i++); if ((b & 0x80) == 0) return (b == 0 && i != 1) ? 0 : i; @@ -796,11 +796,10 @@ static BoolInt Xz_CheckFooter(CXzStreamFlags flags, UInt64 indexSize, const Byte *buf) { - return indexSize == (((UInt64)GetUi32(buf + 4) + 1) << 2) - && GetUi32(buf) == CrcCalc(buf + 4, 6) - && flags == GetBe16(buf + 8) - && buf[10] == XZ_FOOTER_SIG_0 - && buf[11] == XZ_FOOTER_SIG_1; + return indexSize == (((UInt64)GetUi32a(buf + 4) + 1) << 2) + && GetUi32a(buf) == CrcCalc(buf + 4, 6) + && flags == GetBe16a(buf + 8) + && GetUi16a(buf + 10) == (XZ_FOOTER_SIG_0 | (XZ_FOOTER_SIG_1 << 8)); } #define READ_VARINT_AND_CHECK(buf, pos, size, res) \ @@ -1166,7 +1165,7 @@ p->indexPreSize = 1 + Xz_WriteVarInt(p->buf + 1, p->numBlocks); p->indexPos = p->indexPreSize; p->indexSize += p->indexPreSize; - Sha256_Final(&p->sha, p->shaDigest); + Sha256_Final(&p->sha, (Byte *)(void *)p->shaDigest32); Sha256_Init(&p->sha); p->crc = CrcUpdate(CRC_INIT_VAL, p->buf, p->indexPreSize); p->state = XZ_STATE_STREAM_INDEX; @@ -1241,10 +1240,10 @@ break; } { - Byte digest[XZ_CHECK_SIZE_MAX]; + UInt32 digest32[XZ_CHECK_SIZE_MAX / 4]; p->state = XZ_STATE_BLOCK_HEADER; p->pos = 0; - if (XzCheck_Final(&p->check, digest) && memcmp(digest, p->buf, checkSize) != 0) + if (XzCheck_Final(&p->check, (void *)digest32) && memcmp(digest32, p->buf, checkSize) != 0) return SZ_ERROR_CRC; if (p->decodeOnlyOneBlock) { @@ -1289,12 +1288,12 @@ } else { - Byte digest[SHA256_DIGEST_SIZE]; + UInt32 digest32[SHA256_DIGEST_SIZE / 4]; p->state = XZ_STATE_STREAM_INDEX_CRC; p->indexSize += 4; p->pos = 0; - Sha256_Final(&p->sha, digest); - if (memcmp(digest, p->shaDigest, SHA256_DIGEST_SIZE) != 0) + Sha256_Final(&p->sha, (void *)digest32); + if (memcmp(digest32, p->shaDigest32, SHA256_DIGEST_SIZE) != 0) return SZ_ERROR_CRC; } } @@ -1313,7 +1312,7 @@ const Byte *ptr = p->buf; p->state = XZ_STATE_STREAM_FOOTER; p->pos = 0; - if (CRC_GET_DIGEST(p->crc) != GetUi32(ptr)) + if (CRC_GET_DIGEST(p->crc) != GetUi32a(ptr)) return SZ_ERROR_CRC; } break; @@ -1343,7 +1342,7 @@ { if (*src != 0) { - if (((UInt32)p->padSize & 3) != 0) + if ((unsigned)p->padSize & 3) return SZ_ERROR_NO_ARCHIVE; p->pos = 0; p->state = XZ_STATE_STREAM_HEADER; diff -Nru 7zip-24.09+dfsg/C/XzEnc.c 7zip-25.01+dfsg/C/XzEnc.c --- 7zip-24.09+dfsg/C/XzEnc.c 2024-03-01 06:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/XzEnc.c 2025-07-04 06:00:00.000000000 +0000 @@ -1,5 +1,5 @@ /* XzEnc.c -- Xz Encode -2024-03-01 : Igor Pavlov : Public domain */ +: Igor Pavlov : Public domain */ #include "Precomp.h" @@ -411,6 +411,7 @@ } } +Z7_FORCE_INLINE static void SeqInFilter_Construct(CSeqInFilter *p) { p->buf = NULL; @@ -418,6 +419,7 @@ p->vt.Read = SeqInFilter_Read; } +Z7_FORCE_INLINE static void SeqInFilter_Free(CSeqInFilter *p, ISzAllocPtr alloc) { if (p->StateCoder.p) @@ -507,6 +509,7 @@ void XzProps_Init(CXzProps *p) { p->checkId = XZ_CHECK_CRC32; + p->numThreadGroups = 0; p->blockSize = XZ_PROPS_BLOCK_SIZE_AUTO; p->numBlockThreads_Reduced = -1; p->numBlockThreads_Max = -1; @@ -689,6 +692,7 @@ } CLzma2WithFilters; +Z7_FORCE_INLINE static void Lzma2WithFilters_Construct(CLzma2WithFilters *p) { p->lzma2 = NULL; @@ -712,6 +716,7 @@ } +Z7_FORCE_INLINE static void Lzma2WithFilters_Free(CLzma2WithFilters *p, ISzAllocPtr alloc) { #ifdef USE_SUBBLOCK @@ -1236,6 +1241,7 @@ } p->mtCoder.numThreadsMax = (unsigned)props->numBlockThreads_Max; + p->mtCoder.numThreadGroups = props->numThreadGroups; p->mtCoder.expectedDataSize = p->expectedDataSize; RINOK(MtCoder_Code(&p->mtCoder)) diff -Nru 7zip-24.09+dfsg/C/XzEnc.h 7zip-25.01+dfsg/C/XzEnc.h --- 7zip-24.09+dfsg/C/XzEnc.h 2023-04-13 08:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/XzEnc.h 2025-07-02 12:00:00.000000000 +0000 @@ -1,5 +1,5 @@ /* XzEnc.h -- Xz Encode -2023-04-13 : Igor Pavlov : Public domain */ +: Igor Pavlov : Public domain */ #ifndef ZIP7_INC_XZ_ENC_H #define ZIP7_INC_XZ_ENC_H @@ -31,6 +31,7 @@ CLzma2EncProps lzma2Props; CXzFilterProps filterProps; unsigned checkId; + unsigned numThreadGroups; // 0 : no groups UInt64 blockSize; int numBlockThreads_Reduced; int numBlockThreads_Max; diff -Nru 7zip-24.09+dfsg/C/XzIn.c 7zip-25.01+dfsg/C/XzIn.c --- 7zip-24.09+dfsg/C/XzIn.c 2023-09-07 13:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/C/XzIn.c 2025-05-04 10:00:00.000000000 +0000 @@ -1,38 +1,39 @@ /* XzIn.c - Xz input -2023-09-07 : Igor Pavlov : Public domain */ +: Igor Pavlov : Public domain */ #include "Precomp.h" #include #include "7zCrc.h" -#include "CpuArch.h" #include "Xz.h" +#include "CpuArch.h" -/* -#define XZ_FOOTER_SIG_CHECK(p) (memcmp((p), XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE) == 0) -*/ -#define XZ_FOOTER_SIG_CHECK(p) ((p)[0] == XZ_FOOTER_SIG_0 && (p)[1] == XZ_FOOTER_SIG_1) - +#define XZ_FOOTER_12B_ALIGNED16_SIG_CHECK(p) \ + (GetUi16a((const Byte *)(const void *)(p) + 10) == \ + (XZ_FOOTER_SIG_0 | (XZ_FOOTER_SIG_1 << 8))) SRes Xz_ReadHeader(CXzStreamFlags *p, ISeqInStreamPtr inStream) { - Byte sig[XZ_STREAM_HEADER_SIZE]; + UInt32 data32[XZ_STREAM_HEADER_SIZE / 4]; size_t processedSize = XZ_STREAM_HEADER_SIZE; - RINOK(SeqInStream_ReadMax(inStream, sig, &processedSize)) + RINOK(SeqInStream_ReadMax(inStream, data32, &processedSize)) if (processedSize != XZ_STREAM_HEADER_SIZE - || memcmp(sig, XZ_SIG, XZ_SIG_SIZE) != 0) + || memcmp(data32, XZ_SIG, XZ_SIG_SIZE) != 0) return SZ_ERROR_NO_ARCHIVE; - return Xz_ParseHeader(p, sig); + return Xz_ParseHeader(p, (const Byte *)(const void *)data32); } -#define READ_VARINT_AND_CHECK(buf, pos, size, res) \ - { const unsigned s = Xz_ReadVarInt(buf + pos, size - pos, res); \ +#define READ_VARINT_AND_CHECK(buf, size, res) \ +{ const unsigned s = Xz_ReadVarInt(buf, size, res); \ if (s == 0) return SZ_ERROR_ARCHIVE; \ - pos += s; } + size -= s; \ + buf += s; \ +} SRes XzBlock_ReadHeader(CXzBlock *p, ISeqInStreamPtr inStream, BoolInt *isIndex, UInt32 *headerSizeRes) { + MY_ALIGN(4) Byte header[XZ_BLOCK_HEADER_SIZE_MAX]; unsigned headerSize; *headerSizeRes = 0; @@ -57,8 +58,12 @@ return XzBlock_Parse(p, header); } + #define ADD_SIZE_CHECK(size, val) \ - { const UInt64 newSize = size + (val); if (newSize < size) return XZ_SIZE_OVERFLOW; size = newSize; } +{ const UInt64 newSize = size + (val); \ + if (newSize < size) return XZ_SIZE_OVERFLOW; \ + size = newSize; \ +} UInt64 Xz_GetUnpackSize(const CXzStream *p) { @@ -82,76 +87,85 @@ return size; } -/* -SRes XzBlock_ReadFooter(CXzBlock *p, CXzStreamFlags f, ISeqInStreamPtr inStream) -{ - return SeqInStream_Read(inStream, p->check, XzFlags_GetCheckSize(f)); -} -*/ -static SRes Xz_ReadIndex2(CXzStream *p, const Byte *buf, size_t size, ISzAllocPtr alloc) +// input; +// CXzStream (p) is empty object. +// size != 0 +// (size & 3) == 0 +// (buf) is aligned for at least 4 bytes. +// output: +// p->numBlocks is number of allocated items in p->blocks +// p->blocks[*] values must be ignored, if function returns error. +static SRes Xz_ParseIndex(CXzStream *p, const Byte *buf, size_t size, ISzAllocPtr alloc) { - size_t numBlocks, pos = 1; - UInt32 crc; - + size_t numBlocks; if (size < 5 || buf[0] != 0) return SZ_ERROR_ARCHIVE; - size -= 4; - crc = CrcCalc(buf, size); - if (crc != GetUi32(buf + size)) - return SZ_ERROR_ARCHIVE; - + { + const UInt32 crc = CrcCalc(buf, size); + if (crc != GetUi32a(buf + size)) + return SZ_ERROR_ARCHIVE; + } + buf++; + size--; { UInt64 numBlocks64; - READ_VARINT_AND_CHECK(buf, pos, size, &numBlocks64) - numBlocks = (size_t)numBlocks64; - if (numBlocks != numBlocks64 || numBlocks * 2 > size) + READ_VARINT_AND_CHECK(buf, size, &numBlocks64) + // (numBlocks64) is 63-bit value, so we can calculate (numBlocks64 * 2): + if (numBlocks64 * 2 > size) return SZ_ERROR_ARCHIVE; + if (numBlocks64 >= ((size_t)1 << (sizeof(size_t) * 8 - 1)) / sizeof(CXzBlockSizes)) + return SZ_ERROR_MEM; // SZ_ERROR_ARCHIVE + numBlocks = (size_t)numBlocks64; } - - Xz_Free(p, alloc); - if (numBlocks != 0) + // Xz_Free(p, alloc); // it's optional, because (p) is empty already + if (numBlocks) { - size_t i; - p->numBlocks = numBlocks; - p->blocks = (CXzBlockSizes *)ISzAlloc_Alloc(alloc, sizeof(CXzBlockSizes) * numBlocks); - if (!p->blocks) + CXzBlockSizes *blocks = (CXzBlockSizes *)ISzAlloc_Alloc(alloc, sizeof(CXzBlockSizes) * numBlocks); + if (!blocks) return SZ_ERROR_MEM; - for (i = 0; i < numBlocks; i++) + p->blocks = blocks; + p->numBlocks = numBlocks; + // the caller will call Xz_Free() in case of error + do { - CXzBlockSizes *block = &p->blocks[i]; - READ_VARINT_AND_CHECK(buf, pos, size, &block->totalSize) - READ_VARINT_AND_CHECK(buf, pos, size, &block->unpackSize) - if (block->totalSize == 0) + READ_VARINT_AND_CHECK(buf, size, &blocks->totalSize) + READ_VARINT_AND_CHECK(buf, size, &blocks->unpackSize) + if (blocks->totalSize == 0) return SZ_ERROR_ARCHIVE; + blocks++; } + while (--numBlocks); } - while ((pos & 3) != 0) - if (buf[pos++] != 0) + if (size >= 4) + return SZ_ERROR_ARCHIVE; + while (size) + if (buf[--size]) return SZ_ERROR_ARCHIVE; - return (pos == size) ? SZ_OK : SZ_ERROR_ARCHIVE; + return SZ_OK; } + +/* static SRes Xz_ReadIndex(CXzStream *p, ILookInStreamPtr stream, UInt64 indexSize, ISzAllocPtr alloc) { SRes res; size_t size; Byte *buf; - if (indexSize > ((UInt32)1 << 31)) - return SZ_ERROR_UNSUPPORTED; + if (indexSize >= ((size_t)1 << (sizeof(size_t) * 8 - 1))) + return SZ_ERROR_MEM; // SZ_ERROR_ARCHIVE size = (size_t)indexSize; - if (size != indexSize) - return SZ_ERROR_UNSUPPORTED; buf = (Byte *)ISzAlloc_Alloc(alloc, size); if (!buf) return SZ_ERROR_MEM; res = LookInStream_Read2(stream, buf, size, SZ_ERROR_UNSUPPORTED); if (res == SZ_OK) - res = Xz_ReadIndex2(p, buf, size, alloc); + res = Xz_ParseIndex(p, buf, size, alloc); ISzAlloc_Free(alloc, buf); return res; } +*/ static SRes LookInStream_SeekRead_ForArc(ILookInStreamPtr stream, UInt64 offset, void *buf, size_t size) { @@ -160,84 +174,102 @@ /* return LookInStream_Read2(stream, buf, size, SZ_ERROR_NO_ARCHIVE); */ } + +/* +in: + (*startOffset) is position in (stream) where xz_stream must be finished. +out: + if returns SZ_OK, then (*startOffset) is position in stream that shows start of xz_stream. +*/ static SRes Xz_ReadBackward(CXzStream *p, ILookInStreamPtr stream, Int64 *startOffset, ISzAllocPtr alloc) { - UInt64 indexSize; - Byte buf[XZ_STREAM_FOOTER_SIZE]; + #define TEMP_BUF_SIZE (1 << 10) + UInt32 buf32[TEMP_BUF_SIZE / 4]; UInt64 pos = (UInt64)*startOffset; - if ((pos & 3) != 0 || pos < XZ_STREAM_FOOTER_SIZE) + if ((pos & 3) || pos < XZ_STREAM_FOOTER_SIZE) return SZ_ERROR_NO_ARCHIVE; - pos -= XZ_STREAM_FOOTER_SIZE; - RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf, XZ_STREAM_FOOTER_SIZE)) + RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf32, XZ_STREAM_FOOTER_SIZE)) - if (!XZ_FOOTER_SIG_CHECK(buf + 10)) + if (!XZ_FOOTER_12B_ALIGNED16_SIG_CHECK(buf32)) { - UInt32 total = 0; pos += XZ_STREAM_FOOTER_SIZE; - for (;;) { - size_t i; - #define TEMP_BUF_SIZE (1 << 10) - Byte temp[TEMP_BUF_SIZE]; - - i = (pos > TEMP_BUF_SIZE) ? TEMP_BUF_SIZE : (size_t)pos; + // pos != 0 + // (pos & 3) == 0 + size_t i = pos >= TEMP_BUF_SIZE ? TEMP_BUF_SIZE : (size_t)pos; pos -= i; - RINOK(LookInStream_SeekRead_ForArc(stream, pos, temp, i)) - total += (UInt32)i; - for (; i != 0; i--) - if (temp[i - 1] != 0) + RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf32, i)) + i /= 4; + do + if (buf32[i - 1] != 0) break; - if (i != 0) - { - if ((i & 3) != 0) - return SZ_ERROR_NO_ARCHIVE; - pos += i; - break; - } - if (pos < XZ_STREAM_FOOTER_SIZE || total > (1 << 16)) + while (--i); + + pos += i * 4; + #define XZ_STREAM_BACKWARD_READING_PAD_MAX (1 << 16) + // here we don't support rare case with big padding for xz stream. + // so we have padding limit for backward reading. + if ((UInt64)*startOffset - pos > XZ_STREAM_BACKWARD_READING_PAD_MAX) return SZ_ERROR_NO_ARCHIVE; + if (i) + break; } - + // we try to open xz stream after skipping zero padding. + // ((UInt64)*startOffset == pos) is possible here! if (pos < XZ_STREAM_FOOTER_SIZE) return SZ_ERROR_NO_ARCHIVE; pos -= XZ_STREAM_FOOTER_SIZE; - RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf, XZ_STREAM_FOOTER_SIZE)) - if (!XZ_FOOTER_SIG_CHECK(buf + 10)) + RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf32, XZ_STREAM_FOOTER_SIZE)) + if (!XZ_FOOTER_12B_ALIGNED16_SIG_CHECK(buf32)) return SZ_ERROR_NO_ARCHIVE; } - p->flags = (CXzStreamFlags)GetBe16(buf + 8); - + p->flags = (CXzStreamFlags)GetBe16a(buf32 + 2); if (!XzFlags_IsSupported(p->flags)) return SZ_ERROR_UNSUPPORTED; - { /* to eliminate GCC 6.3 warning: dereferencing type-punned pointer will break strict-aliasing rules */ - const Byte *buf_ptr = buf; - if (GetUi32(buf_ptr) != CrcCalc(buf + 4, 6)) + const UInt32 *buf_ptr = buf32; + if (GetUi32a(buf_ptr) != CrcCalc(buf32 + 1, 6)) return SZ_ERROR_ARCHIVE; } - - indexSize = ((UInt64)GetUi32(buf + 4) + 1) << 2; - - if (pos < indexSize) - return SZ_ERROR_ARCHIVE; - - pos -= indexSize; - RINOK(LookInStream_SeekTo(stream, pos)) - RINOK(Xz_ReadIndex(p, stream, indexSize, alloc)) - { - UInt64 totalSize = Xz_GetPackSize(p); - if (totalSize == XZ_SIZE_OVERFLOW - || totalSize >= ((UInt64)1 << 63) - || pos < totalSize + XZ_STREAM_HEADER_SIZE) + const UInt64 indexSize = ((UInt64)GetUi32a(buf32 + 1) + 1) << 2; + if (pos < indexSize) return SZ_ERROR_ARCHIVE; - pos -= (totalSize + XZ_STREAM_HEADER_SIZE); + pos -= indexSize; + // v25.00: relaxed indexSize check. We allow big index table. + // if (indexSize > ((UInt32)1 << 31)) + if (indexSize >= ((size_t)1 << (sizeof(size_t) * 8 - 1))) + return SZ_ERROR_MEM; // SZ_ERROR_ARCHIVE + RINOK(LookInStream_SeekTo(stream, pos)) + // RINOK(Xz_ReadIndex(p, stream, indexSize, alloc)) + { + SRes res; + const size_t size = (size_t)indexSize; + // if (size != indexSize) return SZ_ERROR_UNSUPPORTED; + Byte *buf = (Byte *)ISzAlloc_Alloc(alloc, size); + if (!buf) + return SZ_ERROR_MEM; + res = LookInStream_Read2(stream, buf, size, SZ_ERROR_UNSUPPORTED); + if (res == SZ_OK) + res = Xz_ParseIndex(p, buf, size, alloc); + ISzAlloc_Free(alloc, buf); + RINOK(res) + } + } + { + UInt64 total = Xz_GetPackSize(p); + if (total == XZ_SIZE_OVERFLOW || total >= ((UInt64)1 << 63)) + return SZ_ERROR_ARCHIVE; + total += XZ_STREAM_HEADER_SIZE; + if (pos < total) + return SZ_ERROR_ARCHIVE; + pos -= total; RINOK(LookInStream_SeekTo(stream, pos)) *startOffset = (Int64)pos; } @@ -246,7 +278,6 @@ CSecToRead secToRead; SecToRead_CreateVTable(&secToRead); secToRead.realStream = stream; - RINOK(Xz_ReadHeader(&headerFlags, &secToRead.vt)) return (p->flags == headerFlags) ? SZ_OK : SZ_ERROR_ARCHIVE; } @@ -257,8 +288,7 @@ void Xzs_Construct(CXzs *p) { - p->num = p->numAllocated = 0; - p->streams = 0; + Xzs_CONSTRUCT(p) } void Xzs_Free(CXzs *p, ISzAllocPtr alloc) @@ -268,7 +298,7 @@ Xz_Free(&p->streams[i], alloc); ISzAlloc_Free(alloc, p->streams); p->num = p->numAllocated = 0; - p->streams = 0; + p->streams = NULL; } UInt64 Xzs_GetNumBlocks(const CXzs *p) @@ -307,34 +337,49 @@ SRes Xzs_ReadBackward(CXzs *p, ILookInStreamPtr stream, Int64 *startOffset, ICompressProgressPtr progress, ISzAllocPtr alloc) { Int64 endOffset = 0; + // it's supposed that CXzs object is empty here. + // if CXzs object is not empty, it will add new streams to that non-empty object. + // Xzs_Free(p, alloc); // it's optional call to empty CXzs object. RINOK(ILookInStream_Seek(stream, &endOffset, SZ_SEEK_END)) *startOffset = endOffset; for (;;) { CXzStream st; SRes res; - Xz_Construct(&st); + Xz_CONSTRUCT(&st) res = Xz_ReadBackward(&st, stream, startOffset, alloc); + // if (res == SZ_OK), then (*startOffset) is start offset of new stream if + // if (res != SZ_OK), then (*startOffset) is unchend or it's expected start offset of stream with error st.startOffset = (UInt64)*startOffset; - RINOK(res) + // we must store (st) object to array, or we must free (st) local object. + if (res != SZ_OK) + { + Xz_Free(&st, alloc); + return res; + } if (p->num == p->numAllocated) { const size_t newNum = p->num + p->num / 4 + 1; void *data = ISzAlloc_Alloc(alloc, newNum * sizeof(CXzStream)); if (!data) + { + Xz_Free(&st, alloc); return SZ_ERROR_MEM; + } p->numAllocated = newNum; if (p->num != 0) memcpy(data, p->streams, p->num * sizeof(CXzStream)); ISzAlloc_Free(alloc, p->streams); p->streams = (CXzStream *)data; } + // we use direct copying of raw data from local variable (st) to object in array. + // so we don't need to call Xz_Free(&st, alloc) after copying and after p->num++ p->streams[p->num++] = st; if (*startOffset == 0) - break; - RINOK(LookInStream_SeekTo(stream, (UInt64)*startOffset)) + return SZ_OK; + // seek operation is optional: + // RINOK(LookInStream_SeekTo(stream, (UInt64)*startOffset)) if (progress && ICompressProgress_Progress(progress, (UInt64)(endOffset - *startOffset), (UInt64)(Int64)-1) != SZ_OK) return SZ_ERROR_PROGRESS; } - return SZ_OK; } diff -Nru 7zip-24.09+dfsg/CPP/7zip/7zip_gcc.mak 7zip-25.01+dfsg/CPP/7zip/7zip_gcc.mak --- 7zip-24.09+dfsg/CPP/7zip/7zip_gcc.mak 2024-11-25 17:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/7zip_gcc.mak 2025-07-02 05:00:00.000000000 +0000 @@ -1245,8 +1245,6 @@ $(CC) $(CFLAGS) $< $O/Sha512Opt.o: ../../../../C/Sha512Opt.c $(CC) $(CFLAGS) $< -$O/Sort.o: ../../../../C/Sort.c - $(CC) $(CFLAGS) $< $O/SwapBytes.o: ../../../../C/SwapBytes.c $(CC) $(CFLAGS) $< $O/Xxh64.o: ../../../../C/Xxh64.c @@ -1285,6 +1283,8 @@ $(MY_ASM) $(AFLAGS) $< $O/Sha256Opt.o: ../../../../Asm/x86/Sha256Opt.asm $(MY_ASM) $(AFLAGS) $< +$O/Sort.o: ../../../../Asm/x86/Sort.asm + $(MY_ASM) $(AFLAGS) $< ifndef USE_JWASM USE_X86_ASM_AES=1 @@ -1299,6 +1299,8 @@ $(CC) $(CFLAGS) $< $O/Sha256Opt.o: ../../../../C/Sha256Opt.c $(CC) $(CFLAGS) $< +$O/Sort.o: ../../../../C/Sort.c + $(CC) $(CFLAGS) $< endif diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/7z/7zCompressionMode.h 7zip-25.01+dfsg/CPP/7zip/Archive/7z/7zCompressionMode.h --- 7zip-24.09+dfsg/CPP/7zip/Archive/7z/7zCompressionMode.h 2023-11-24 18:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/7z/7zCompressionMode.h 2024-12-14 09:00:00.000000000 +0000 @@ -59,6 +59,7 @@ bool NumThreads_WasForced; bool MultiThreadMixer; UInt32 NumThreads; + UInt32 NumThreadGroups; #endif UString Password; // _Wipe @@ -74,6 +75,7 @@ , NumThreads_WasForced(false) , MultiThreadMixer(true) , NumThreads(1) + , NumThreadGroups(0) #endif , MemoryUsageLimit((UInt64)1 << 30) {} diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/7z/7zHandlerOut.cpp 7zip-25.01+dfsg/CPP/7zip/Archive/7z/7zHandlerOut.cpp --- 7zip-24.09+dfsg/CPP/7zip/Archive/7z/7zHandlerOut.cpp 2024-05-12 12:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/7z/7zHandlerOut.cpp 2025-07-03 07:00:00.000000000 +0000 @@ -111,8 +111,8 @@ } } - const UInt64 kSolidBytes_Min = (1 << 24); - const UInt64 kSolidBytes_Max = ((UInt64)1 << 32); + const UInt64 kSolidBytes_Min = 1 << 24; + const UInt64 kSolidBytes_Max = (UInt64)1 << 32; // for non-LZMA2 methods bool needSolid = false; @@ -122,22 +122,24 @@ SetGlobalLevelTo(oneMethodInfo); - #ifndef Z7_ST +#ifndef Z7_ST const bool numThreads_WasSpecifiedInMethod = (oneMethodInfo.Get_NumThreads() >= 0); if (!numThreads_WasSpecifiedInMethod) { // here we set the (NCoderPropID::kNumThreads) property in each method, only if there is no such property already CMultiMethodProps::SetMethodThreadsTo_IfNotFinded(oneMethodInfo, methodMode.NumThreads); } - #endif + if (methodMode.NumThreadGroups > 1) + CMultiMethodProps::Set_Method_NumThreadGroups_IfNotFinded(oneMethodInfo, methodMode.NumThreadGroups); +#endif CMethodFull &methodFull = methodMode.Methods.AddNew(); RINOK(PropsMethod_To_FullMethod(methodFull, oneMethodInfo)) - #ifndef Z7_ST +#ifndef Z7_ST methodFull.Set_NumThreads = true; methodFull.NumThreads = methodMode.NumThreads; - #endif +#endif if (methodFull.Id != k_Copy) needSolid = true; @@ -217,19 +219,18 @@ // here we get real chunkSize cs = oneMethodInfo.Get_Xz_BlockSize(); if (dicSize > cs) - dicSize = cs; + dicSize = cs; - const UInt64 kSolidBytes_Lzma2_Max = ((UInt64)1 << 34); + const UInt64 kSolidBytes_Lzma2_Max = (UInt64)1 << 34; if (numSolidBytes > kSolidBytes_Lzma2_Max) - numSolidBytes = kSolidBytes_Lzma2_Max; + numSolidBytes = kSolidBytes_Lzma2_Max; methodFull.Set_NumThreads = false; // we don't use ICompressSetCoderMt::SetNumberOfThreads() for LZMA2 encoder #ifndef Z7_ST if (!numThreads_WasSpecifiedInMethod && !methodMode.NumThreads_WasForced - && methodMode.MemoryUsageLimit_WasSet - ) + && methodMode.MemoryUsageLimit_WasSet) { const UInt32 lzmaThreads = oneMethodInfo.Get_Lzma_NumThreads(); const UInt32 numBlockThreads_Original = methodMode.NumThreads / lzmaThreads; @@ -273,14 +274,14 @@ { numSolidBytes = (UInt64)dicSize << 7; if (numSolidBytes > kSolidBytes_Max) - numSolidBytes = kSolidBytes_Max; + numSolidBytes = kSolidBytes_Max; } if (_numSolidBytesDefined) continue; if (numSolidBytes < kSolidBytes_Min) - numSolidBytes = kSolidBytes_Min; + numSolidBytes = kSolidBytes_Min; _numSolidBytes = numSolidBytes; _numSolidBytesDefined = true; } @@ -704,6 +705,9 @@ methodMode.NumThreads = numThreads; methodMode.NumThreads_WasForced = _numThreads_WasForced; methodMode.MultiThreadMixer = _useMultiThreadMixer; +#ifdef _WIN32 + methodMode.NumThreadGroups = _numThreadGroups; // _change it +#endif // headerMethod.NumThreads = 1; headerMethod.MultiThreadMixer = _useMultiThreadMixer; } diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/ArHandler.cpp 7zip-25.01+dfsg/CPP/7zip/Archive/ArHandler.cpp --- 7zip-24.09+dfsg/CPP/7zip/Archive/ArHandler.cpp 2024-02-25 19:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/ArHandler.cpp 2025-06-16 10:00:00.000000000 +0000 @@ -325,7 +325,7 @@ { unsigned i; for (i = 0; i < _items.Size(); i++) - if (_items[i].Name == "//") + if (_items[i].Name.IsEqualTo("//")) break; if (i == _items.Size()) return S_OK; @@ -378,7 +378,7 @@ if (item.Name[0] == '/') continue; CItem &prev = _items[i - 1]; - if (item.Name == prev.Name) + if (item.Name.IsEqualTo(prev.Name)) { if (prev.SameNameIndex < 0) prev.SameNameIndex = 0; @@ -448,9 +448,9 @@ HRESULT CHandler::ParseLibSymbols(IInStream *stream, unsigned fileIndex) { CItem &item = _items[fileIndex]; - if (item.Name != "/" && - item.Name != "__.SYMDEF" && - item.Name != "__.SYMDEF SORTED") + if (!item.Name.IsEqualTo("/") && + !item.Name.IsEqualTo("__.SYMDEF") && + !item.Name.IsEqualTo("__.SYMDEF SORTED")) return S_OK; if (item.Size > ((UInt32)1 << 30) || item.Size < 4) @@ -462,7 +462,7 @@ size_t pos = 0; - if (item.Name != "/") + if (!item.Name.IsEqualTo("/")) { // "__.SYMDEF" parsing (BSD) unsigned be; @@ -603,7 +603,7 @@ if (_longNames_FileIndex >= 0) _items.Delete((unsigned)_longNames_FileIndex); - if (!_items.IsEmpty() && _items[0].Name == "debian-binary") + if (!_items.IsEmpty() && _items[0].Name.IsEqualTo("debian-binary")) { _type = kType_Deb; _items.DeleteFrontal(1); diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Bz2Handler.cpp 7zip-25.01+dfsg/CPP/7zip/Archive/Bz2Handler.cpp --- 7zip-24.09+dfsg/CPP/7zip/Archive/Bz2Handler.cpp 2024-02-26 09:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/Bz2Handler.cpp 2025-07-02 12:00:00.000000000 +0000 @@ -427,9 +427,13 @@ } CMethodProps props2 = _props; - #ifndef Z7_ST +#ifndef Z7_ST props2.AddProp_NumThreads(_props._numThreads); - #endif +#ifdef _WIN32 + if (_props._numThreadGroups > 1) + props2.AddProp32(NCoderPropID::kNumThreadGroups, _props._numThreadGroups); +#endif +#endif return UpdateArchive(size, outStream, props2, updateCallback); } diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/ComHandler.cpp 7zip-25.01+dfsg/CPP/7zip/Archive/ComHandler.cpp --- 7zip-24.09+dfsg/CPP/7zip/Archive/ComHandler.cpp 2024-02-15 07:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/ComHandler.cpp 2025-04-26 11:18:00.000000000 +0000 @@ -68,7 +68,7 @@ static const Byte kRootStorage = 5; } -static const UInt32 kNameSizeMax = 64; +static const unsigned kNameSizeMax = 64; struct CItem { @@ -98,30 +98,30 @@ class CDatabase { - UInt32 NumSectorsInMiniStream; CObjArray MiniSids; HRESULT AddNode(int parent, UInt32 did); -public: +public: CObjArray Fat; - UInt32 FatSize; - CObjArray Mat; - UInt32 MatSize; - CObjectVector Items; CRecordVector Refs; +private: + UInt32 NumSectorsInMiniStream; +public: + UInt32 MatSize; + UInt32 FatSize; UInt32 LongStreamMinSize; unsigned SectorSizeBits; unsigned MiniSectorSizeBits; Int32 MainSubfile; + EType Type; UInt64 PhySize; UInt64 PhySize_Aligned; - EType Type; bool IsNotArcType() const { @@ -148,14 +148,14 @@ UInt64 GetItemPackSize(UInt64 size) const { - UInt64 mask = ((UInt64)1 << (IsLargeStream(size) ? SectorSizeBits : MiniSectorSizeBits)) - 1; + const UInt64 mask = ((UInt32)1 << (IsLargeStream(size) ? SectorSizeBits : MiniSectorSizeBits)) - 1; return (size + mask) & ~mask; } bool GetMiniCluster(UInt32 sid, UInt64 &res) const { - unsigned subBits = SectorSizeBits - MiniSectorSizeBits; - UInt32 fid = sid >> subBits; + const unsigned subBits = SectorSizeBits - MiniSectorSizeBits; + const UInt32 fid = sid >> subBits; if (fid >= NumSectorsInMiniStream) return false; res = (((UInt64)MiniSids[fid] + 1) << subBits) + (sid & ((1 << subBits) - 1)); @@ -177,7 +177,7 @@ HRESULT CDatabase::ReadIDs(IInStream *inStream, Byte *buf, unsigned sectorSizeBits, UInt32 sid, UInt32 *dest) { RINOK(ReadSector(inStream, buf, sectorSizeBits, sid)) - UInt32 sectorSize = (UInt32)1 << sectorSizeBits; + const UInt32 sectorSize = (UInt32)1 << sectorSizeBits; for (UInt32 t = 0; t < sectorSize; t += 4) *dest++ = Get32(buf + t); return S_OK; @@ -373,7 +373,7 @@ HRESULT CDatabase::Update_PhySize_WithItem(unsigned index) { const CItem &item = Items[index]; - bool isLargeStream = (index == 0 || IsLargeStream(item.Size)); + const bool isLargeStream = (index == 0 || IsLargeStream(item.Size)); if (!isLargeStream) return S_OK; const unsigned bsLog = isLargeStream ? SectorSizeBits : MiniSectorSizeBits; @@ -527,6 +527,10 @@ { CItem item; item.Parse(sect + i, mode64bit); + // we use (item.Size) check here. + // so we don't need additional overflow checks for (item.Size +) in another code + if (item.Size >= ((UInt64)1 << 63)) + return S_FALSE; Items.Add(item); } sid = Fat[sid]; @@ -767,11 +771,8 @@ UInt64 totalPackSize; totalSize = totalPackSize = 0; - NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); - CMyComPtr copyCoder = copyCoderSpec; - - CLocalProgress *lps = new CLocalProgress; - CMyComPtr progress = lps; + CMyComPtr2_Create copyCoder; + CMyComPtr2_Create lps; lps->Init(extractCallback, false); for (i = 0; i < numItems; i++) @@ -781,7 +782,8 @@ RINOK(lps->SetCur()) const UInt32 index = allFilesMode ? i : indices[i]; const CItem &item = _db.Items[_db.Refs[index].Did]; - + Int32 res; + { CMyComPtr outStream; const Int32 askMode = testMode ? NExtract::NAskMode::kTest : @@ -801,7 +803,7 @@ if (!testMode && !outStream) continue; RINOK(extractCallback->PrepareOperation(askMode)) - Int32 res = NExtract::NOperationResult::kDataError; + res = NExtract::NOperationResult::kDataError; CMyComPtr inStream; HRESULT hres = GetStream(index, &inStream); if (hres == S_FALSE) @@ -813,12 +815,12 @@ RINOK(hres) if (inStream) { - RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)) - if (copyCoderSpec->TotalSize == item.Size) + RINOK(copyCoder.Interface()->Code(inStream, outStream, NULL, NULL, lps)) + if (copyCoder->TotalSize == item.Size) res = NExtract::NOperationResult::kOK; } } - outStream.Release(); + } RINOK(extractCallback->SetOperationResult(res)) } return S_OK; diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Common/HandlerOut.cpp 7zip-25.01+dfsg/CPP/7zip/Archive/Common/HandlerOut.cpp --- 7zip-24.09+dfsg/CPP/7zip/Archive/Common/HandlerOut.cpp 2023-05-07 15:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/Common/HandlerOut.cpp 2025-07-02 06:00:00.000000000 +0000 @@ -4,8 +4,6 @@ #include "../../../Common/StringToInt.h" -#include "../Common/ParseProperties.h" - #include "HandlerOut.h" namespace NArchive { @@ -82,6 +80,7 @@ return true; } + bool CCommonMethodProps::SetCommonProperty(const UString &name, const PROPVARIANT &value, HRESULT &hres) { hres = S_OK; @@ -151,6 +150,11 @@ SetMethodProp32_Replace(oneMethodInfo, NCoderPropID::kNumThreads, numThreads); } +void CMultiMethodProps::Set_Method_NumThreadGroups_IfNotFinded(CMethodProps &oneMethodInfo, UInt32 numThreadGroups) +{ + SetMethodProp32(oneMethodInfo, NCoderPropID::kNumThreadGroups, numThreadGroups); +} + #endif // Z7_ST diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Common/HandlerOut.h 7zip-25.01+dfsg/CPP/7zip/Archive/Common/HandlerOut.h --- 7zip-24.09+dfsg/CPP/7zip/Archive/Common/HandlerOut.h 2024-10-22 09:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/Common/HandlerOut.h 2025-07-02 07:00:00.000000000 +0000 @@ -17,11 +17,21 @@ void InitCommon() { // _Write_MTime = true; - #ifndef Z7_ST - _numProcessors = _numThreads = NWindows::NSystem::GetNumberOfProcessors(); - _numThreads_WasForced = false; - #endif - + { +#ifndef Z7_ST + _numThreads_WasForced = false; + UInt32 numThreads; +#ifdef _WIN32 + NWindows::NSystem::CProcessAffinity aff; + numThreads = aff.Load_and_GetNumberOfThreads(); + _numThreadGroups = aff.IsGroupMode ? aff.Groups.GroupSizes.Size() : 0; +#else + numThreads = NWindows::NSystem::GetNumberOfProcessors(); +#endif // _WIN32 + _numProcessors = _numThreads = numThreads; +#endif // Z7_ST + } + size_t memAvail = (size_t)sizeof(size_t) << 28; _memAvail = memAvail; _memUsage_Compress = memAvail; @@ -46,11 +56,14 @@ } public: - #ifndef Z7_ST +#ifndef Z7_ST UInt32 _numThreads; UInt32 _numProcessors; +#ifdef _WIN32 + UInt32 _numThreadGroups; +#endif bool _numThreads_WasForced; - #endif +#endif bool _memUsage_WasSet; UInt64 _memUsage_Compress; @@ -80,10 +93,12 @@ void SetGlobalLevelTo(COneMethodInfo &oneMethodInfo) const; - #ifndef Z7_ST +#ifndef Z7_ST static void SetMethodThreadsTo_IfNotFinded(CMethodProps &props, UInt32 numThreads); static void SetMethodThreadsTo_Replace(CMethodProps &props, UInt32 numThreads); - #endif + + static void Set_Method_NumThreadGroups_IfNotFinded(CMethodProps &props, UInt32 numThreadGroups); +#endif unsigned GetNumEmptyMethods() const diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Common/ItemNameUtils.cpp 7zip-25.01+dfsg/CPP/7zip/Archive/Common/ItemNameUtils.cpp --- 7zip-24.09+dfsg/CPP/7zip/Archive/Common/ItemNameUtils.cpp 2022-01-09 18:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/Common/ItemNameUtils.cpp 2025-06-18 11:00:00.000000000 +0000 @@ -47,6 +47,25 @@ } +#if WCHAR_PATH_SEPARATOR != L'/' +void ReplaceToWinSlashes(UString &name, bool useBackslashReplacement) +{ + // name.Replace(kUnixPathSepar, kOsPathSepar); + const unsigned len = name.Len(); + for (unsigned i = 0; i < len; i++) + { + wchar_t c = name[i]; + if (c == L'/') + c = WCHAR_PATH_SEPARATOR; + else if (useBackslashReplacement && c == L'\\') + c = WCHAR_IN_FILE_NAME_BACKSLASH_REPLACEMENT; // WSL scheme + else + continue; + name.ReplaceOneCharAtPos(i, c); + } +} +#endif + void ReplaceToOsSlashes_Remove_TailSlash(UString &name, bool #if WCHAR_PATH_SEPARATOR != L'/' useBackslashReplacement @@ -57,21 +76,7 @@ return; #if WCHAR_PATH_SEPARATOR != L'/' - { - // name.Replace(kUnixPathSepar, kOsPathSepar); - const unsigned len = name.Len(); - for (unsigned i = 0; i < len; i++) - { - wchar_t c = name[i]; - if (c == L'/') - c = WCHAR_PATH_SEPARATOR; - else if (useBackslashReplacement && c == L'\\') - c = WCHAR_IN_FILE_NAME_BACKSLASH_REPLACEMENT; // WSL scheme - else - continue; - name.ReplaceOneCharAtPos(i, c); - } - } + ReplaceToWinSlashes(name, useBackslashReplacement); #endif if (name.Back() == kOsPathSepar) diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Common/ItemNameUtils.h 7zip-25.01+dfsg/CPP/7zip/Archive/Common/ItemNameUtils.h --- 7zip-24.09+dfsg/CPP/7zip/Archive/Common/ItemNameUtils.h 2023-01-10 17:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/Common/ItemNameUtils.h 2025-06-16 12:00:00.000000000 +0000 @@ -13,6 +13,9 @@ UString GetOsPath(const UString &name); UString GetOsPath_Remove_TailSlash(const UString &name); +#if WCHAR_PATH_SEPARATOR != L'/' +void ReplaceToWinSlashes(UString &name, bool useBackslashReplacement); +#endif void ReplaceToOsSlashes_Remove_TailSlash(UString &name, bool useBackslashReplacement = false); void NormalizeSlashes_in_FileName_for_OsPath(wchar_t *s, unsigned len); void NormalizeSlashes_in_FileName_for_OsPath(UString &name); diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/CpioHandler.cpp 7zip-25.01+dfsg/CPP/7zip/Archive/CpioHandler.cpp --- 7zip-24.09+dfsg/CPP/7zip/Archive/CpioHandler.cpp 2023-10-02 10:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/CpioHandler.cpp 2025-06-21 07:00:00.000000000 +0000 @@ -437,7 +437,14 @@ return S_OK; /* v23.02: we have disabled rDevMinor check because real file - from Apple contains rDevMinor==255 by some unknown reason */ + from Apple contains rDevMinor==255 by some unknown reason + cpio 2.13 and older versions: it copies stat::st_rdev to archive. + and stat::st_rdev can be non-zero for some old linux/filesystems cases for regular files. + cpio 2.14 (2023) copies st_rdev to archive only if (S_ISBLK (st->st_mode) || S_ISCHR (st->st_mode)) + v25.00: we have disabled RDevMajor check here to support some rare case created by cpio 2.13- with old linux. + But we still keep full check in IsArc_Cpio() to reduce false cpio detection cases. + */ +#if 0 // 0 : to disable check to support some old linux cpio archives. if (item.RDevMajor != 0 // || item.RDevMinor != 0 ) @@ -446,6 +453,7 @@ !MY_LIN_S_ISBLK(item.Mode)) return S_OK; } +#endif // Size must be 0 for FIFOs and directories if (item.IsDir() || MY_LIN_S_ISFIFO(item.Mode)) @@ -873,17 +881,13 @@ { case kpidPath: { - UString res; - bool needConvert = true; - #ifdef _WIN32 - // if ( - ConvertUTF8ToUnicode(item.Name, res); - // ) - needConvert = false; - #endif - if (needConvert) - res = MultiByteToUnicodeString(item.Name, CP_OEMCP); - prop = NItemName::GetOsPath(res); +#ifdef _WIN32 + UString u; + ConvertUTF8ToUnicode(item.Name, u); +#else + const UString u = MultiByteToUnicodeString(item.Name, CP_OEMCP); +#endif + prop = NItemName::GetOsPath(u); break; } case kpidIsDir: prop = item.IsDir(); break; @@ -921,16 +925,12 @@ s.SetFrom_CalcLen((const char *)(const void *)(const Byte *)item.Data, (unsigned)item.Data.Size()); if (s.Len() == item.Data.Size()) { +#ifdef _WIN32 UString u; - bool needConvert = true; - #ifdef _WIN32 - // if ( - ConvertUTF8ToUnicode(item.Name, u); - // ) - needConvert = false; - #endif - if (needConvert) - u = MultiByteToUnicodeString(s, CP_OEMCP); + ConvertUTF8ToUnicode(item.Name, u); +#else + const UString u = MultiByteToUnicodeString(s, CP_OEMCP); +#endif prop = u; } } diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/DmgHandler.cpp 7zip-25.01+dfsg/CPP/7zip/Archive/DmgHandler.cpp --- 7zip-24.09+dfsg/CPP/7zip/Archive/DmgHandler.cpp 2024-03-28 12:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/DmgHandler.cpp 2025-06-16 09:00:00.000000000 +0000 @@ -444,7 +444,7 @@ { const CAppleName &a = k_Names[i]; if (a.Ext) - if (name == a.AppleName) + if (name.IsEqualTo(a.AppleName)) return a.Ext; } return NULL; @@ -784,7 +784,7 @@ for (unsigned i = 0; i + 1 < item.SubItems.Size(); i++) { const CXmlItem &si = item.SubItems[i]; - if (si.IsTagged("key") && si.GetSubString() == key) + if (si.IsTagged("key") && si.GetSubString().IsEqualTo(key)) { const CXmlItem *si_1 = &item.SubItems[i + 1]; if (si_1->IsTagged(nextTag)) @@ -1251,7 +1251,7 @@ #endif } - if (xml.Root.Name != "plist") + if (!xml.Root.Name.IsEqualTo("plist")) return S_FALSE; const CXmlItem *dictItem = xml.Root.FindSubTag_GetPtr("dict"); diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/FatHandler.cpp 7zip-25.01+dfsg/CPP/7zip/Archive/FatHandler.cpp --- 7zip-24.09+dfsg/CPP/7zip/Archive/FatHandler.cpp 2023-06-26 17:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/FatHandler.cpp 2025-02-09 09:00:00.000000000 +0000 @@ -2,13 +2,12 @@ #include "StdAfx.h" -// #include - #include "../../../C/CpuArch.h" #include "../../Common/ComTry.h" #include "../../Common/IntToString.h" #include "../../Common/MyBuffer.h" +#include "../../Common/MyBuffer2.h" #include "../../Common/MyCom.h" #include "../../Common/StringConvert.h" @@ -22,14 +21,19 @@ #include "../Compress/CopyCoder.h" -#include "Common/DummyOutStream.h" +#include "Common/ItemNameUtils.h" #define Get16(p) GetUi16(p) #define Get32(p) GetUi32(p) #define Get16a(p) GetUi16a(p) #define Get32a(p) GetUi32a(p) -#define PRF(x) /* x */ +#if 0 +#include +#define PRF(x) x +#else +#define PRF(x) +#endif namespace NArchive { namespace NFat { @@ -38,35 +42,34 @@ struct CHeader { - UInt32 NumSectors; - UInt16 NumReservedSectors; + Byte NumFatBits; + Byte SectorSizeLog; + Byte SectorsPerClusterLog; + Byte ClusterSizeLog; Byte NumFats; + Byte MediaType; + + bool VolFieldsDefined; + bool HeadersWarning; + + UInt32 FatSize; + UInt32 BadCluster; + + UInt16 NumReservedSectors; + UInt32 NumSectors; UInt32 NumFatSectors; UInt32 RootDirSector; UInt32 NumRootDirSectors; UInt32 DataSector; - UInt32 FatSize; - UInt32 BadCluster; - - Byte NumFatBits; - Byte SectorSizeLog; - Byte SectorsPerClusterLog; - Byte ClusterSizeLog; - UInt16 SectorsPerTrack; UInt16 NumHeads; UInt32 NumHiddenSectors; - - bool VolFieldsDefined; - bool HeadersWarning; UInt32 VolId; // Byte VolName[11]; // Byte FileSys[8]; - // Byte OemName[5]; - Byte MediaType; // 32-bit FAT UInt16 Flags; @@ -104,15 +107,8 @@ bool Parse(const Byte *p); }; -static int GetLog(UInt32 num) -{ - for (int i = 0; i < 31; i++) - if (((UInt32)1 << i) == num) - return i; - return -1; -} -static const UInt32 kHeaderSize = 512; +static const unsigned kHeaderSize = 512; API_FUNC_IsArc IsArc_Fat(const Byte *p, size_t size); API_FUNC_IsArc IsArc_Fat(const Byte *p, size_t size) @@ -125,7 +121,7 @@ bool CHeader::Parse(const Byte *p) { - if (p[0x1FE] != 0x55 || p[0x1FF] != 0xAA) + if (Get16(p + 0x1FE) != 0xAA55) return false; HeadersWarning = false; @@ -139,22 +135,40 @@ } { { - const UInt32 val32 = Get16(p + 11); - const int s = GetLog(val32); - if (s < 9 || s > 12) - return false; - SectorSizeLog = (Byte)s; + const unsigned num = Get16(p + 11); + unsigned i = 9; + unsigned m = 1 << i; + for (;;) + { + if (m == num) + break; + m <<= 1; + if (++i > 12) + return false; + } + SectorSizeLog = (Byte)i; } { - const UInt32 val32 = p[13]; - const int s = GetLog(val32); - if (s < 0) + const unsigned num = p[13]; + unsigned i = 0; + unsigned m = 1 << i; + for (;;) + { + if (m == num) + break; + m <<= 1; + if (++i > 7) + return false; + } + SectorsPerClusterLog = (Byte)i; + i += SectorSizeLog; + ClusterSizeLog = (Byte)i; + // (2^15 = 32 KB is safe cluster size that is suported by all system. + // (2^16 = 64 KB is supported by some systems + // (128 KB / 256 KB) can be created by some tools, but it is not supported by many tools. + if (i > 18) // 256 KB return false; - SectorsPerClusterLog = (Byte)s; } - ClusterSizeLog = (Byte)(SectorSizeLog + SectorsPerClusterLog); - if (ClusterSizeLog > 24) - return false; } NumReservedSectors = Get16(p + 14); @@ -169,7 +183,7 @@ const bool isOkOffset = (codeOffset == 0) || (codeOffset == (p[0] == 0xEB ? 2 : 3)); - const UInt16 numRootDirEntries = Get16(p + 17); + const unsigned numRootDirEntries = Get16(p + 17); if (numRootDirEntries == 0) { if (codeOffset < 90 && !isOkOffset) @@ -183,10 +197,10 @@ if (codeOffset < 62 - 24 && !isOkOffset) return false; NumFatBits = 0; - const UInt32 mask = (1 << (SectorSizeLog - 5)) - 1; - if ((numRootDirEntries & mask) != 0) + const unsigned mask = (1u << (SectorSizeLog - 5)) - 1; + if (numRootDirEntries & mask) return false; - NumRootDirSectors = (numRootDirEntries + mask) >> (SectorSizeLog - 5); + NumRootDirSectors = (numRootDirEntries /* + mask */) >> (SectorSizeLog - 5); } NumSectors = Get16(p + 19); @@ -198,7 +212,6 @@ else if (IsFat32()) return false; */ - MediaType = p[21]; NumFatSectors = Get16(p + 22); SectorsPerTrack = Get16(p + 24); @@ -222,7 +235,7 @@ return false; RootCluster = Get32(p + 8); FsInfoSector = Get16(p + 12); - for (int i = 16; i < 28; i++) + for (unsigned i = 16; i < 28; i++) if (p[i] != 0) return false; p += 28; @@ -260,7 +273,7 @@ if (numClusters >= 0xFFF5) return false; NumFatBits = (Byte)(numClusters < 0xFF5 ? 12 : 16); - BadCluster &= ((1 << NumFatBits) - 1); + BadCluster &= (((UInt32)1 << NumFatBits) - 1); } FatSize = numClusters + 2; @@ -283,103 +296,157 @@ return true; } -struct CItem + + +class CItem { - UString UName; - char DosName[11]; + Z7_CLASS_NO_COPY(CItem) +public: + UInt32 Size; + Byte Attrib; Byte CTime2; - UInt32 CTime; - UInt32 MTime; UInt16 ADate; - Byte Attrib; + CByteBuffer LongName; // if LongName.Size() == 0 : no long name + // if LongName.Size() != 0 : it's NULL terminated UTF16-LE string. + char DosName[11]; Byte Flags; - UInt32 Size; + UInt32 MTime; + UInt32 CTime; UInt32 Cluster; Int32 Parent; + CItem() {} + // NT uses Flags to store Low Case status bool NameIsLow() const { return (Flags & 0x8) != 0; } bool ExtIsLow() const { return (Flags & 0x10) != 0; } bool IsDir() const { return (Attrib & 0x10) != 0; } - UString GetShortName() const; - UString GetName() const; - UString GetVolName() const; + void GetShortName(UString &dest) const; + void GetName(UString &name) const; }; -static unsigned CopyAndTrim(char *dest, const char *src, unsigned size, bool toLower) + +static char *CopyAndTrim(char *dest, const char *src, + unsigned size, unsigned toLower) { - memcpy(dest, src, size); - if (toLower) + do { - for (unsigned i = 0; i < size; i++) + if (src[(size_t)size - 1] != ' ') { - char c = dest[i]; - if (c >= 'A' && c <= 'Z') - dest[i] = (char)(c + 0x20); + const unsigned range = toLower ? 'Z' - 'A' + 1 : 0; + do + { + unsigned c = (Byte)*src++; + if ((unsigned)(c - 'A') < range) + c += 0x20; + *dest++ = (char)c; + } + while (--size); + break; } } - - for (unsigned i = size;;) - { - if (i == 0) - return 0; - if (dest[i - 1] != ' ') - return i; - i--; - } + while (--size); + *dest = 0; + return dest; } -static UString FatStringToUnicode(const char *s) + +static void FatStringToUnicode(UString &dest, const char *s) { - return MultiByteToUnicodeString(s, CP_OEMCP); + MultiByteToUnicodeString2(dest, AString(s), CP_OEMCP); } -UString CItem::GetShortName() const +void CItem::GetShortName(UString &shortName) const { char s[16]; - unsigned i = CopyAndTrim(s, DosName, 8, NameIsLow()); - s[i++] = '.'; - unsigned j = CopyAndTrim(s + i, DosName + 8, 3, ExtIsLow()); - if (j == 0) - i--; - s[i + j] = 0; - return FatStringToUnicode(s); + char *dest = CopyAndTrim(s, DosName, 8, NameIsLow()); + *dest++ = '.'; + char *dest2 = CopyAndTrim(dest, DosName + 8, 3, ExtIsLow()); + if (dest == dest2) + dest[-1] = 0; + FatStringToUnicode(shortName, s); } -UString CItem::GetName() const + + +// numWords != 0 +static unsigned ParseLongName(UInt16 *buf, unsigned numWords) { - if (!UName.IsEmpty()) - return UName; - return GetShortName(); + unsigned i; + for (i = 0; i < numWords; i++) + { + const unsigned c = buf[i]; + if (c == 0) + break; + if (c == 0xFFFF) + return 0; + } + if (i == 0) + return 0; + buf[i] = 0; + numWords -= i; + i++; + if (numWords > 1) + { + numWords--; + buf += i; + do + if (*buf++ != 0xFFFF) + return 0; + while (--numWords); + } + return i; // it includes NULL terminator +} + + +void CItem::GetName(UString &name) const +{ + if (LongName.Size() >= 2) + { + const Byte * const p = LongName; + const unsigned numWords = ((unsigned)LongName.Size() - 2) / 2; + wchar_t *dest = name.GetBuf(numWords); + for (unsigned i = 0; i < numWords; i++) + dest[i] = (wchar_t)Get16(p + (size_t)i * 2); + name.ReleaseBuf_SetEnd(numWords); + } + else + GetShortName(name); + if (name.IsEmpty()) // it's unexpected + name = '_'; + NItemName::NormalizeSlashes_in_FileName_for_OsPath(name); } -UString CItem::GetVolName() const + +static void GetVolName(const char dosName[11], NWindows::NCOM::CPropVariant &prop) { - if (!UName.IsEmpty()) - return UName; char s[12]; - unsigned i = CopyAndTrim(s, DosName, 11, false); - s[i] = 0; - return FatStringToUnicode(s); + CopyAndTrim(s, dosName, 11, false); + UString u; + FatStringToUnicode(u, AString(s)); + prop = u; } + struct CDatabase { - CHeader Header; CObjectVector Items; UInt32 *Fat; + CHeader Header; CMyComPtr InStream; IArchiveOpenCallback *OpenCallback; + CAlignedBuffer ByteBuf; + CByteBuffer LfnBuf; UInt32 NumFreeClusters; - bool VolItemDefined; - CItem VolItem; UInt32 NumDirClusters; - CByteBuffer ByteBuf; UInt64 NumCurUsedBytes; - UInt64 PhySize; + UInt32 Vol_MTime; + char VolLabel[11]; + bool VolItem_Defined; + CDatabase(): Fat(NULL) {} ~CDatabase() { ClearAndClose(); } @@ -388,7 +455,7 @@ HRESULT OpenProgressFat(bool changeTotal = true); HRESULT OpenProgress(); - UString GetItemPath(UInt32 index) const; + void GetItemPath(UInt32 index, UString &s) const; HRESULT Open(); HRESULT ReadDir(Int32 parent, UInt32 cluster, unsigned level); @@ -400,6 +467,7 @@ HRESULT SeekToCluster(UInt32 cluster) { return SeekToSector(Header.ClusterToSector(cluster)); } }; + HRESULT CDatabase::SeekToSector(UInt32 sector) { return InStream_SeekSet(InStream, (UInt64)sector << Header.SectorSizeLog); @@ -408,7 +476,7 @@ void CDatabase::Clear() { PhySize = 0; - VolItemDefined = false; + VolItem_Defined = false; NumDirClusters = 0; NumCurUsedBytes = 0; @@ -440,49 +508,35 @@ { if (!OpenCallback) return S_OK; - UInt64 numItems = Items.Size(); + const UInt64 numItems = Items.Size(); return OpenCallback->SetCompleted(&numItems, &NumCurUsedBytes); } -UString CDatabase::GetItemPath(UInt32 index) const +void CDatabase::GetItemPath(UInt32 index, UString &s) const { - const CItem *item = &Items[index]; - UString name = item->GetName(); + UString name; for (;;) { - index = (UInt32)item->Parent; - if (item->Parent < 0) - return name; - item = &Items[index]; - name.InsertAtFront(WCHAR_PATH_SEPARATOR); - if (item->UName.IsEmpty()) - name.Insert(0, item->GetShortName()); - else - name.Insert(0, item->UName); + const CItem &item = Items[index]; + item.GetName(name); + if (item.Parent >= 0) + name.InsertAtFront(WCHAR_PATH_SEPARATOR); + s.Insert(0, name); + index = (UInt32)item.Parent; + if (item.Parent < 0) + break; } } -static wchar_t *AddSubStringToName(wchar_t *dest, const Byte *p, unsigned numChars) -{ - for (unsigned i = 0; i < numChars; i++) - { - wchar_t c = Get16(p + i * 2); - if (c != 0 && c != 0xFFFF) - *dest++ = c; - } - *dest = 0; - return dest; -} HRESULT CDatabase::ReadDir(Int32 parent, UInt32 cluster, unsigned level) { - unsigned startIndex = Items.Size(); + const unsigned startIndex = Items.Size(); if (startIndex >= (1 << 30) || level > 256) return S_FALSE; - UInt32 sectorIndex = 0; UInt32 blockSize = Header.ClusterSize(); - bool clusterMode = (Header.IsFat32() || parent >= 0); + const bool clusterMode = (Header.IsFat32() || parent >= 0); if (!clusterMode) { blockSize = Header.SectorSize(); @@ -490,21 +544,26 @@ } ByteBuf.Alloc(blockSize); - UString curName; - int checkSum = -1; - int numLongRecords = -1; + + const unsigned k_NumLfnRecords_MAX = 20; // 260 symbols limit (strict limit) + // const unsigned k_NumLfnRecords_MAX = 0x40 - 1; // 1260 symbols limit (relaxed limit) + const unsigned k_NumLfnBytes_in_Record = 13 * 2; + // we reserve 2 additional bytes for NULL terminator + LfnBuf.Alloc(k_NumLfnRecords_MAX * k_NumLfnBytes_in_Record + 2 * 1); + UInt32 curDirBytes_read = 0; + UInt32 sectorIndex = 0; + unsigned num_lfn_records = 0; + unsigned lfn_RecordIndex = 0; + int checkSum = -1; + bool is_record_error = false; + for (UInt32 pos = blockSize;; pos += 32) { if (pos == blockSize) { pos = 0; - if ((NumDirClusters & 0xFF) == 0) - { - RINOK(OpenProgress()) - } - if (clusterMode) { if (Header.IsEoc(cluster)) @@ -514,21 +573,37 @@ PRF(printf("\nCluster = %4X", cluster)); RINOK(SeekToCluster(cluster)) const UInt32 newCluster = Fat[cluster]; - if ((newCluster & kFatItemUsedByDirMask) != 0) + if (newCluster & kFatItemUsedByDirMask) return S_FALSE; Fat[cluster] |= kFatItemUsedByDirMask; cluster = newCluster; NumDirClusters++; + if ((NumDirClusters & 0xFF) == 0) + { + RINOK(OpenProgress()) + } NumCurUsedBytes += Header.ClusterSize(); } else if (sectorIndex++ >= Header.NumRootDirSectors) break; + // if (curDirBytes_read > (1u << 28)) // do we need some relaxed limit for non-MS FATs? + if (curDirBytes_read >= (1u << 21)) // 2MB limit from FAT specification. + return S_FALSE; RINOK(ReadStream_FALSE(InStream, ByteBuf, blockSize)) + curDirBytes_read += blockSize; } - const Byte *p = ByteBuf + pos; - + if (is_record_error) + { + Header.HeadersWarning = true; + num_lfn_records = 0; + lfn_RecordIndex = 0; + checkSum = -1; + } + + const Byte * const p = ByteBuf + pos; + if (p[0] == 0) { /* @@ -538,125 +613,191 @@ */ break; } - + + is_record_error = true; + if (p[0] == 0xE5) { - if (numLongRecords > 0) - return S_FALSE; + // deleted entry + if (num_lfn_records == 0) + is_record_error = false; continue; } - - Byte attrib = p[11]; - if ((attrib & 0x3F) == 0xF) + // else { - if (p[0] > 0x7F || Get16(p + 26) != 0) - return S_FALSE; - int longIndex = p[0] & 0x3F; - if (longIndex == 0) - return S_FALSE; - bool isLast = (p[0] & 0x40) != 0; - if (numLongRecords < 0) + const Byte attrib = p[11]; + // maybe we can use more strick check : (attrib == 0xF) ? + if ((attrib & 0x3F) == 0xF) { - if (!isLast) + // long file name (LFN) entry + const unsigned longIndex = p[0] & 0x3F; + if (longIndex == 0 + || longIndex > k_NumLfnRecords_MAX + || p[0] > 0x7F + || Get16a(p + 26) != 0 // LDIR_FstClusLO + ) + { + return S_FALSE; + // break; + } + const bool isLast = (p[0] & 0x40) != 0; + if (num_lfn_records == 0) + { + if (!isLast) + continue; // orphan + num_lfn_records = longIndex; + } + else if (isLast || longIndex != lfn_RecordIndex) + { return S_FALSE; - numLongRecords = longIndex; + // break; + } + + lfn_RecordIndex = longIndex - 1; + + if (p[12] == 0) + { + Byte * const dest = LfnBuf + k_NumLfnBytes_in_Record * lfn_RecordIndex; + memcpy(dest, p + 1, 5 * 2); + memcpy(dest + 5 * 2, p + 14, 6 * 2); + memcpy(dest + 11 * 2, p + 28, 2 * 2); + if (isLast) + checkSum = p[13]; + if (checkSum == p[13]) + is_record_error = false; + // else return S_FALSE; + continue; + } + // else + checkSum = -1; // we will ignore LfnBuf in this case + continue; } - else if (isLast || numLongRecords != longIndex) - return S_FALSE; - - numLongRecords--; - if (p[12] == 0) + if (lfn_RecordIndex) { - wchar_t nameBuf[14]; - wchar_t *dest; - - dest = AddSubStringToName(nameBuf, p + 1, 5); - dest = AddSubStringToName(dest, p + 14, 6); - AddSubStringToName(dest, p + 28, 2); - curName = nameBuf + curName; - if (isLast) - checkSum = p[13]; - if (checkSum != p[13]) - return S_FALSE; + Header.HeadersWarning = true; + // return S_FALSE; } - } - else - { - if (numLongRecords > 0) - return S_FALSE; - CItem item; - memcpy(item.DosName, p, 11); + // lfn_RecordIndex = 0; - if (checkSum >= 0) + const unsigned type_in_attrib = attrib & 0x18; + if (type_in_attrib == 0x18) { - Byte sum = 0; - for (unsigned i = 0; i < 11; i++) - sum = (Byte)(((sum & 1) ? 0x80 : 0) + (sum >> 1) + (Byte)item.DosName[i]); - if (sum == checkSum) - item.UName = curName; + // invalid directory record (both flags are set: dir_flag and volume_flag) + return S_FALSE; + // break; + // continue; } - - if (item.DosName[0] == 5) - item.DosName[0] = (char)(Byte)0xE5; - item.Attrib = attrib; - item.Flags = p[12]; - item.Size = Get32(p + 28); - item.Cluster = Get16(p + 26); - if (Header.NumFatBits > 16) - item.Cluster |= ((UInt32)Get16(p + 20) << 16); - else + if (type_in_attrib == 8) // volume_flag { - // OS/2 and WinNT probably can store EA (extended atributes) in that field. + if (!VolItem_Defined && level == 0) + { + VolItem_Defined = true; + memcpy(VolLabel, p, 11); + Vol_MTime = Get32(p + 22); + is_record_error = false; + } } - - item.CTime = Get32(p + 14); - item.CTime2 = p[13]; - item.ADate = Get16(p + 18); - item.MTime = Get32(p + 22); - item.Parent = parent; - - if (attrib == 8) + else if (memcmp(p, ". ", 11) == 0 + || memcmp(p, ".. ", 11) == 0) { - VolItem = item; - VolItemDefined = true; + if (num_lfn_records == 0 && type_in_attrib == 0x10) // dir_flag + is_record_error = false; } else - if (memcmp(item.DosName, ". ", 11) != 0 && - memcmp(item.DosName, ".. ", 11) != 0) { - if (!item.IsDir()) - NumCurUsedBytes += Header.GetFilePackSize(item.Size); - Items.Add(item); - PRF(printf("\n%7d: %S", Items.Size(), GetItemPath(Items.Size() - 1))); + CItem &item = Items.AddNew(); + memcpy(item.DosName, p, 11); + if (item.DosName[0] == 5) + item.DosName[0] = (char)(Byte)0xE5; // 0xE5 is valid KANJI lead byte value. + item.Attrib = attrib; + item.Flags = p[12]; + item.Size = Get32a(p + 28); + item.Cluster = Get16a(p + 26); + if (Header.NumFatBits > 16) + item.Cluster |= ((UInt32)Get16a(p + 20) << 16); + else + { + // OS/2 and WinNT probably can store EA (extended atributes) in that field. + } + item.CTime = Get32(p + 14); + item.CTime2 = p[13]; + item.ADate = Get16a(p + 18); + item.MTime = Get32(p + 22); + item.Parent = parent; + { + if (!item.IsDir()) + NumCurUsedBytes += Header.GetFilePackSize(item.Size); + // PRF(printf("\n%7d: %S", Items.Size(), GetItemPath(Items.Size() - 1))); + PRF(printf("\n%7d" /* ": %S" */, Items.Size() /* , item.GetShortName() */ );) + } + if (num_lfn_records == 0) + is_record_error = false; + else if (checkSum >= 0 && lfn_RecordIndex == 0) + { + Byte sum = 0; + for (unsigned i = 0; i < 11; i++) + sum = (Byte)((sum << 7) + (sum >> 1) + (Byte)item.DosName[i]); + if (sum == checkSum) + { + const unsigned numWords = ParseLongName((UInt16 *)(void *)(Byte *)LfnBuf, + num_lfn_records * k_NumLfnBytes_in_Record / 2); + if (numWords > 1) + { + // numWords includes NULL terminator + item.LongName.CopyFrom(LfnBuf, numWords * 2); + is_record_error = false; + } + } + } + + if ( + // item.LongName.Size() < 20 || // for debug + item.LongName.Size() <= 2 * 1 + && memcmp(p, " ", 11) == 0) + { + char s[16 + 16]; + const size_t numChars = (size_t)(ConvertUInt32ToString( + Items.Size() - 1 - startIndex, + MyStpCpy(s, "[NONAME]-")) - s) + 1; + item.LongName.Alloc(numChars * 2); + for (size_t i = 0; i < numChars; i++) + { + SetUi16a(item.LongName + i * 2, (Byte)s[i]) + } + Header.HeadersWarning = true; + } } - numLongRecords = -1; - curName.Empty(); - checkSum = -1; + num_lfn_records = 0; } } - unsigned finishIndex = Items.Size(); + if (is_record_error) + Header.HeadersWarning = true; + + const unsigned finishIndex = Items.Size(); for (unsigned i = startIndex; i < finishIndex; i++) { const CItem &item = Items[i]; if (item.IsDir()) { - PRF(printf("\n%S", GetItemPath(i))); - RINOK(CDatabase::ReadDir((int)i, item.Cluster, level + 1)) + PRF(printf("\n---- %c%c%c%c%c", item.DosName[0], item.DosName[1], item.DosName[2], item.DosName[3], item.DosName[4])); + RINOK(ReadDir((int)i, item.Cluster, level + 1)) } } return S_OK; } + + HRESULT CDatabase::Open() { Clear(); - bool numFreeClustersDefined = false; + bool numFreeClusters_Defined = false; { - Byte buf[kHeaderSize]; - RINOK(ReadStream_FALSE(InStream, buf, kHeaderSize)) - if (!Header.Parse(buf)) + UInt32 buf32[kHeaderSize / 4]; + RINOK(ReadStream_FALSE(InStream, buf32, kHeaderSize)) + if (!Header.Parse((Byte *)(void *)buf32)) return S_FALSE; UInt64 fileSize; RINOK(InStream_GetSize_SeekToEnd(InStream, fileSize)) @@ -671,21 +812,21 @@ { if (((UInt32)Header.FsInfoSector << Header.SectorSizeLog) + kHeaderSize <= fileSize && SeekToSector(Header.FsInfoSector) == S_OK - && ReadStream_FALSE(InStream, buf, kHeaderSize) == S_OK - && 0xaa550000 == Get32(buf + 508) - && 0x41615252 == Get32(buf) - && 0x61417272 == Get32(buf + 484)) + && ReadStream_FALSE(InStream, buf32, kHeaderSize) == S_OK + && 0xaa550000 == Get32a(buf32 + 508 / 4) + && 0x41615252 == Get32a(buf32) + && 0x61417272 == Get32a(buf32 + 484 / 4)) { - NumFreeClusters = Get32(buf + 488); - numFreeClustersDefined = (NumFreeClusters <= Header.FatSize); + NumFreeClusters = Get32a(buf32 + 488 / 4); + numFreeClusters_Defined = (NumFreeClusters <= Header.FatSize); } else Header.HeadersWarning = true; } } - // numFreeClustersDefined = false; // to recalculate NumFreeClusters - if (!numFreeClustersDefined) + // numFreeClusters_Defined = false; // to recalculate NumFreeClusters + if (!numFreeClusters_Defined) NumFreeClusters = 0; CByteBuffer byteBuf; @@ -695,7 +836,7 @@ RINOK(SeekToSector(Header.GetFatSector())) if (Header.NumFatBits == 32) { - const UInt32 kBufSize = (1 << 15); + const UInt32 kBufSize = 1 << 15; byteBuf.Alloc(kBufSize); for (UInt32 i = 0;;) { @@ -712,7 +853,7 @@ const UInt32 *src = (const UInt32 *)(const void *)(const Byte *)byteBuf; UInt32 *dest = Fat + i; const UInt32 *srcLim = src + size; - if (numFreeClustersDefined) + if (numFreeClusters_Defined) do *dest++ = Get32a(src) & 0x0FFFFFFF; while (++src != srcLim); @@ -731,7 +872,7 @@ i += size; if ((i & 0xFFFFF) == 0) { - RINOK(OpenProgressFat(!numFreeClustersDefined)) + RINOK(OpenProgressFat(!numFreeClusters_Defined)) } } } @@ -751,7 +892,7 @@ for (UInt32 j = 0; j < fatSize; j++) fat[j] = (Get16(p + j * 3 / 2) >> ((j & 1) << 2)) & 0xFFF; - if (!numFreeClustersDefined) + if (!numFreeClusters_Defined) { UInt32 numFreeClusters = 0; for (UInt32 i = 0; i < fatSize; i++) @@ -781,11 +922,12 @@ Z7_class_CHandler_final: public IInArchive, + public IArchiveGetRawProps, public IInArchiveGetStream, public CMyUnknownImp, CDatabase { - Z7_IFACES_IMP_UNK_2(IInArchive, IInArchiveGetStream) + Z7_IFACES_IMP_UNK_3(IInArchive, IArchiveGetRawProps, IInArchiveGetStream) }; Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream)) @@ -831,6 +973,8 @@ COM_TRY_END } + + static const Byte kProps[] = { kpidPath, @@ -842,6 +986,7 @@ kpidATime, kpidAttrib, kpidShortName + // , kpidCharacts }; enum @@ -922,15 +1067,16 @@ case kpidPhySize: prop = PhySize; break; case kpidFreeSpace: prop = (UInt64)NumFreeClusters << Header.ClusterSizeLog; break; case kpidHeadersSize: prop = GetHeadersSize(); break; - case kpidMTime: if (VolItemDefined) PropVariant_SetFrom_DosTime(prop, VolItem.MTime); break; + case kpidMTime: if (VolItem_Defined) PropVariant_SetFrom_DosTime(prop, Vol_MTime); break; case kpidShortComment: - case kpidVolumeName: if (VolItemDefined) prop = VolItem.GetVolName(); break; + case kpidVolumeName: if (VolItem_Defined) GetVolName(VolLabel, prop); break; case kpidNumFats: if (Header.NumFats != 2) prop = Header.NumFats; break; case kpidSectorSize: prop = (UInt32)1 << Header.SectorSizeLog; break; // case kpidSectorsPerTrack: prop = Header.SectorsPerTrack; break; // case kpidNumHeads: prop = Header.NumHeads; break; // case kpidOemName: STRING_TO_PROP(Header.OemName, prop); break; case kpidId: if (Header.VolFieldsDefined) prop = Header.VolId; break; + case kpidIsTree: prop = true; break; // case kpidVolName: if (Header.VolFieldsDefined) STRING_TO_PROP(Header.VolName, prop); break; // case kpidFileSysType: if (Header.VolFieldsDefined) STRING_TO_PROP(Header.FileSys, prop); break; // case kpidHiddenSectors: prop = Header.NumHiddenSectors; break; @@ -948,6 +1094,52 @@ COM_TRY_END } + +Z7_COM7F_IMF(CHandler::GetNumRawProps(UInt32 *numProps)) +{ + *numProps = 0; + return S_OK; +} + +Z7_COM7F_IMF(CHandler::GetRawPropInfo(UInt32 /* index */ , BSTR *name, PROPID *propID)) +{ + *name = NULL; + *propID = 0; + return S_OK; +} + +Z7_COM7F_IMF(CHandler::GetParent(UInt32 index, UInt32 *parent, UInt32 *parentType)) +{ + *parentType = NParentType::kDir; + int par = -1; + if (index < Items.Size()) + par = Items[index].Parent; + *parent = (UInt32)(Int32)par; + return S_OK; +} + +Z7_COM7F_IMF(CHandler::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType)) +{ + *data = NULL; + *dataSize = 0; + *propType = 0; + + if (index < Items.Size() + && propID == kpidName) + { + CByteBuffer &buf = Items[index].LongName; + const UInt32 size = (UInt32)buf.Size(); + if (size != 0) + { + *dataSize = size; + *propType = NPropDataType::kUtf16z; + *data = (const void *)(const Byte *)buf; + } + } + return S_OK; +} + + Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)) { COM_TRY_BEGIN @@ -955,8 +1147,28 @@ const CItem &item = Items[index]; switch (propID) { - case kpidPath: prop = GetItemPath(index); break; - case kpidShortName: prop = item.GetShortName(); break; + case kpidPath: + case kpidName: + case kpidShortName: + { + UString s; + if (propID == kpidPath) + GetItemPath(index, s); + else if (propID == kpidName) + item.GetName(s); + else + item.GetShortName(s); + prop = s; + break; + } +/* + case kpidCharacts: + { + if (item.LongName.Size()) + prop = "LFN"; + break; + } +*/ case kpidIsDir: prop = item.IsDir(); break; case kpidMTime: PropVariant_SetFrom_DosTime(prop, item.MTime); break; case kpidCTime: FatTimeToProp(item.CTime, item.CTime2, prop); break; @@ -1004,34 +1216,44 @@ Int32 testMode, IArchiveExtractCallback *extractCallback)) { COM_TRY_BEGIN - const bool allFilesMode = (numItems == (UInt32)(Int32)-1); - if (allFilesMode) + if (numItems == (UInt32)(Int32)-1) + { + indices = NULL; numItems = Items.Size(); - if (numItems == 0) - return S_OK; - UInt32 i; + if (numItems == 0) + return S_OK; + } + else + { + if (numItems == 0) + return S_OK; + if (!indices) + return E_INVALIDARG; + } UInt64 totalSize = 0; - for (i = 0; i < numItems; i++) { - const CItem &item = Items[allFilesMode ? i : indices[i]]; - if (!item.IsDir()) - totalSize += item.Size; + UInt32 i = 0; + do + { + UInt32 index = i; + if (indices) + index = indices[i]; + const CItem &item = Items[index]; + if (!item.IsDir()) + totalSize += item.Size; + } + while (++i != numItems); } RINOK(extractCallback->SetTotal(totalSize)) - UInt64 totalPackSize; - totalSize = totalPackSize = 0; - - NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); - CMyComPtr copyCoder = copyCoderSpec; - - CLocalProgress *lps = new CLocalProgress; - CMyComPtr progress = lps; + CMyComPtr2_Create lps; lps->Init(extractCallback, false); + CMyComPtr2_Create copyCoder; - CDummyOutStream *outStreamSpec = new CDummyOutStream; - CMyComPtr outStream(outStreamSpec); + UInt64 totalPackSize; + totalSize = totalPackSize = 0; + UInt32 i; for (i = 0;; i++) { lps->InSize = totalPackSize; @@ -1039,46 +1261,45 @@ RINOK(lps->SetCur()) if (i == numItems) break; - CMyComPtr realOutStream; - const Int32 askMode = testMode ? - NExtract::NAskMode::kTest : - NExtract::NAskMode::kExtract; - const UInt32 index = allFilesMode ? i : indices[i]; - const CItem &item = Items[index]; - RINOK(extractCallback->GetStream(index, &realOutStream, askMode)) - - if (item.IsDir()) + int res; { + CMyComPtr realOutStream; + const Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + UInt32 index = i; + if (indices) + index = indices[i]; + const CItem &item = Items[index]; + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)) + + if (item.IsDir()) + { + RINOK(extractCallback->PrepareOperation(askMode)) + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)) + continue; + } + + totalPackSize += Header.GetFilePackSize(item.Size); + totalSize += item.Size; + + if (!testMode && !realOutStream) + continue; RINOK(extractCallback->PrepareOperation(askMode)) - RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)) - continue; - } - - totalPackSize += Header.GetFilePackSize(item.Size); - totalSize += item.Size; - - if (!testMode && !realOutStream) - continue; - RINOK(extractCallback->PrepareOperation(askMode)) - - outStreamSpec->SetStream(realOutStream); - realOutStream.Release(); - outStreamSpec->Init(); - - int res = NExtract::NOperationResult::kDataError; - CMyComPtr inStream; - HRESULT hres = GetStream(index, &inStream); - if (hres != S_FALSE) - { - RINOK(hres) - if (inStream) + res = NExtract::NOperationResult::kDataError; + CMyComPtr inStream; + const HRESULT hres = GetStream(index, &inStream); + if (hres != S_FALSE) { - RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)) - if (copyCoderSpec->TotalSize == item.Size) - res = NExtract::NOperationResult::kOK; + RINOK(hres) + if (inStream) + { + RINOK(copyCoder.Interface()->Code(inStream, realOutStream, NULL, NULL, lps)) + if (copyCoder->TotalSize == item.Size) + res = NExtract::NOperationResult::kOK; + } } } - outStreamSpec->ReleaseStream(); RINOK(extractCallback->SetOperationResult(res)) } return S_OK; diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Nsis/NsisIn.cpp 7zip-25.01+dfsg/CPP/7zip/Archive/Nsis/NsisIn.cpp --- 7zip-24.09+dfsg/CPP/7zip/Archive/Nsis/NsisIn.cpp 2023-12-11 11:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/Nsis/NsisIn.cpp 2025-06-16 08:00:00.000000000 +0000 @@ -4005,7 +4005,7 @@ AddParam_Var(params[0]); AString temp; ReadString2(temp, params[1]); - if (temp != "$TEMP") + if (!temp.IsEqualTo("$TEMP")) SpaceQuStr(temp); break; } @@ -4410,7 +4410,7 @@ } else { - if (func == "DllUnregisterServer") + if (func.IsEqualTo("DllUnregisterServer")) { s += "UnRegDLL"; printFunc = false; @@ -4418,7 +4418,7 @@ else { s += "RegDLL"; - if (func == "DllRegisterServer") + if (func.IsEqualTo("DllRegisterServer")) printFunc = false; } AddParam(params[0]); @@ -4886,7 +4886,7 @@ AddParam_Var(params[1]); AddParam(params[2]); AddParam(params[4]); - // if (params[2] == "0") AddCommentAndString("GetWinVer"); + // if (params[2].IsEqualTo("0")) AddCommentAndString("GetWinVer"); } else s += "GetOsInfo"; diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/NtfsHandler.cpp 7zip-25.01+dfsg/CPP/7zip/Archive/NtfsHandler.cpp --- 7zip-24.09+dfsg/CPP/7zip/Archive/NtfsHandler.cpp 2024-03-28 18:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/NtfsHandler.cpp 2025-06-16 08:00:00.000000000 +0000 @@ -1907,7 +1907,7 @@ for (i = 0; i < SecurityAttrs.Size(); i++) { const CAttr &attr = SecurityAttrs[i]; - if (attr.Name == L"$SII") + if (attr.Name.IsEqualTo("$SII")) { if (attr.Type == ATTR_TYPE_INDEX_ROOT) { diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/PeHandler.cpp 7zip-25.01+dfsg/CPP/7zip/Archive/PeHandler.cpp --- 7zip-24.09+dfsg/CPP/7zip/Archive/PeHandler.cpp 2024-07-12 09:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/PeHandler.cpp 2025-06-16 07:00:00.000000000 +0000 @@ -2638,7 +2638,7 @@ { const CSection § = _sections[i]; if (IsOpt()) - if (_parseResources && sect.Name == ".rsrc") + if (_parseResources && sect.Name.IsEqualTo(".rsrc")) { // 20.01: we try to parse only first copy of .rsrc section. _parseResources = false; @@ -2727,7 +2727,7 @@ for (i = 0; i < _mixItems.Size(); i++) { const CMixItem &mixItem = _mixItems[i]; - if (mixItem.StringIndex < 0 && mixItem.ResourceIndex < 0 && _sections[mixItem.SectionIndex].Name == "_winzip_") + if (mixItem.StringIndex < 0 && mixItem.ResourceIndex < 0 && _sections[mixItem.SectionIndex].Name.IsEqualTo("_winzip_")) { _mainSubfile = (Int32)(int)i; break; diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Rar/Rar5Handler.cpp 7zip-25.01+dfsg/CPP/7zip/Archive/Rar/Rar5Handler.cpp --- 7zip-24.09+dfsg/CPP/7zip/Archive/Rar/Rar5Handler.cpp 2024-11-26 11:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/Rar/Rar5Handler.cpp 2025-06-18 07:00:00.000000000 +0000 @@ -393,6 +393,7 @@ if (!FindExtra_Link(link)) return; + bool isWindows = (HostOS == kHost_Windows); if (link.Type != linkType) { if (linkType != NLinkType::kUnixSymLink) @@ -400,8 +401,11 @@ switch ((unsigned)link.Type) { case NLinkType::kUnixSymLink: + isWindows = false; + break; case NLinkType::kWinSymLink: case NLinkType::kWinJunction: + isWindows = true; break; default: return; } @@ -409,10 +413,15 @@ AString s; s.SetFrom_CalcLen((const char *)(Extra + link.NameOffset), link.NameLen); - UString unicode; ConvertUTF8ToUnicode(s, unicode); - prop = NItemName::GetOsPath(unicode); + // rar5.0 used '\\' separator for windows symlinks and \??\ prefix for abs paths. + // rar5.1+ uses '/' separator for windows symlinks and /??/ prefix for abs paths. + // v25.00: we convert Windows slashes to Linux slashes: + if (isWindows) + unicode.Replace(L'\\', L'/'); + prop = unicode; + // prop = NItemName::GetOsPath(unicode); } bool CItem::GetAltStreamName(AString &name) const diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Rar/Rar5Handler.h 7zip-25.01+dfsg/CPP/7zip/Archive/Rar/Rar5Handler.h --- 7zip-24.09+dfsg/CPP/7zip/Archive/Rar/Rar5Handler.h 2024-03-18 14:42:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/Rar/Rar5Handler.h 2025-06-16 10:00:00.000000000 +0000 @@ -286,10 +286,10 @@ bool IsService() const { return RecordType == NHeaderType::kService; } - bool Is_STM() const { return IsService() && Name == "STM"; } - bool Is_CMT() const { return IsService() && Name == "CMT"; } - bool Is_ACL() const { return IsService() && Name == "ACL"; } - // bool Is_QO() const { return IsService() && Name == "QO"; } + bool Is_STM() const { return IsService() && Name.IsEqualTo("STM"); } + bool Is_CMT() const { return IsService() && Name.IsEqualTo("CMT"); } + bool Is_ACL() const { return IsService() && Name.IsEqualTo("ACL"); } + // bool Is_QO() const { return IsService() && Name.IsEqualTo("QO"); } int FindExtra(unsigned extraID, unsigned &recordDataSize) const; void PrintInfo(AString &s) const; diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Rar/RarHandler.cpp 7zip-25.01+dfsg/CPP/7zip/Archive/Rar/RarHandler.cpp --- 7zip-24.09+dfsg/CPP/7zip/Archive/Rar/RarHandler.cpp 2023-03-06 16:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/Rar/RarHandler.cpp 2025-06-16 08:00:00.000000000 +0000 @@ -435,13 +435,13 @@ size -= sizeof(item.Salt); p += sizeof(item.Salt); } - if (item.Name == "ACL" && size == 0) + if (item.Name.IsEqualTo("ACL") && size == 0) { item.IsAltStream = true; item.Name.Empty(); item.UnicodeName.SetFromAscii(".ACL"); } - else if (item.Name == "STM" && size != 0 && (size & 1) == 0) + else if (item.Name.IsEqualTo("STM") && size != 0 && (size & 1) == 0) { item.IsAltStream = true; item.Name.Empty(); diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/RpmHandler.cpp 7zip-25.01+dfsg/CPP/7zip/Archive/RpmHandler.cpp --- 7zip-24.09+dfsg/CPP/7zip/Archive/RpmHandler.cpp 2023-12-18 17:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/RpmHandler.cpp 2025-06-16 07:00:00.000000000 +0000 @@ -330,11 +330,11 @@ if (!_compressor.IsEmpty()) { s = _compressor; - if (_compressor == "bzip2") + if (_compressor.IsEqualTo("bzip2")) s = "bz2"; - else if (_compressor == "gzip") + else if (_compressor.IsEqualTo("gzip")) s = "gz"; - else if (_compressor == "zstd") + else if (_compressor.IsEqualTo("zstd")) s = "zst"; } else diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/VmdkHandler.cpp 7zip-25.01+dfsg/CPP/7zip/Archive/VmdkHandler.cpp --- 7zip-24.09+dfsg/CPP/7zip/Archive/VmdkHandler.cpp 2023-09-07 08:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/VmdkHandler.cpp 2025-06-16 09:00:00.000000000 +0000 @@ -202,9 +202,12 @@ // PartitionUUID // DeviceIdentifier - bool IsType_ZERO() const { return Type == "ZERO"; } - // bool IsType_FLAT() const { return Type == "FLAT"; } - bool IsType_Flat() const { return Type == "FLAT" || Type == "VMFS" || Type == "VMFSRAW"; } + bool IsType_ZERO() const { return Type.IsEqualTo("ZERO"); } + // bool IsType_FLAT() const { return Type.IsEqualTo("FLAT"); } + bool IsType_Flat() const + { return Type.IsEqualTo("FLAT") + || Type.IsEqualTo("VMFS") + || Type.IsEqualTo("VMFSRAW"); } bool Parse(const char *s); }; diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Wim/WimIn.cpp 7zip-25.01+dfsg/CPP/7zip/Archive/Wim/WimIn.cpp --- 7zip-24.09+dfsg/CPP/7zip/Archive/Wim/WimIn.cpp 2024-01-01 09:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/Wim/WimIn.cpp 2025-06-16 07:00:00.000000000 +0000 @@ -1814,7 +1814,7 @@ if (!Xml.Parse(utf)) return false; - if (Xml.Root.Name != "WIM") + if (!Xml.Root.Name.IsEqualTo("WIM")) return false; FOR_VECTOR (i, Xml.Root.SubItems) diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/XarHandler.cpp 7zip-25.01+dfsg/CPP/7zip/Archive/XarHandler.cpp --- 7zip-24.09+dfsg/CPP/7zip/Archive/XarHandler.cpp 2024-11-12 11:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/XarHandler.cpp 2025-06-16 07:00:00.000000000 +0000 @@ -266,7 +266,7 @@ bool IsCopyMethod() const { - return Method.IsEmpty() || Method == "octet-stream"; + return Method.IsEmpty() || Method.IsEqualTo("octet-stream"); } void UpdateTotalPackSize(UInt64 &totalSize) const @@ -416,7 +416,7 @@ return true; if (level >= 1024) return false; - if (item.Name == "file") + if (item.Name.IsEqualTo("file")) { CFile file(parent); parent = (int)files.Size(); @@ -435,19 +435,19 @@ { file.Type = typeItem->GetSubString(); // file.LinkFrom = typeItem->GetPropVal("link"); - if (file.Type == "directory") + if (file.Type.IsEqualTo("directory")) file.IsDir = true; else { // file.IsDir = false; /* - else if (file.Type == "file") + else if (file.Type.IsEqualTo("file")) {} - else if (file.Type == "hardlink") + else if (file.Type.IsEqualTo("hardlink")) {} else */ - if (file.Type == "symlink") + if (file.Type.IsEqualTo("symlink")) file.Is_SymLink = true; // file.IsDir = false; } @@ -489,7 +489,7 @@ if (s.IsPrefixedBy(xx)) { s.DeleteFrontal(xx.Len()); - if (s == "gzip") + if (s.IsEqualTo("gzip")) s = METHOD_NAME_ZLIB; } } @@ -692,12 +692,13 @@ file.UpdateTotalPackSize(totalPackSize); if (file.Parent == -1) { - if (file.Name == "Payload" || file.Name == "Content") + if (file.Name.IsEqualTo("Payload") || + file.Name.IsEqualTo("Content")) { _mainSubfile = (Int32)(int)i; numMainFiles++; } - else if (file.Name == "PackageInfo") + else if (file.Name.IsEqualTo("PackageInfo")) _is_pkg = true; } } @@ -1210,9 +1211,9 @@ else opRes = NExtract::NOperationResult::kUnsupportedMethod; } - else if (item.Method == METHOD_NAME_ZLIB) + else if (item.Method.IsEqualTo(METHOD_NAME_ZLIB)) coder = zlibCoder; - else if (item.Method == "bzip2") + else if (item.Method.IsEqualTo("bzip2")) coder = bzip2Coder; else opRes = NExtract::NOperationResult::kUnsupportedMethod; diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/XzHandler.cpp 7zip-25.01+dfsg/CPP/7zip/Archive/XzHandler.cpp --- 7zip-24.09+dfsg/CPP/7zip/Archive/XzHandler.cpp 2024-10-02 08:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/XzHandler.cpp 2025-07-03 11:00:00.000000000 +0000 @@ -446,7 +446,7 @@ struct CXzsCPP { CXzs p; - CXzsCPP() { Xzs_Construct(&p); } + CXzsCPP() { Xzs_CONSTRUCT(&p) } ~CXzsCPP() { Xzs_Free(&p, &g_Alloc); } }; @@ -536,6 +536,9 @@ if (res2 == SZ_ERROR_ARCHIVE) return S_FALSE; + // what codes are possible here ? + // ?? res2 == SZ_ERROR_MEM : is possible here + // ?? res2 == SZ_ERROR_UNSUPPORTED : is possible here } else if (!isIndex) { @@ -1159,6 +1162,13 @@ */ #ifndef Z7_ST + +#ifdef _WIN32 + // we don't use chunk multithreading inside lzma2 stream. + // so we don't set xzProps.lzma2Props.numThreadGroups. + if (_numThreadGroups > 1) + xzProps.numThreadGroups = _numThreadGroups; +#endif UInt32 numThreads = _numThreads; @@ -1183,6 +1193,8 @@ CMultiMethodProps::SetMethodThreadsTo_IfNotFinded(oneMethodInfo, numThreads); } + // printf("\n====== GetProcessGroupAffinity : \n"); + UInt64 cs = _numSolidBytes; if (cs != XZ_PROPS_BLOCK_SIZE_AUTO) oneMethodInfo.AddProp_BlockSize2(cs); diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Zip/ZipUpdate.cpp 7zip-25.01+dfsg/CPP/7zip/Archive/Zip/ZipUpdate.cpp --- 7zip-24.09+dfsg/CPP/7zip/Archive/Zip/ZipUpdate.cpp 2024-08-09 10:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Archive/Zip/ZipUpdate.cpp 2025-07-03 09:00:00.000000000 +0000 @@ -250,13 +250,26 @@ HRESULT CreateEvents() { - WRes wres = CompressEvent.CreateIfNotCreated_Reset(); + const WRes wres = CompressEvent.CreateIfNotCreated_Reset(); return HRESULT_FROM_WIN32(wres); } - HRESULT CreateThread() + // (group < 0) means no_group. + HRESULT CreateThread_with_group( +#ifdef _WIN32 + int group +#endif + ) { - WRes wres = Thread.Create(CoderThread, this); + // tested in win10: If thread is created by another thread, + // child thread probably uses same group as parent thread. + // So we don't need to send (group) to encoder in created thread. + const WRes wres = +#ifdef _WIN32 + group >= 0 ? + Thread.Create_With_Group(CoderThread, this, (unsigned)group) : +#endif + Thread.Create(CoderThread, this); return HRESULT_FROM_WIN32(wres); } @@ -450,8 +463,12 @@ if (ui.NewProps) { if (item.HasDescriptor()) - return E_NOTIMPL; - + { + // we know compressed / uncompressed sizes and crc. + // so we remove descriptor here + item.Flags = (UInt16)(item.Flags & ~NFileHeader::NFlags::kDescriptorUsedMask); + // return E_NOTIMPL; + } // we keep ExternalAttrib and some another properties from old archive // item.ExternalAttrib = ui.Attrib; // if we don't change Comment, we keep Comment from OldProperties @@ -1000,6 +1017,9 @@ #ifndef Z7_ST UInt32 numThreads = options._numThreads; +#ifdef _WIN32 + const UInt32 numThreadGroups = options._numThreadGroups; +#endif UInt32 numZipThreads_limit = numThreads; if (numZipThreads_limit > numFilesToCompress) @@ -1014,12 +1034,10 @@ } { + // we reduce number of threads for 32-bit to reduce memory usege to 256 MB const UInt32 kNumMaxThreads = - #ifdef _WIN32 - 64; // _WIN32 supports only 64 threads in one group. So no need for more threads here - #else - 128; - #endif + // _WIN32 (64-bit) supports only 64 threads in one group. + 8 << (sizeof(size_t) / 2); // 32 threads for 32-bit : 128 threads for 64-bit if (numThreads > kNumMaxThreads) numThreads = kNumMaxThreads; } @@ -1264,7 +1282,14 @@ threadInfo.Progress = threadInfo.ProgressSpec; threadInfo.ProgressSpec->Init(&mtCompressProgressMixer, i); threadInfo.MtSem = &mtSem; - RINOK(threadInfo.CreateThread()) + const HRESULT hres = + threadInfo.CreateThread_with_group( +#ifdef _WIN32 + (numThreadGroups > 1 && numThreads > 1) ? + (int)(i % numThreadGroups) : -1 +#endif + ); + RINOK(hres) } } diff -Nru 7zip-24.09+dfsg/CPP/7zip/Bundles/Alone/Alone.dsp 7zip-25.01+dfsg/CPP/7zip/Bundles/Alone/Alone.dsp --- 7zip-24.09+dfsg/CPP/7zip/Bundles/Alone/Alone.dsp 2024-01-29 10:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Bundles/Alone/Alone.dsp 2025-07-06 07:00:00.000000000 +0000 @@ -1148,26 +1148,6 @@ SOURCE=..\..\Compress\PpmdZip.h # End Source File # End Group -# Begin Group "RangeCoder" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=..\..\Compress\RangeCoder.h -# End Source File -# Begin Source File - -SOURCE=..\..\Compress\RangeCoderBit.h -# End Source File -# Begin Source File - -SOURCE=..\..\Compress\RangeCoderBitTree.h -# End Source File -# Begin Source File - -SOURCE=..\..\Compress\RangeCoderOpt.h -# End Source File -# End Group # Begin Group "Shrink" # PROP Default_Filter "" diff -Nru 7zip-24.09+dfsg/CPP/7zip/Bundles/Alone/makefile 7zip-25.01+dfsg/CPP/7zip/Bundles/Alone/makefile --- 7zip-24.09+dfsg/CPP/7zip/Bundles/Alone/makefile 2024-01-29 11:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Bundles/Alone/makefile 2025-07-02 07:00:00.000000000 +0000 @@ -5,6 +5,7 @@ # CONSOLE_VARIANT_FLAGS=-DZ7_PROG_VARIANT_A # ZIP_FLAGS=-DZ7_ZIP_LZFSE_DISABLE +# USE_C_SORT=1 # USE_C_AES = 1 # USE_C_SHA = 1 # USE_C_LZFINDOPT = 1 @@ -221,7 +222,6 @@ $O\Ppmd8.obj \ $O\Ppmd8Dec.obj \ $O\Ppmd8Enc.obj \ - $O\Sort.obj \ $O\SwapBytes.obj \ $O\Threads.obj \ $O\Xxh64.obj \ @@ -240,5 +240,6 @@ !include "../../LzmaDec.mak" !include "../../Sha1.mak" !include "../../Sha256.mak" +!include "../../Sort.mak" !include "../../7zip.mak" diff -Nru 7zip-24.09+dfsg/CPP/7zip/Bundles/Alone7z/makefile 7zip-25.01+dfsg/CPP/7zip/Bundles/Alone7z/makefile --- 7zip-24.09+dfsg/CPP/7zip/Bundles/Alone7z/makefile 2024-02-29 08:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Bundles/Alone7z/makefile 2025-07-01 15:00:00.000000000 +0000 @@ -148,7 +148,6 @@ $O\LzmaEnc.obj \ $O\MtCoder.obj \ $O\MtDec.obj \ - $O\Sort.obj \ $O\SwapBytes.obj \ $O\Threads.obj \ $O\Xz.obj \ @@ -164,5 +163,6 @@ !include "../../LzFindOpt.mak" !include "../../LzmaDec.mak" !include "../../Sha256.mak" +!include "../../Sort.mak" !include "../../7zip.mak" diff -Nru 7zip-24.09+dfsg/CPP/7zip/Bundles/Format7z/makefile 7zip-25.01+dfsg/CPP/7zip/Bundles/Format7z/makefile --- 7zip-24.09+dfsg/CPP/7zip/Bundles/Format7z/makefile 2024-03-20 07:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Bundles/Format7z/makefile 2025-07-01 15:00:00.000000000 +0000 @@ -135,7 +135,6 @@ $O\Ppmd7.obj \ $O\Ppmd7Dec.obj \ $O\Ppmd7Enc.obj \ - $O\Sort.obj \ $O\SwapBytes.obj \ $O\Threads.obj \ @@ -144,5 +143,6 @@ !include "../../LzFindOpt.mak" !include "../../LzmaDec.mak" !include "../../Sha256.mak" +!include "../../Sort.mak" !include "../../7zip.mak" diff -Nru 7zip-24.09+dfsg/CPP/7zip/Bundles/Format7zF/Arc.mak 7zip-25.01+dfsg/CPP/7zip/Bundles/Format7zF/Arc.mak --- 7zip-24.09+dfsg/CPP/7zip/Bundles/Format7zF/Arc.mak 2024-11-25 17:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Bundles/Format7zF/Arc.mak 2025-07-01 15:00:00.000000000 +0000 @@ -291,7 +291,6 @@ $O\Sha3.obj \ $O\Sha512.obj \ $O\Sha512Opt.obj \ - $O\Sort.obj \ $O\SwapBytes.obj \ $O\Threads.obj \ $O\Xxh64.obj \ @@ -308,3 +307,4 @@ !include "../../LzmaDec.mak" !include "../../Sha1.mak" !include "../../Sha256.mak" +!include "../../Sort.mak" diff -Nru 7zip-24.09+dfsg/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp 7zip-25.01+dfsg/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp --- 7zip-24.09+dfsg/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp 2024-03-20 07:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp 2025-06-16 08:00:00.000000000 +0000 @@ -229,7 +229,7 @@ } const FString tempDirPath = tempDir.GetPath(); - // tempDirPath = L"M:\\1\\"; // to test low disk space + // tempDirPath = "M:\\1\\"; // to test low disk space { bool isCorrupt = false; UString errorMessage; @@ -308,7 +308,7 @@ { if (appLaunched.IsEmpty()) { - appLaunched = L"setup.exe"; + appLaunched = "setup.exe"; if (!NFind::DoesFileExist_FollowLink(us2fs(appLaunched))) { if (!assumeYes) diff -Nru 7zip-24.09+dfsg/CPP/7zip/Common/InBuffer.h 7zip-25.01+dfsg/CPP/7zip/Common/InBuffer.h --- 7zip-24.09+dfsg/CPP/7zip/Common/InBuffer.h 2023-12-24 13:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Common/InBuffer.h 2025-02-20 08:00:00.000000000 +0000 @@ -97,6 +97,16 @@ size_t ReadBytesPart(Byte *buf, size_t size); size_t ReadBytes(Byte *buf, size_t size); + const Byte *Lookahead(size_t &rem) + { + rem = (size_t)(_bufLim - _buf); + if (!rem) + { + ReadBlock(); + rem = (size_t)(_bufLim - _buf); + } + return _buf; + } size_t Skip(size_t size); }; diff -Nru 7zip-24.09+dfsg/CPP/7zip/Common/MethodProps.cpp 7zip-25.01+dfsg/CPP/7zip/Common/MethodProps.cpp --- 7zip-24.09+dfsg/CPP/7zip/Common/MethodProps.cpp 2024-02-26 08:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Common/MethodProps.cpp 2025-06-28 09:00:00.000000000 +0000 @@ -324,15 +324,22 @@ HRESULT CProps::SetCoderProps(ICompressSetCoderProperties *scp, const UInt64 *dataSizeReduce) const { - return SetCoderProps_DSReduce_Aff(scp, dataSizeReduce, NULL); + return SetCoderProps_DSReduce_Aff(scp, dataSizeReduce, NULL, NULL, NULL); } HRESULT CProps::SetCoderProps_DSReduce_Aff( ICompressSetCoderProperties *scp, const UInt64 *dataSizeReduce, - const UInt64 *affinity) const -{ - CCoderProps coderProps(Props.Size() + (dataSizeReduce ? 1 : 0) + (affinity ? 1 : 0) ); + const UInt64 *affinity, + const UInt32 *affinityGroup, + const UInt64 *affinityInGroup) const +{ + CCoderProps coderProps(Props.Size() + + (dataSizeReduce ? 1 : 0) + + (affinity ? 1 : 0) + + (affinityGroup ? 1 : 0) + + (affinityInGroup ? 1 : 0) + ); FOR_VECTOR (i, Props) coderProps.AddProp(Props[i]); if (dataSizeReduce) @@ -349,6 +356,20 @@ prop.Value = *affinity; coderProps.AddProp(prop); } + if (affinityGroup) + { + CProp prop; + prop.Id = NCoderPropID::kThreadGroup; + prop.Value = *affinityGroup; + coderProps.AddProp(prop); + } + if (affinityInGroup) + { + CProp prop; + prop.Id = NCoderPropID::kAffinityInGroup; + prop.Value = *affinityInGroup; + coderProps.AddProp(prop); + } return coderProps.SetProps(scp); } @@ -409,6 +430,11 @@ { VT_UI4, "offset" }, { VT_UI4, "zhb" } /* + , { VT_UI4, "tgn" }, // kNumThreadGroups + , { VT_UI4, "tgi" }, // kThreadGroup + , { VT_UI8, "tga" }, // kAffinityInGroup + */ + /* , // { VT_UI4, "zhc" }, // { VT_UI4, "zhd" }, diff -Nru 7zip-24.09+dfsg/CPP/7zip/Common/MethodProps.h 7zip-25.01+dfsg/CPP/7zip/Common/MethodProps.h --- 7zip-24.09+dfsg/CPP/7zip/Common/MethodProps.h 2024-11-11 17:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Common/MethodProps.h 2025-06-28 09:00:00.000000000 +0000 @@ -80,7 +80,11 @@ } HRESULT SetCoderProps(ICompressSetCoderProperties *scp, const UInt64 *dataSizeReduce = NULL) const; - HRESULT SetCoderProps_DSReduce_Aff(ICompressSetCoderProperties *scp, const UInt64 *dataSizeReduce, const UInt64 *affinity) const; + HRESULT SetCoderProps_DSReduce_Aff(ICompressSetCoderProperties *scp, + const UInt64 *dataSizeReduce, + const UInt64 *affinity, + const UInt32 *affinityGroup, + const UInt64 *affinityInGroup) const; }; class CMethodProps: public CProps diff -Nru 7zip-24.09+dfsg/CPP/7zip/Common/OutBuffer.h 7zip-25.01+dfsg/CPP/7zip/Common/OutBuffer.h --- 7zip-24.09+dfsg/CPP/7zip/Common/OutBuffer.h 2023-12-24 13:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Common/OutBuffer.h 2025-02-23 14:00:00.000000000 +0000 @@ -45,6 +45,7 @@ HRESULT Flush() throw(); void FlushWithCheck(); + Z7_FORCE_INLINE void WriteByte(Byte b) { UInt32 pos = _pos; @@ -54,10 +55,34 @@ if (pos == _limitPos) FlushWithCheck(); } + void WriteBytes(const void *data, size_t size) { - for (size_t i = 0; i < size; i++) - WriteByte(((const Byte *)data)[i]); + while (size) + { + UInt32 pos = _pos; + size_t cur = (size_t)(_limitPos - pos); + if (cur >= size) + cur = size; + size -= cur; + Byte *dest = _buf + pos; + pos += (UInt32)cur; + _pos = pos; +#if 0 + memcpy(dest, data, cur); + data = (const void *)((const Byte *)data + cur); +#else + const Byte * const lim = (const Byte *)data + cur; + do + { + *dest++ = *(const Byte *)data; + data = (const void *)((const Byte *)data + 1); + } + while (data != lim); +#endif + if (pos == _limitPos) + FlushWithCheck(); + } } Byte *GetOutBuffer(size_t &avail) diff -Nru 7zip-24.09+dfsg/CPP/7zip/Compress/BZip2Const.h 7zip-25.01+dfsg/CPP/7zip/Compress/BZip2Const.h --- 7zip-24.09+dfsg/CPP/7zip/Compress/BZip2Const.h 2023-01-10 17:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Compress/BZip2Const.h 2024-12-17 11:00:00.000000000 +0000 @@ -46,7 +46,7 @@ const UInt32 kBlockSizeMax = kBlockSizeMultMax * kBlockSizeStep; const unsigned kNumSelectorsBits = 15; -const UInt32 kNumSelectorsMax = (2 + (kBlockSizeMax / kGroupSize)); +const unsigned kNumSelectorsMax = 2 + kBlockSizeMax / kGroupSize; const unsigned kRleModeRepSize = 4; diff -Nru 7zip-24.09+dfsg/CPP/7zip/Compress/BZip2Encoder.cpp 7zip-25.01+dfsg/CPP/7zip/Compress/BZip2Encoder.cpp --- 7zip-24.09+dfsg/CPP/7zip/Compress/BZip2Encoder.cpp 2023-01-30 18:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Compress/BZip2Encoder.cpp 2025-07-06 06:00:00.000000000 +0000 @@ -6,18 +6,20 @@ #include "../../../C/BwtSort.h" #include "../../../C/HuffEnc.h" -#include "BZip2Crc.h" #include "BZip2Encoder.h" -#include "Mtf8.h" namespace NCompress { namespace NBZip2 { -const unsigned kMaxHuffmanLenForEncoding = 16; // it must be < kMaxHuffmanLen = 20 - -static const UInt32 kBufferSize = (1 << 17); +#define HUFFMAN_LEN 16 +#if HUFFMAN_LEN > Z7_HUFFMAN_LEN_MAX + #error Stop_Compiling_Bad_HUFFMAN_LEN_BZip2Encoder +#endif + +static const size_t kBufferSize = 1 << 17; static const unsigned kNumHuffPasses = 4; + bool CThreadInfo::Alloc() { if (!m_BlockSorterIndex) @@ -27,11 +29,15 @@ return false; } - if (!m_Block) + if (!m_Block_Base) { - m_Block = (Byte *)::MidAlloc(kBlockSizeMax * 5 + kBlockSizeMax / 10 + (20 << 10)); - if (!m_Block) + const unsigned kPadSize = 1 << 7; // we need at least 1 byte backward padding, becuase we use (m_Block - 1) pointer; + m_Block_Base = (Byte *)::MidAlloc(kBlockSizeMax * 5 + + kBlockSizeMax / 10 + (20 << 10) + + kPadSize); + if (!m_Block_Base) return false; + m_Block = m_Block_Base + kPadSize; m_MtfArray = m_Block + kBlockSizeMax; m_TempArray = m_MtfArray + kBlockSizeMax * 2 + 2; } @@ -42,8 +48,8 @@ { ::BigFree(m_BlockSorterIndex); m_BlockSorterIndex = NULL; - ::MidFree(m_Block); - m_Block = NULL; + ::MidFree(m_Block_Base); + m_Block_Base = NULL; } #ifndef Z7_ST @@ -60,10 +66,14 @@ if (wres == 0) { wres = CanWriteEvent.Create(); if (wres == 0) { - if (Encoder->_props.Affinity != 0) - wres = Thread.Create_With_Affinity(MFThread, this, (CAffinityMask)Encoder->_props.Affinity); - else - wres = Thread.Create(MFThread, this); + wres = +#ifdef _WIN32 + Encoder->_props.NumThreadGroups > 1 ? + Thread.Create_With_Group(MFThread, this, ThreadNextGroup_GetNext(&Encoder->ThreadNextGroup), 0) : // affinity +#endif + Encoder->_props.Affinity != 0 ? + Thread.Create_With_Affinity(MFThread, this, (CAffinityMask)Encoder->_props.Affinity) : + Thread.Create(MFThread, this); }}} return HRESULT_FROM_WIN32(wres); } @@ -216,94 +226,251 @@ } #endif +struct CRleEncoder +{ + const Byte *_src; + const Byte *_srcLim; + Byte *_dest; + const Byte *_destLim; + Byte _prevByte; + unsigned _numReps; + + void Encode(); +}; + +Z7_NO_INLINE +void CRleEncoder::Encode() +{ + const Byte *src = _src; + const Byte * const srcLim = _srcLim; + Byte *dest = _dest; + const Byte * const destLim = _destLim; + Byte prev = _prevByte; + unsigned numReps = _numReps; + // (dest < destLim) + // src = srcLim; // for debug + while (dest < destLim) + { + if (src == srcLim) + break; + const Byte b = *src++; + if (b != prev) + { + if (numReps >= kRleModeRepSize) + *dest++ = (Byte)(numReps - kRleModeRepSize); + *dest++ = b; + numReps = 1; + prev = b; + /* + { // speed optimization code: + if (dest >= destLim || src == srcLim) + break; + const Byte b2 = *src++; + *dest++ = b2; + numReps += (prev == b2); + prev = b2; + } + */ + continue; + } + numReps++; + if (numReps <= kRleModeRepSize) + *dest++ = b; + else if (numReps == kRleModeRepSize + 255) + { + *dest++ = (Byte)(numReps - kRleModeRepSize); + numReps = 0; + } + } + _src = src; + _dest = dest; + _prevByte = prev; + _numReps = numReps; + // (dest <= destLim + 1) +} + + +// out: return value is blockSize: size of data filled in buffer[]: +// (returned_blockSize <= _props.BlockSizeMult * kBlockSizeStep) UInt32 CEncoder::ReadRleBlock(Byte *buffer) { + CRleEncoder rle; UInt32 i = 0; - Byte prevByte; - if (m_InStream.ReadByte(prevByte)) + if (m_InStream.ReadByte(rle._prevByte)) { NumBlocks++; - const UInt32 blockSize = _props.BlockSizeMult * kBlockSizeStep - 1; - unsigned numReps = 1; - buffer[i++] = prevByte; - while (i < blockSize) // "- 1" to support RLE - { - Byte b; - if (!m_InStream.ReadByte(b)) + const UInt32 blockSize = _props.BlockSizeMult * kBlockSizeStep - 1; // -1 for RLE + rle._destLim = buffer + blockSize; + rle._numReps = 1; + buffer[i++] = rle._prevByte; + while (i < blockSize) + { + rle._dest = buffer + i; + size_t rem; + const Byte * const ptr = m_InStream.Lookahead(rem); + if (rem == 0) break; - if (b != prevByte) - { - if (numReps >= kRleModeRepSize) - buffer[i++] = (Byte)(numReps - kRleModeRepSize); - buffer[i++] = b; - numReps = 1; - prevByte = b; - continue; - } - numReps++; - if (numReps <= kRleModeRepSize) - buffer[i++] = b; - else if (numReps == kRleModeRepSize + 255) - { - buffer[i++] = (Byte)(numReps - kRleModeRepSize); - numReps = 0; - } - } - // it's to support original BZip2 decoder - if (numReps >= kRleModeRepSize) - buffer[i++] = (Byte)(numReps - kRleModeRepSize); + rle._src = ptr; + rle._srcLim = ptr + rem; + rle.Encode(); + m_InStream.Skip((size_t)(rle._src - ptr)); + i = (UInt32)(size_t)(rle._dest - buffer); + // (i <= blockSize + 1) + } + const int n = (int)rle._numReps - (int)kRleModeRepSize; + if (n >= 0) + buffer[i++] = (Byte)n; } return i; } -void CThreadInfo::WriteBits2(UInt32 value, unsigned numBits) { m_OutStreamCurrent->WriteBits(value, numBits); } -void CThreadInfo::WriteByte2(Byte b) { WriteBits2(b, 8); } -void CThreadInfo::WriteBit2(Byte v) { WriteBits2(v, 1); } -void CThreadInfo::WriteCrc2(UInt32 v) -{ - for (unsigned i = 0; i < 4; i++) - WriteByte2(((Byte)(v >> (24 - i * 8)))); -} - -void CEncoder::WriteBits(UInt32 value, unsigned numBits) { m_OutStream.WriteBits(value, numBits); } -void CEncoder::WriteByte(Byte b) { WriteBits(b, 8); } -// void CEncoder::WriteBit(Byte v) { WriteBits(v, 1); } -void CEncoder::WriteCrc(UInt32 v) -{ - for (unsigned i = 0; i < 4; i++) - WriteByte(((Byte)(v >> (24 - i * 8)))); + + +Z7_NO_INLINE +void CThreadInfo::WriteBits2(UInt32 value, unsigned numBits) + { m_OutStreamCurrent.WriteBits(value, numBits); } +/* +Z7_NO_INLINE +void CThreadInfo::WriteByte2(unsigned b) + { m_OutStreamCurrent.WriteByte(b); } +*/ +// void CEncoder::WriteBits(UInt32 value, unsigned numBits) { m_OutStream.WriteBits(value, numBits); } +Z7_NO_INLINE +void CEncoder::WriteByte(Byte b) { m_OutStream.WriteByte(b); } + + +#define WRITE_BITS_UPDATE(value, numBits) \ +{ \ + numBits -= _bitPos; \ + const UInt32 hi = value >> numBits; \ + *_buf++ = (Byte)(_curByte | hi); \ + value -= hi << numBits; \ + _bitPos = 8; \ + _curByte = 0; \ +} + +#if HUFFMAN_LEN > 16 + +#define WRITE_BITS_HUFF(value2, numBits2) \ +{ \ + UInt32 value = value2; \ + unsigned numBits = numBits2; \ + while (numBits >= _bitPos) { \ + WRITE_BITS_UPDATE(value, numBits) \ + } \ + _bitPos -= numBits; \ + _curByte |= (value << _bitPos); \ +} + +#else // HUFFMAN_LEN <= 16 + +// numBits2 <= 16 is supported +#define WRITE_BITS_HUFF(value2, numBits2) \ +{ \ + UInt32 value = value2; \ + unsigned numBits = numBits2; \ + if (numBits >= _bitPos) \ + { \ + WRITE_BITS_UPDATE(value, numBits) \ + if (numBits >= _bitPos) \ + { \ + numBits -= _bitPos; \ + const UInt32 hi = value >> numBits; \ + *_buf++ = (Byte)hi; \ + value -= hi << numBits; \ + } \ + } \ + _bitPos -= numBits; \ + _curByte |= (value << _bitPos); \ +} + +#endif + +#define WRITE_BITS_8(value2, numBits2) \ +{ \ + UInt32 value = value2; \ + unsigned numBits = numBits2; \ + if (numBits >= _bitPos) \ + { \ + WRITE_BITS_UPDATE(value, numBits) \ + } \ + _bitPos -= numBits; \ + _curByte |= (value << _bitPos); \ +} + +#define WRITE_BIT_PRE \ + { _bitPos--; } + +#define WRITE_BIT_POST \ +{ \ + if (_bitPos == 0) \ + { \ + *_buf++ = (Byte)_curByte; \ + _curByte = 0; \ + _bitPos = 8; \ + } \ +} + +#define WRITE_BIT_0 \ +{ \ + WRITE_BIT_PRE \ + WRITE_BIT_POST \ +} + +#define WRITE_BIT_1 \ +{ \ + WRITE_BIT_PRE \ + _curByte |= 1u << _bitPos; \ + WRITE_BIT_POST \ } // blockSize > 0 void CThreadInfo::EncodeBlock(const Byte *block, UInt32 blockSize) { - WriteBit2(0); // Randomised = false - + // WriteBit2(0); // Randomised = false { - UInt32 origPtr = BlockSort(m_BlockSorterIndex, block, blockSize); + const UInt32 origPtr = BlockSort(m_BlockSorterIndex, block, blockSize); // if (m_BlockSorterIndex[origPtr] != 0) throw 1; m_BlockSorterIndex[origPtr] = blockSize; - WriteBits2(origPtr, kNumOrigBits); + WriteBits2(origPtr, kNumOrigBits + 1); // + 1 for additional high bit flag (Randomised = false) } - - CMtf8Encoder mtf; - unsigned numInUse = 0; + Byte mtfBuf[256]; + // memset(mtfBuf, 0, sizeof(mtfBuf)); // to disable MSVC warning + unsigned numInUse; { Byte inUse[256]; Byte inUse16[16]; - UInt32 i; + unsigned i; for (i = 0; i < 256; i++) inUse[i] = 0; for (i = 0; i < 16; i++) inUse16[i] = 0; - for (i = 0; i < blockSize; i++) - inUse[block[i]] = 1; + { + const Byte * cur = block; + block = block + (size_t)blockSize - 1; + if (cur != block) + { + do + { + const unsigned b0 = cur[0]; + const unsigned b1 = cur[1]; + cur += 2; + inUse[b0] = 1; + inUse[b1] = 1; + } + while (cur < block); + } + if (cur == block) + inUse[cur[0]] = 1; + block -= blockSize; // block pointer is (original_block - 1) + } + numInUse = 0; for (i = 0; i < 256; i++) if (inUse[i]) { inUse16[i >> 4] = 1; - mtf.Buf[numInUse++] = (Byte)i; + mtfBuf[numInUse++] = (Byte)i; } for (i = 0; i < 16; i++) WriteBit2(inUse16[i]); @@ -311,65 +478,88 @@ if (inUse16[i >> 4]) WriteBit2(inUse[i]); } - unsigned alphaSize = numInUse + 2; + const unsigned alphaSize = numInUse + 2; - Byte *mtfs = m_MtfArray; - UInt32 mtfArraySize = 0; UInt32 symbolCounts[kMaxAlphaSize]; { for (unsigned i = 0; i < kMaxAlphaSize; i++) symbolCounts[i] = 0; + symbolCounts[(size_t)alphaSize - 1] = 1; } + Byte *mtfs = m_MtfArray; { - UInt32 rleSize = 0; - UInt32 i = 0; const UInt32 *bsIndex = m_BlockSorterIndex; - block--; + const UInt32 *bsIndex_rle = bsIndex; + const UInt32 * const bsIndex_end = bsIndex + blockSize; + // block--; // backward fix + // block pointer is (original_block - 1) do { - unsigned pos = mtf.FindAndMove(block[bsIndex[i]]); - if (pos == 0) - rleSize++; - else + const Byte v = block[*bsIndex++]; + Byte a = mtfBuf[0]; + if (v != a) { - while (rleSize != 0) + mtfBuf[0] = v; { - rleSize--; - mtfs[mtfArraySize++] = (Byte)(rleSize & 1); - symbolCounts[rleSize & 1]++; - rleSize >>= 1; + UInt32 rleSize = (UInt32)(size_t)(bsIndex - bsIndex_rle) - 1; + bsIndex_rle = bsIndex; + while (rleSize) + { + const unsigned sym = (unsigned)(--rleSize & 1); + *mtfs++ = (Byte)sym; + symbolCounts[sym]++; + rleSize >>= 1; + } } - if (pos >= 0xFE) + unsigned pos1 = 2; // = real_pos + 1 + Byte b; + b = mtfBuf[1]; mtfBuf[1] = a; if (v != b) + { a = mtfBuf[2]; mtfBuf[2] = b; if (v == a) pos1 = 3; + else { b = mtfBuf[3]; mtfBuf[3] = a; if (v == b) pos1 = 4; + else + { + Byte *m = mtfBuf + 7; + for (;;) + { + a = m[-3]; m[-3] = b; if (v == a) { pos1 = (unsigned)(size_t)(m - (mtfBuf + 2)); break; } + b = m[-2]; m[-2] = a; if (v == b) { pos1 = (unsigned)(size_t)(m - (mtfBuf + 1)); break; } + a = m[-1]; m[-1] = b; if (v == a) { pos1 = (unsigned)(size_t)(m - (mtfBuf )); break; } + b = m[ 0]; m[ 0] = a; m += 4; if (v == b) { pos1 = (unsigned)(size_t)(m - (mtfBuf + 3)); break; } + } + }}} + symbolCounts[pos1]++; + if (pos1 >= 0xff) { - mtfs[mtfArraySize++] = 0xFF; - mtfs[mtfArraySize++] = (Byte)(pos - 0xFE); + *mtfs++ = 0xff; + // pos1 -= 0xff; + pos1++; // we need only low byte } - else - mtfs[mtfArraySize++] = (Byte)(pos + 1); - symbolCounts[(size_t)pos + 1]++; + *mtfs++ = (Byte)pos1; } } - while (++i < blockSize); + while (bsIndex < bsIndex_end); - while (rleSize != 0) + UInt32 rleSize = (UInt32)(size_t)(bsIndex - bsIndex_rle); + while (rleSize) { - rleSize--; - mtfs[mtfArraySize++] = (Byte)(rleSize & 1); - symbolCounts[rleSize & 1]++; + const unsigned sym = (unsigned)(--rleSize & 1); + *mtfs++ = (Byte)sym; + symbolCounts[sym]++; rleSize >>= 1; } - - if (alphaSize < 256) - mtfs[mtfArraySize++] = (Byte)(alphaSize - 1); - else + + unsigned d = alphaSize - 1; + if (alphaSize >= 256) { - mtfs[mtfArraySize++] = 0xFF; - mtfs[mtfArraySize++] = (Byte)(alphaSize - 256); + *mtfs++ = 0xff; + d = alphaSize; // (-256) } - symbolCounts[(size_t)alphaSize - 1]++; + *mtfs++ = (Byte)d; } + const Byte * const mtf_lim = mtfs; + UInt32 numSymbols = 0; { for (unsigned i = 0; i < kMaxAlphaSize; i++) @@ -378,34 +568,30 @@ unsigned bestNumTables = kNumTablesMin; UInt32 bestPrice = 0xFFFFFFFF; - UInt32 startPos = m_OutStreamCurrent->GetPos(); - Byte startCurByte = m_OutStreamCurrent->GetCurByte(); + const UInt32 startPos = m_OutStreamCurrent.GetPos(); + const unsigned startCurByte = m_OutStreamCurrent.GetCurByte(); for (unsigned nt = kNumTablesMin; nt <= kNumTablesMax + 1; nt++) { unsigned numTables; if (m_OptimizeNumTables) { - m_OutStreamCurrent->SetPos(startPos); - m_OutStreamCurrent->SetCurState((startPos & 7), startCurByte); - if (nt <= kNumTablesMax) - numTables = nt; - else - numTables = bestNumTables; + m_OutStreamCurrent.SetPos(startPos); + m_OutStreamCurrent.SetCurState(startPos & 7, startCurByte); + numTables = (nt <= kNumTablesMax ? nt : bestNumTables); } else { - if (numSymbols < 200) numTables = 2; - else if (numSymbols < 600) numTables = 3; + if (numSymbols < 200) numTables = 2; + else if (numSymbols < 600) numTables = 3; else if (numSymbols < 1200) numTables = 4; else if (numSymbols < 2400) numTables = 5; - else numTables = 6; + else numTables = 6; } WriteBits2(numTables, kNumTablesBits); - - UInt32 numSelectors = (numSymbols + kGroupSize - 1) / kGroupSize; - WriteBits2(numSelectors, kNumSelectorsBits); + const unsigned numSelectors = (numSymbols + kGroupSize - 1) / kGroupSize; + WriteBits2((UInt32)numSelectors, kNumSelectorsBits); { UInt32 remFreq = numSymbols; @@ -436,28 +622,23 @@ for (unsigned pass = 0; pass < kNumHuffPasses; pass++) { + memset(Freqs, 0, sizeof(Freqs[0]) * numTables); + // memset(Freqs, 0, sizeof(Freqs)); { - unsigned t = 0; - do - memset(Freqs[t], 0, sizeof(Freqs[t])); - while (++t < numTables); - } - - { - UInt32 mtfPos = 0; + mtfs = m_MtfArray; UInt32 g = 0; do { - UInt32 symbols[kGroupSize]; + unsigned symbols[kGroupSize]; unsigned i = 0; do { - UInt32 symbol = mtfs[mtfPos++]; + UInt32 symbol = *mtfs++; if (symbol >= 0xFF) - symbol += mtfs[mtfPos++]; + symbol += *mtfs++; symbols[i] = symbol; } - while (++i < kGroupSize && mtfPos < mtfArraySize); + while (++i < kGroupSize && mtfs < mtf_lim); UInt32 bestPrice2 = 0xFFFFFFFF; unsigned t = 0; @@ -482,7 +663,7 @@ freqs[symbols[j]]++; while (++j < i); } - while (mtfPos < mtfArraySize); + while (mtfs < mtf_lim); } unsigned t = 0; @@ -494,11 +675,15 @@ if (freqs[i] == 0) freqs[i] = 1; while (++i < alphaSize); - Huffman_Generate(freqs, Codes[t], Lens[t], kMaxAlphaSize, kMaxHuffmanLenForEncoding); + Huffman_Generate(freqs, Codes[t], Lens[t], kMaxAlphaSize, HUFFMAN_LEN); } while (++t < numTables); } + unsigned _bitPos; // 0 < _bitPos <= 8 : number of non-filled low bits in _curByte + unsigned _curByte; // low (_bitPos) bits are zeros + // high (8 - _bitPos) bits are filled + Byte *_buf; { Byte mtfSel[kNumTablesMax]; { @@ -507,81 +692,97 @@ mtfSel[t] = (Byte)t; while (++t < numTables); } + + _bitPos = m_OutStreamCurrent._bitPos; + _curByte = m_OutStreamCurrent._curByte; + _buf = m_OutStreamCurrent._buf; + // stream.Init_from_Global(m_OutStreamCurrent); - UInt32 i = 0; + const Byte *selectors = m_Selectors; + const Byte * const selectors_lim = selectors + numSelectors; + Byte prev = 0; // mtfSel[0]; do { - Byte sel = m_Selectors[i]; - unsigned pos; - for (pos = 0; mtfSel[pos] != sel; pos++) - WriteBit2(1); - WriteBit2(0); - for (; pos > 0; pos--) - mtfSel[pos] = mtfSel[(size_t)pos - 1]; - mtfSel[0] = sel; + const Byte sel = *selectors++; + if (prev != sel) + { + Byte *mtfSel_cur = &mtfSel[1]; + for (;;) + { + WRITE_BIT_1 + const Byte next = *mtfSel_cur; + *mtfSel_cur++ = prev; + prev = next; + if (next == sel) + break; + } + // mtfSel[0] = sel; + } + WRITE_BIT_0 } - while (++i < numSelectors); + while (selectors != selectors_lim); } - { unsigned t = 0; do { const Byte *lens = Lens[t]; - UInt32 len = lens[0]; - WriteBits2(len, kNumLevelsBits); + unsigned len = lens[0]; + WRITE_BITS_8(len, kNumLevelsBits) unsigned i = 0; do { - UInt32 level = lens[i]; + const unsigned level = lens[i]; while (len != level) { - WriteBit2(1); + WRITE_BIT_1 if (len < level) { - WriteBit2(0); len++; + WRITE_BIT_0 } else { - WriteBit2(1); len--; + WRITE_BIT_1 } } - WriteBit2(0); + WRITE_BIT_0 } while (++i < alphaSize); } while (++t < numTables); } - { - UInt32 groupSize = 0; - UInt32 groupIndex = 0; + UInt32 groupSize = 1; + const Byte *selectors = m_Selectors; const Byte *lens = NULL; const UInt32 *codes = NULL; - UInt32 mtfPos = 0; + mtfs = m_MtfArray; do { - UInt32 symbol = mtfs[mtfPos++]; + unsigned symbol = *mtfs++; if (symbol >= 0xFF) - symbol += mtfs[mtfPos++]; - if (groupSize == 0) + symbol += *mtfs++; + if (--groupSize == 0) { groupSize = kGroupSize; - unsigned t = m_Selectors[groupIndex++]; + const unsigned t = *selectors++; lens = Lens[t]; codes = Codes[t]; } - groupSize--; - m_OutStreamCurrent->WriteBits(codes[symbol], lens[symbol]); + WRITE_BITS_HUFF(codes[symbol], lens[symbol]) } - while (mtfPos < mtfArraySize); + while (mtfs < mtf_lim); } + // Restore_from_Local: + m_OutStreamCurrent._bitPos = _bitPos; + m_OutStreamCurrent._curByte = _curByte; + m_OutStreamCurrent._buf = _buf; if (!m_OptimizeNumTables) break; - UInt32 price = m_OutStreamCurrent->GetPos() - startPos; + const UInt32 price = m_OutStreamCurrent.GetPos() - startPos; if (price <= bestPrice) { if (nt == kNumTablesMax) @@ -592,6 +793,7 @@ } } + // blockSize > 0 UInt32 CThreadInfo::EncodeBlockWithHeaders(const Byte *block, UInt32 blockSize) { @@ -603,142 +805,126 @@ WriteByte2(kBlockSig5); CBZip2Crc crc; - unsigned numReps = 0; - Byte prevByte = block[0]; - UInt32 i = 0; - do + const Byte * const lim = block + blockSize; + unsigned b = *block++; + crc.UpdateByte(b); + for (;;) { - Byte b = block[i]; - if (numReps == kRleModeRepSize) - { - for (; b > 0; b--) - crc.UpdateByte(prevByte); - numReps = 0; - continue; - } - if (prevByte == b) - numReps++; - else - { - numReps = 1; - prevByte = b; - } - crc.UpdateByte(b); - } - while (++i < blockSize); - UInt32 crcRes = crc.GetDigest(); - WriteCrc2(crcRes); - EncodeBlock(block, blockSize); + const unsigned prev = b; + if (block >= lim) { break; } b = *block++; crc.UpdateByte(b); if (prev != b) continue; + if (block >= lim) { break; } b = *block++; crc.UpdateByte(b); if (prev != b) continue; + if (block >= lim) { break; } b = *block++; crc.UpdateByte(b); if (prev != b) continue; + if (block >= lim) { break; } b = *block++; if (b) do crc.UpdateByte(prev); while (--b); + if (block >= lim) { break; } b = *block++; crc.UpdateByte(b); + } + const UInt32 crcRes = crc.GetDigest(); + for (int i = 24; i >= 0; i -= 8) + WriteByte2((Byte)(crcRes >> i)); + EncodeBlock(lim - blockSize, blockSize); return crcRes; } + void CThreadInfo::EncodeBlock2(const Byte *block, UInt32 blockSize, UInt32 numPasses) { - UInt32 numCrcs = m_NumCrcs; - bool needCompare = false; + const UInt32 numCrcs = m_NumCrcs; - UInt32 startBytePos = m_OutStreamCurrent->GetBytePos(); - UInt32 startPos = m_OutStreamCurrent->GetPos(); - Byte startCurByte = m_OutStreamCurrent->GetCurByte(); - Byte endCurByte = 0; - UInt32 endPos = 0; + const UInt32 startBytePos = m_OutStreamCurrent.GetBytePos(); + const UInt32 startPos = m_OutStreamCurrent.GetPos(); + const unsigned startCurByte = m_OutStreamCurrent.GetCurByte(); + unsigned endCurByte = 0; + UInt32 endPos = 0; // 0 means no no additional passes if (numPasses > 1 && blockSize >= (1 << 10)) { - UInt32 blockSize0 = blockSize / 2; // ???? - - for (; (block[blockSize0] == block[(size_t)blockSize0 - 1] - || block[(size_t)blockSize0 - 1] == block[(size_t)blockSize0 - 2]) - && blockSize0 < blockSize; - blockSize0++); + UInt32 bs0 = blockSize / 2; + for (; bs0 < blockSize && + (block[ bs0 ] == + block[(size_t)bs0 - 1] || + block[(size_t)bs0 - 1] == + block[(size_t)bs0 - 2]); + bs0++) + {} - if (blockSize0 < blockSize) + if (bs0 < blockSize) { - EncodeBlock2(block, blockSize0, numPasses - 1); - EncodeBlock2(block + blockSize0, blockSize - blockSize0, numPasses - 1); - endPos = m_OutStreamCurrent->GetPos(); - endCurByte = m_OutStreamCurrent->GetCurByte(); - if ((endPos & 7) > 0) + EncodeBlock2(block, bs0, numPasses - 1); + EncodeBlock2(block + bs0, blockSize - bs0, numPasses - 1); + endPos = m_OutStreamCurrent.GetPos(); + endCurByte = m_OutStreamCurrent.GetCurByte(); + // we prepare next byte as identical byte to starting byte for main encoding attempt: + if (endPos & 7) WriteBits2(0, 8 - (endPos & 7)); - m_OutStreamCurrent->SetCurState((startPos & 7), startCurByte); - needCompare = true; + m_OutStreamCurrent.SetCurState((startPos & 7), startCurByte); } } - UInt32 startBytePos2 = m_OutStreamCurrent->GetBytePos(); - UInt32 startPos2 = m_OutStreamCurrent->GetPos(); - UInt32 crcVal = EncodeBlockWithHeaders(block, blockSize); - UInt32 endPos2 = m_OutStreamCurrent->GetPos(); + const UInt32 startBytePos2 = m_OutStreamCurrent.GetBytePos(); + const UInt32 startPos2 = m_OutStreamCurrent.GetPos(); + const UInt32 crcVal = EncodeBlockWithHeaders(block, blockSize); - if (needCompare) + if (endPos) { - UInt32 size2 = endPos2 - startPos2; - if (size2 < endPos - startPos) - { - UInt32 numBytes = m_OutStreamCurrent->GetBytePos() - startBytePos2; - Byte *buffer = m_OutStreamCurrent->GetStream(); - for (UInt32 i = 0; i < numBytes; i++) - buffer[startBytePos + i] = buffer[startBytePos2 + i]; - m_OutStreamCurrent->SetPos(startPos + endPos2 - startPos2); - m_NumCrcs = numCrcs; - m_CRCs[m_NumCrcs++] = crcVal; - } - else + const UInt32 size2 = m_OutStreamCurrent.GetPos() - startPos2; + if (size2 >= endPos - startPos) { - m_OutStreamCurrent->SetPos(endPos); - m_OutStreamCurrent->SetCurState((endPos & 7), endCurByte); + m_OutStreamCurrent.SetPos(endPos); + m_OutStreamCurrent.SetCurState((endPos & 7), endCurByte); + return; } + const UInt32 numBytes = m_OutStreamCurrent.GetBytePos() - startBytePos2; + Byte * const buffer = m_OutStreamCurrent.GetStream(); + memmove(buffer + startBytePos, buffer + startBytePos2, numBytes); + m_OutStreamCurrent.SetPos(startPos + size2); + // we don't call m_OutStreamCurrent.SetCurState() here because + // m_OutStreamCurrent._curByte is correct already } - else - { - m_NumCrcs = numCrcs; - m_CRCs[m_NumCrcs++] = crcVal; - } + m_CRCs[numCrcs] = crcVal; + m_NumCrcs = numCrcs + 1; } + HRESULT CThreadInfo::EncodeBlock3(UInt32 blockSize) { - CMsbfEncoderTemp outStreamTemp; + CMsbfEncoderTemp &outStreamTemp = m_OutStreamCurrent; outStreamTemp.SetStream(m_TempArray); outStreamTemp.Init(); - m_OutStreamCurrent = &outStreamTemp; - m_NumCrcs = 0; EncodeBlock2(m_Block, blockSize, Encoder->_props.NumPasses); - #ifndef Z7_ST +#ifndef Z7_ST if (Encoder->MtMode) Encoder->ThreadsInfo[m_BlockIndex].CanWriteEvent.Lock(); - #endif +#endif + for (UInt32 i = 0; i < m_NumCrcs; i++) Encoder->CombinedCrc.Update(m_CRCs[i]); - Encoder->WriteBytes(m_TempArray, outStreamTemp.GetPos(), outStreamTemp.GetCurByte()); + Encoder->WriteBytes(m_TempArray, outStreamTemp.GetPos(), outStreamTemp.GetNonFlushedByteBits()); HRESULT res = S_OK; - #ifndef Z7_ST + +#ifndef Z7_ST if (Encoder->MtMode) { UInt32 blockIndex = m_BlockIndex + 1; if (blockIndex == Encoder->NumThreads) blockIndex = 0; - if (Encoder->Progress) { const UInt64 packSize = Encoder->m_OutStream.GetProcessedSize(); res = Encoder->Progress->SetRatioInfo(&m_UnpackSize, &packSize); } - Encoder->ThreadsInfo[blockIndex].CanWriteEvent.Set(); } - #endif +#endif return res; } -void CEncoder::WriteBytes(const Byte *data, UInt32 sizeInBits, Byte lastByte) +void CEncoder::WriteBytes(const Byte *data, UInt32 sizeInBits, unsigned lastByteBits) { - UInt32 bytesSize = (sizeInBits >> 3); - for (UInt32 i = 0; i < bytesSize; i++) - m_OutStream.WriteBits(data[i], 8); - WriteBits(lastByte, (sizeInBits & 7)); + m_OutStream.WriteBytes(data, sizeInBits >> 3); + sizeInBits &= 7; + if (sizeInBits) + m_OutStream.WriteBits(lastByteBits, sizeInBits); } @@ -746,11 +932,12 @@ const UInt64 * /* inSize */, const UInt64 * /* outSize */, ICompressProgressInfo *progress) { NumBlocks = 0; - #ifndef Z7_ST +#ifndef Z7_ST Progress = progress; + ThreadNextGroup_Init(&ThreadNextGroup, _props.NumThreadGroups, 0); // startGroup RINOK(Create()) for (UInt32 t = 0; t < NumThreads; t++) - #endif +#endif { #ifndef Z7_ST CThreadInfo &ti = ThreadsInfo[t]; @@ -823,11 +1010,11 @@ { CThreadInfo &ti = #ifndef Z7_ST - ThreadsInfo[0]; + ThreadsInfo[0]; #else - ThreadsInfo; + ThreadsInfo; #endif - UInt32 blockSize = ReadRleBlock(ti.m_Block); + const UInt32 blockSize = ReadRleBlock(ti.m_Block); if (blockSize == 0) break; RINOK(ti.EncodeBlock3(blockSize)) @@ -845,8 +1032,11 @@ WriteByte(kFinSig3); WriteByte(kFinSig4); WriteByte(kFinSig5); - - WriteCrc(CombinedCrc.GetDigest()); + { + const UInt32 v = CombinedCrc.GetDigest(); + for (int i = 24; i >= 0; i -= 8) + WriteByte((Byte)(v >> i)); + } RINOK(Flush()) if (!m_InStream.WasFinished()) return E_FAIL; @@ -869,14 +1059,21 @@ for (UInt32 i = 0; i < numProps; i++) { const PROPVARIANT &prop = coderProps[i]; - PROPID propID = propIDs[i]; + const PROPID propID = propIDs[i]; if (propID == NCoderPropID::kAffinity) { - if (prop.vt == VT_UI8) - props.Affinity = prop.uhVal.QuadPart; - else + if (prop.vt != VT_UI8) + return E_INVALIDARG; + props.Affinity = prop.uhVal.QuadPart; + continue; + } + + if (propID == NCoderPropID::kNumThreadGroups) + { + if (prop.vt != VT_UI4) return E_INVALIDARG; + props.NumThreadGroups = (UInt32)prop.ulVal; continue; } @@ -884,7 +1081,7 @@ continue; if (prop.vt != VT_UI4) return E_INVALIDARG; - UInt32 v = (UInt32)prop.ulVal; + const UInt32 v = (UInt32)prop.ulVal; switch (propID) { case NCoderPropID::kNumPasses: props.NumPasses = v; break; diff -Nru 7zip-24.09+dfsg/CPP/7zip/Compress/BZip2Encoder.h 7zip-25.01+dfsg/CPP/7zip/Compress/BZip2Encoder.h --- 7zip-24.09+dfsg/CPP/7zip/Compress/BZip2Encoder.h 2023-03-28 10:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Compress/BZip2Encoder.h 2025-02-28 06:00:00.000000000 +0000 @@ -3,7 +3,6 @@ #ifndef ZIP7_INC_COMPRESS_BZIP2_ENCODER_H #define ZIP7_INC_COMPRESS_BZIP2_ENCODER_H -#include "../../Common/Defs.h" #include "../../Common/MyCom.h" #ifndef Z7_ST @@ -23,80 +22,114 @@ namespace NCompress { namespace NBZip2 { -class CMsbfEncoderTemp +const unsigned kNumPassesMax = 10; + +struct CMsbfEncoderTemp { - UInt32 _pos; - unsigned _bitPos; - Byte _curByte; + unsigned _bitPos; // 0 < _bitPos <= 8 : number of non-filled low bits in _curByte + unsigned _curByte; // low (_bitPos) bits are zeros + // high (8 - _bitPos) bits are filled Byte *_buf; -public: - void SetStream(Byte *buf) { _buf = buf; } - Byte *GetStream() const { return _buf; } + Byte *_buf_base; + void SetStream(Byte *buf) { _buf_base = _buf = buf; } + Byte *GetStream() const { return _buf_base; } void Init() { - _pos = 0; _bitPos = 8; _curByte = 0; + _buf = _buf_base; } - void Flush() - { - if (_bitPos < 8) - WriteBits(0, _bitPos); - } - + // required condition: (value >> numBits) == 0 + // numBits == 0 is allowed void WriteBits(UInt32 value, unsigned numBits) { - while (numBits > 0) + do { - unsigned numNewBits = MyMin(numBits, _bitPos); - numBits -= numNewBits; - - _curByte = (Byte)(_curByte << numNewBits); - UInt32 newBits = value >> numBits; - _curByte |= Byte(newBits); - value -= (newBits << numBits); - - _bitPos -= numNewBits; - - if (_bitPos == 0) + unsigned bp = _bitPos; + unsigned curByte = _curByte; + if (numBits < bp) { - _buf[_pos++] = _curByte; - _bitPos = 8; + bp -= numBits; + _curByte = curByte | (value << bp); + _bitPos = bp; + return; } + numBits -= bp; + const UInt32 hi = value >> numBits; + value -= (hi << numBits); + Byte *buf = _buf; + _bitPos = 8; + _curByte = 0; + *buf++ = (Byte)(curByte | hi); + _buf = buf; } + while (numBits); } - - UInt32 GetBytePos() const { return _pos ; } - UInt32 GetPos() const { return _pos * 8 + (8 - _bitPos); } - Byte GetCurByte() const { return _curByte; } + + void WriteBit(unsigned value) + { + const unsigned bp = _bitPos - 1; + const unsigned curByte = _curByte | (value << bp); + _curByte = curByte; + _bitPos = bp; + if (bp == 0) + { + *_buf++ = (Byte)curByte; + _curByte = 0; + _bitPos = 8; + } + } + + void WriteByte(unsigned b) + { + const unsigned bp = _bitPos; + const unsigned a = _curByte | (b >> (8 - bp)); + _curByte = b << bp; + Byte *buf = _buf; + *buf++ = (Byte)a; + _buf = buf; + } + + UInt32 GetBytePos() const { return (UInt32)(size_t)(_buf - _buf_base); } + UInt32 GetPos() const { return GetBytePos() * 8 + 8 - _bitPos; } + unsigned GetCurByte() const { return _curByte; } + unsigned GetNonFlushedByteBits() const { return _curByte >> _bitPos; } void SetPos(UInt32 bitPos) { - _pos = bitPos >> 3; + _buf = _buf_base + (bitPos >> 3); _bitPos = 8 - ((unsigned)bitPos & 7); } - void SetCurState(unsigned bitPos, Byte curByte) + void SetCurState(unsigned bitPos, unsigned curByte) { _bitPos = 8 - bitPos; _curByte = curByte; } }; -class CEncoder; -const unsigned kNumPassesMax = 10; +class CEncoder; class CThreadInfo { +private: + CMsbfEncoderTemp m_OutStreamCurrent; public: + CEncoder *Encoder; Byte *m_Block; private: Byte *m_MtfArray; Byte *m_TempArray; UInt32 *m_BlockSorterIndex; - CMsbfEncoderTemp *m_OutStreamCurrent; +public: + bool m_OptimizeNumTables; + UInt32 m_NumCrcs; + UInt32 m_BlockIndex; + UInt64 m_UnpackSize; + + Byte *m_Block_Base; Byte Lens[kNumTablesMax][kMaxAlphaSize]; UInt32 Freqs[kNumTablesMax][kMaxAlphaSize]; @@ -105,20 +138,16 @@ Byte m_Selectors[kNumSelectorsMax]; UInt32 m_CRCs[1 << kNumPassesMax]; - UInt32 m_NumCrcs; void WriteBits2(UInt32 value, unsigned numBits); - void WriteByte2(Byte b); - void WriteBit2(Byte v); - void WriteCrc2(UInt32 v); + void WriteByte2(unsigned b) { WriteBits2(b, 8); } + void WriteBit2(unsigned v) { m_OutStreamCurrent.WriteBit(v); } void EncodeBlock(const Byte *block, UInt32 blockSize); UInt32 EncodeBlockWithHeaders(const Byte *block, UInt32 blockSize); void EncodeBlock2(const Byte *block, UInt32 blockSize, UInt32 numPasses); public: - bool m_OptimizeNumTables; - CEncoder *Encoder; - #ifndef Z7_ST +#ifndef Z7_ST NWindows::CThread Thread; NWindows::NSynchronization::CAutoResetEvent StreamWasFinishedEvent; @@ -127,17 +156,14 @@ // it's not member of this thread. We just need one event per thread NWindows::NSynchronization::CAutoResetEvent CanWriteEvent; -private: - UInt32 m_BlockIndex; - UInt64 m_UnpackSize; public: Byte MtPad[1 << 8]; // It's pad for Multi-Threading. Must be >= Cache_Line_Size. HRESULT Create(); void FinishStream(bool needLeave); THREAD_FUNC_RET_TYPE ThreadFunc(); - #endif +#endif - CThreadInfo(): m_Block(NULL), m_BlockSorterIndex(NULL) {} + CThreadInfo(): m_BlockSorterIndex(NULL), m_Block_Base(NULL) {} ~CThreadInfo() { Free(); } bool Alloc(); void Free(); @@ -145,16 +171,19 @@ HRESULT EncodeBlock3(UInt32 blockSize); }; + struct CEncProps { UInt32 BlockSizeMult; UInt32 NumPasses; + UInt32 NumThreadGroups; UInt64 Affinity; CEncProps() { BlockSizeMult = (UInt32)(Int32)-1; NumPasses = (UInt32)(Int32)-1; + NumThreadGroups = 0; Affinity = 0; } void Normalize(int level); @@ -206,6 +235,7 @@ bool CloseThreads; bool StreamWasFinished; NWindows::NSynchronization::CManualResetEvent CanStartWaitingEvent; + CThreadNextGroup ThreadNextGroup; HRESULT Result; ICompressProgressInfo *Progress; @@ -218,12 +248,8 @@ UInt64 GetInProcessedSize() const { return m_InStream.GetProcessedSize(); } UInt32 ReadRleBlock(Byte *buf); - void WriteBytes(const Byte *data, UInt32 sizeInBits, Byte lastByte); - - void WriteBits(UInt32 value, unsigned numBits); + void WriteBytes(const Byte *data, UInt32 sizeInBits, unsigned lastByteBits); void WriteByte(Byte b); - // void WriteBit(Byte v); - void WriteCrc(UInt32 v); #ifndef Z7_ST HRESULT Create(); diff -Nru 7zip-24.09+dfsg/CPP/7zip/Compress/BitlEncoder.h 7zip-25.01+dfsg/CPP/7zip/Compress/BitlEncoder.h --- 7zip-24.09+dfsg/CPP/7zip/Compress/BitlEncoder.h 2023-01-10 17:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Compress/BitlEncoder.h 2025-03-13 13:00:00.000000000 +0000 @@ -33,6 +33,7 @@ _bitPos = 8; _curByte = 0; } + Z7_FORCE_INLINE void WriteBits(UInt32 value, unsigned numBits) { while (numBits > 0) diff -Nru 7zip-24.09+dfsg/CPP/7zip/Compress/BitmEncoder.h 7zip-25.01+dfsg/CPP/7zip/Compress/BitmEncoder.h --- 7zip-24.09+dfsg/CPP/7zip/Compress/BitmEncoder.h 2023-01-10 17:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Compress/BitmEncoder.h 2025-02-28 06:00:00.000000000 +0000 @@ -8,8 +8,9 @@ template class CBitmEncoder { - unsigned _bitPos; - Byte _curByte; + unsigned _bitPos; // 0 < _bitPos <= 8 : number of non-filled low bits in _curByte + unsigned _curByte; // low (_bitPos) bits are zeros + // high (8 - _bitPos) bits are filled TOutByte _stream; public: bool Create(UInt32 bufferSize) { return _stream.Create(bufferSize); } @@ -24,25 +25,65 @@ HRESULT Flush() { if (_bitPos < 8) - WriteBits(0, _bitPos); + { + _stream.WriteByte((Byte)_curByte); + _bitPos = 8; + _curByte = 0; + } return _stream.Flush(); } + + // required condition: (value >> numBits) == 0 + // numBits == 0 is allowed void WriteBits(UInt32 value, unsigned numBits) { - while (numBits > 0) + do { - if (numBits < _bitPos) + unsigned bp = _bitPos; + unsigned curByte = _curByte; + if (numBits < bp) { - _curByte = (Byte)(_curByte | (value << (_bitPos -= numBits))); + bp -= numBits; + _curByte = curByte | (value << bp); + _bitPos = bp; return; } - numBits -= _bitPos; - UInt32 newBits = (value >> numBits); - value -= (newBits << numBits); - _stream.WriteByte((Byte)(_curByte | newBits)); + numBits -= bp; + const UInt32 hi = (value >> numBits); + value -= (hi << numBits); + _stream.WriteByte((Byte)(curByte | hi)); _bitPos = 8; _curByte = 0; } + while (numBits); + } + void WriteByte(unsigned b) + { + const unsigned bp = _bitPos; + const unsigned a = _curByte | (b >> (8 - bp)); + _curByte = b << bp; + _stream.WriteByte((Byte)a); + } + + void WriteBytes(const Byte *data, size_t num) + { + const unsigned bp = _bitPos; +#if 1 // 1 for optional speed-optimized code branch + if (bp == 8) + { + _stream.WriteBytes(data, num); + return; + } +#endif + unsigned c = _curByte; + const unsigned bp_rev = 8 - bp; + for (size_t i = 0; i < num; i++) + { + const unsigned b = data[i]; + _stream.WriteByte((Byte)(c | (b >> bp_rev))); + c = b << bp; + } + _curByte = c; } }; diff -Nru 7zip-24.09+dfsg/CPP/7zip/Compress/DeflateDecoder.cpp 7zip-25.01+dfsg/CPP/7zip/Compress/DeflateDecoder.cpp --- 7zip-24.09+dfsg/CPP/7zip/Compress/DeflateDecoder.cpp 2024-01-01 18:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Compress/DeflateDecoder.cpp 2025-03-04 12:00:00.000000000 +0000 @@ -117,15 +117,13 @@ if (_numDistLevels > kDistTableSize32) return false; - Byte levelLevels[kLevelTableSize]; - for (unsigned i = 0; i < kLevelTableSize; i++) - { - const unsigned position = kCodeLengthAlphabetOrder[i]; - if (i < numLevelCodes) - levelLevels[position] = (Byte)ReadBits(kLevelFieldSize); - else - levelLevels[position] = 0; - } + const unsigned kLevelTableSize_aligned4 = kLevelTableSize + 1; + Byte levelLevels[kLevelTableSize_aligned4]; + memset (levelLevels, 0, sizeof(levelLevels)); + unsigned i = 0; + do + levelLevels[kCodeLengthAlphabetOrder[i++]] = (Byte)ReadBits(kLevelFieldSize); + while (i != numLevelCodes); if (m_InBitStream.ExtraBitsWereRead()) return false; diff -Nru 7zip-24.09+dfsg/CPP/7zip/Compress/DeflateEncoder.cpp 7zip-25.01+dfsg/CPP/7zip/Compress/DeflateEncoder.cpp --- 7zip-24.09+dfsg/CPP/7zip/Compress/DeflateEncoder.cpp 2024-01-01 18:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Compress/DeflateEncoder.cpp 2025-01-13 13:00:00.000000000 +0000 @@ -19,12 +19,16 @@ #define NO_INLINE #endif +#define MAX_HUF_LEN_12 12 + namespace NCompress { namespace NDeflate { namespace NEncoder { +static const unsigned k_CodeValue_Len_Is_Literal_Flag = 1u << 15; + static const unsigned kNumDivPassesMax = 10; // [0, 16); ratio/speed/ram tradeoff; use big value for better compression ratio. -static const UInt32 kNumTables = (1 << kNumDivPassesMax); +static const unsigned kNumTables = 1u << kNumDivPassesMax; static const UInt32 kFixedHuffmanCodeBlockSizeMax = (1 << 8); // [0, (1 << 32)); ratio/speed tradeoff; use big value for better compression ratio. static const UInt32 kDivideCodeBlockSizeMin = (1 << 7); // [1, (1 << 32)); ratio/speed tradeoff; use small value for better compression ratio. @@ -77,7 +81,7 @@ static CFastPosInit g_FastPosInit; -inline UInt32 GetPosSlot(UInt32 pos) +inline unsigned GetPosSlot(UInt32 pos) { /* if (pos < 0x200) @@ -162,13 +166,13 @@ // COM_TRY_BEGIN if (!m_Values) { - m_Values = (CCodeValue *)MyAlloc((kMaxUncompressedBlockSize) * sizeof(CCodeValue)); + m_Values = (CCodeValue *)MyAlloc(kMaxUncompressedBlockSize * sizeof(CCodeValue)); if (!m_Values) return E_OUTOFMEMORY; } if (!m_Tables) { - m_Tables = (CTables *)MyAlloc((kNumTables) * sizeof(CTables)); + m_Tables = (CTables *)MyAlloc(kNumTables * sizeof(CTables)); if (!m_Tables) return E_OUTOFMEMORY; } @@ -268,19 +272,21 @@ UInt32 distanceTmp[kMatchMaxLen * 2 + 3]; - const UInt32 numPairs = (UInt32)((_btMode ? + const size_t numPairs = (size_t)((_btMode ? Bt3Zip_MatchFinder_GetMatches(&_lzInWindow, distanceTmp): Hc3Zip_MatchFinder_GetMatches(&_lzInWindow, distanceTmp)) - distanceTmp); - *m_MatchDistances = (UInt16)numPairs; + UInt16 *matchDistances = m_MatchDistances; + *matchDistances++ = (UInt16)numPairs; if (numPairs != 0) { - UInt32 i; + size_t i; for (i = 0; i < numPairs; i += 2) { - m_MatchDistances[(size_t)i + 1] = (UInt16)distanceTmp[i]; - m_MatchDistances[(size_t)i + 2] = (UInt16)distanceTmp[(size_t)i + 1]; + matchDistances[0] = (UInt16)distanceTmp[i]; + matchDistances[1] = (UInt16)distanceTmp[(size_t)i + 1]; + matchDistances += 2; } UInt32 len = distanceTmp[(size_t)numPairs - 2]; if (len == m_NumFastBytes && m_NumFastBytes != m_MatchMaxLen) @@ -291,11 +297,11 @@ if (numAvail > m_MatchMaxLen) numAvail = m_MatchMaxLen; for (; len < numAvail && pby[len] == pby2[len]; len++); - m_MatchDistances[(size_t)i - 1] = (UInt16)len; + matchDistances[-2] = (UInt16)len; } } if (m_IsMultiPass) - m_Pos += numPairs + 1; + m_Pos += (UInt32)numPairs + 1; if (!m_SecondPass) m_AdditionalOffset++; } @@ -535,6 +541,7 @@ } #define WRITE_HF2(codes, lens, i) m_OutStream.WriteBits(codes[i], lens[i]) +#define WRITE_HF2_NO_INLINE(codes, lens, i) WriteBits(codes[i], lens[i]) #define WRITE_HF(i) WriteBits(codes[i], lens[i]) NO_INLINE void CCoder::LevelTableCode(const Byte *levels, unsigned numLevels, const Byte *lens, const UInt32 *codes) @@ -619,17 +626,22 @@ return price; } -static NO_INLINE UInt32 Huffman_GetPrice_Spec(const UInt32 *freqs, const Byte *lens, UInt32 num, const Byte *extraBits, UInt32 extraBase) +static NO_INLINE UInt32 Huffman_GetPrice_Spec( + const UInt32 *freqs, const Byte *lens, UInt32 num, + const Byte *extraBits, UInt32 extraBase) { - return Huffman_GetPrice(freqs, lens, num) + + return + Huffman_GetPrice(freqs, lens, num) + Huffman_GetPrice(freqs + extraBase, extraBits, num - extraBase); } NO_INLINE UInt32 CCoder::GetLzBlockPrice() const { return - Huffman_GetPrice_Spec(mainFreqs, m_NewLevels.litLenLevels, kFixedMainTableSize, m_LenDirectBits, kSymbolMatch) + - Huffman_GetPrice_Spec(distFreqs, m_NewLevels.distLevels, kDistTableSize64, kDistDirectBits, 0); + Huffman_GetPrice_Spec(mainFreqs, m_NewLevels.litLenLevels, + kFixedMainTableSize, m_LenDirectBits, kSymbolMatch) + + Huffman_GetPrice_Spec(distFreqs, m_NewLevels.distLevels, + kDistTableSize64, kDistDirectBits, 0); } NO_INLINE void CCoder::TryBlock() @@ -658,7 +670,7 @@ CCodeValue &codeValue = m_Values[m_ValueIndex++]; if (len >= kMatchMinLen) { - UInt32 newLen = len - kMatchMinLen; + const UInt32 newLen = len - kMatchMinLen; codeValue.Len = (UInt16)newLen; mainFreqs[kSymbolMatch + (size_t)g_LenSlots[newLen]]++; codeValue.Pos = (UInt16)pos; @@ -666,10 +678,10 @@ } else { - Byte b = *(Inline_MatchFinder_GetPointerToCurrentPos(&_lzInWindow) - m_AdditionalOffset); + const unsigned b = *(Inline_MatchFinder_GetPointerToCurrentPos(&_lzInWindow) - m_AdditionalOffset); mainFreqs[b]++; - codeValue.SetAsLiteral(); - codeValue.Pos = b; + codeValue.Len = k_CodeValue_Len_Is_Literal_Flag; + codeValue.Pos = (UInt16)b; } m_AdditionalOffset -= len; BlockSizeRes += len; @@ -704,16 +716,24 @@ } } +#if MAX_HUF_LEN_12 > 12 +// Huffman_ReverseBits() now supports 12-bits values only. +#error Stop_Compiling_Bad_MAX_HUF_LEN_12 +#endif static NO_INLINE void Huffman_ReverseBits(UInt32 *codes, const Byte *lens, UInt32 num) { - for (UInt32 i = 0; i < num; i++) + const Byte * const lens_lim = lens + num; + do { - UInt32 x = codes[i]; - x = ((x & 0x5555) << 1) | ((x & 0xAAAA) >> 1); - x = ((x & 0x3333) << 2) | ((x & 0xCCCC) >> 2); - x = ((x & 0x0F0F) << 4) | ((x & 0xF0F0) >> 4); - codes[i] = (((x & 0x00FF) << 8) | ((x & 0xFF00) >> 8)) >> (16 - lens[i]); + // we should change constants, if lens[*] can be larger than 12. + UInt32 x = *codes; + x = ((x & (0x555 )) << 2) + (x & (0xAAA )); + x = ((x & (0x333 << 1)) << 4) | (x & (0xCCC << 1)); + x = ((x & (0xF0F << 3)) << 8) | (x & (0x0F0 << 3)); + // we can use (x) instead of (x & (0xFF << 7)), if we support garabage data after (*lens) bits. + *codes++ = (((x & (0xFF << 7)) << 16) | x) >> (*lens ^ 31); } + while (++lens != lens_lim); } NO_INLINE void CCoder::WriteBlock() @@ -721,24 +741,28 @@ Huffman_ReverseBits(mainCodes, m_NewLevels.litLenLevels, kFixedMainTableSize); Huffman_ReverseBits(distCodes, m_NewLevels.distLevels, kDistTableSize64); - for (UInt32 i = 0; i < m_ValueIndex; i++) + CCodeValue *values = m_Values; + const CCodeValue * const values_lim = values + m_ValueIndex; + + if (values != values_lim) + do { - const CCodeValue &codeValue = m_Values[i]; - if (codeValue.IsLiteral()) - WRITE_HF2(mainCodes, m_NewLevels.litLenLevels, codeValue.Pos); + const UInt32 len = values->Len; + const UInt32 dist = values->Pos; + if (len == k_CodeValue_Len_Is_Literal_Flag) + WRITE_HF2(mainCodes, m_NewLevels.litLenLevels, dist); else { - UInt32 len = codeValue.Len; - UInt32 lenSlot = g_LenSlots[len]; + const unsigned lenSlot = g_LenSlots[len]; WRITE_HF2(mainCodes, m_NewLevels.litLenLevels, kSymbolMatch + lenSlot); m_OutStream.WriteBits(len - m_LenStart[lenSlot], m_LenDirectBits[lenSlot]); - UInt32 dist = codeValue.Pos; - UInt32 posSlot = GetPosSlot(dist); + const unsigned posSlot = GetPosSlot(dist); WRITE_HF2(distCodes, m_NewLevels.distLevels, posSlot); m_OutStream.WriteBits(dist - kDistStart[posSlot], kDistDirectBits[posSlot]); } } - WRITE_HF2(mainCodes, m_NewLevels.litLenLevels, kSymbolEndOfBlock); + while (++values != values_lim); + WRITE_HF2_NO_INLINE(mainCodes, m_NewLevels.litLenLevels, kSymbolEndOfBlock); } static UInt32 GetStorePrice(UInt32 blockSize, unsigned bitPosition) @@ -787,10 +811,10 @@ { m_Pos = posTemp; TryBlock(); - unsigned numHuffBits = - (m_ValueIndex > 18000 ? 12 : - (m_ValueIndex > 7000 ? 11 : - (m_ValueIndex > 2000 ? 10 : 9))); + const unsigned numHuffBits = + m_ValueIndex > 18000 ? MAX_HUF_LEN_12 : + m_ValueIndex > 7000 ? 11 : + m_ValueIndex > 2000 ? 10 : 9; MakeTables(numHuffBits); SetPrices(m_NewLevels); } diff -Nru 7zip-24.09+dfsg/CPP/7zip/Compress/Lzma2Encoder.cpp 7zip-25.01+dfsg/CPP/7zip/Compress/Lzma2Encoder.cpp --- 7zip-24.09+dfsg/CPP/7zip/Compress/Lzma2Encoder.cpp 2023-03-28 11:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Compress/Lzma2Encoder.cpp 2025-06-28 12:00:00.000000000 +0000 @@ -52,7 +52,15 @@ case NCoderPropID::kNumThreads: if (prop.vt != VT_UI4) return E_INVALIDARG; - lzma2Props.numTotalThreads = (int)(prop.ulVal); + lzma2Props.numTotalThreads = (int)prop.ulVal; + break; + case NCoderPropID::kNumThreadGroups: + if (prop.vt != VT_UI4) + return E_INVALIDARG; + // 16-bit value supported by Windows + if (prop.ulVal >= (1u << 16)) + return E_INVALIDARG; + lzma2Props.numThreadGroups = (unsigned)prop.ulVal; break; default: RINOK(NLzma::SetLzmaProp(propID, prop, lzma2Props.lzmaProps)) diff -Nru 7zip-24.09+dfsg/CPP/7zip/Compress/LzmaEncoder.cpp 7zip-25.01+dfsg/CPP/7zip/Compress/LzmaEncoder.cpp --- 7zip-24.09+dfsg/CPP/7zip/Compress/LzmaEncoder.cpp 2023-03-28 13:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Compress/LzmaEncoder.cpp 2024-12-19 08:00:00.000000000 +0000 @@ -101,6 +101,24 @@ return S_OK; } + if (propID == NCoderPropID::kAffinityInGroup) + { + if (prop.vt == VT_UI8) + ep.affinityInGroup = prop.uhVal.QuadPart; + else + return E_INVALIDARG; + return S_OK; + } + + if (propID == NCoderPropID::kThreadGroup) + { + if (prop.vt == VT_UI4) + ep.affinityGroup = (Int32)(UInt32)prop.ulVal; + else + return E_INVALIDARG; + return S_OK; + } + if (propID == NCoderPropID::kHashBits) { if (prop.vt == VT_UI4) diff -Nru 7zip-24.09+dfsg/CPP/7zip/Compress/Mtf8.h 7zip-25.01+dfsg/CPP/7zip/Compress/Mtf8.h --- 7zip-24.09+dfsg/CPP/7zip/Compress/Mtf8.h 2023-03-28 13:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Compress/Mtf8.h 2024-12-17 18:00:00.000000000 +0000 @@ -13,6 +13,18 @@ unsigned FindAndMove(Byte v) throw() { +#if 1 + Byte b = Buf[0]; + if (v == b) + return 0; + Buf[0] = v; + for (unsigned pos = 0;;) + { + Byte a; + a = Buf[++pos]; Buf[pos] = b; if (v == a) return pos; + b = Buf[++pos]; Buf[pos] = a; if (v == b) return pos; + } +#else size_t pos; for (pos = 0; Buf[pos] != v; pos++); const unsigned resPos = (unsigned)pos; @@ -31,6 +43,7 @@ Buf[pos] = Buf[pos - 1]; Buf[0] = v; return resPos; +#endif } }; diff -Nru 7zip-24.09+dfsg/CPP/7zip/Crypto/MyAes.cpp 7zip-25.01+dfsg/CPP/7zip/Crypto/MyAes.cpp --- 7zip-24.09+dfsg/CPP/7zip/Crypto/MyAes.cpp 2024-03-01 09:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Crypto/MyAes.cpp 2024-12-10 08:00:00.000000000 +0000 @@ -153,7 +153,26 @@ #ifndef Z7_EXTRACT_ONLY #ifdef MY_CPU_X86_OR_AMD64 - #define USE_HW_AES + + #if defined(__INTEL_COMPILER) + #if (__INTEL_COMPILER >= 1110) + #define USE_HW_AES + #if (__INTEL_COMPILER >= 1900) + #define USE_HW_VAES + #endif + #endif + #elif defined(Z7_CLANG_VERSION) && (Z7_CLANG_VERSION >= 30800) \ + || defined(Z7_GCC_VERSION) && (Z7_GCC_VERSION >= 40400) + #define USE_HW_AES + #if defined(__clang__) && (__clang_major__ >= 8) \ + || defined(__GNUC__) && (__GNUC__ >= 8) + #define USE_HW_VAES + #endif + #elif defined(_MSC_VER) + #define USE_HW_AES + #define USE_HW_VAES + #endif + #elif defined(MY_CPU_ARM_OR_ARM64) && defined(MY_CPU_LE) #if defined(__ARM_FEATURE_AES) \ @@ -186,15 +205,15 @@ #define SET_AES_FUNC_2(f2) \ if (algo == 2) if (g_Aes_SupportedFunctions_Flags & k_Aes_SupportedFunctions_HW) \ { f = f2; } - #ifdef MY_CPU_X86_OR_AMD64 + #ifdef USE_HW_VAES #define SET_AES_FUNC_23(f2, f3) \ SET_AES_FUNC_2(f2) \ if (algo == 3) if (g_Aes_SupportedFunctions_Flags & k_Aes_SupportedFunctions_HW_256) \ { f = f3; } - #else // MY_CPU_X86_OR_AMD64 + #else // USE_HW_VAES #define SET_AES_FUNC_23(f2, f3) \ SET_AES_FUNC_2(f2) - #endif // MY_CPU_X86_OR_AMD64 + #endif // USE_HW_VAES #else // USE_HW_AES #define SET_AES_FUNC_23(f2, f3) #endif // USE_HW_AES diff -Nru 7zip-24.09+dfsg/CPP/7zip/ICoder.h 7zip-25.01+dfsg/CPP/7zip/ICoder.h --- 7zip-24.09+dfsg/CPP/7zip/ICoder.h 2023-04-06 10:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/ICoder.h 2024-12-14 19:00:00.000000000 +0000 @@ -136,6 +136,9 @@ kAffinity, // VT_UI8 kBranchOffset, // VT_UI4 kHashBits, // VT_UI4 + kNumThreadGroups, // VT_UI4 + kThreadGroup, // VT_UI4 + kAffinityInGroup, // VT_UI8 /* // kHash3Bits, // VT_UI4 // kHash2Bits, // VT_UI4 diff -Nru 7zip-24.09+dfsg/CPP/7zip/Sort.mak 7zip-25.01+dfsg/CPP/7zip/Sort.mak --- 7zip-24.09+dfsg/CPP/7zip/Sort.mak 1970-01-01 00:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/Sort.mak 2025-07-02 08:00:00.000000000 +0000 @@ -0,0 +1,6 @@ +!IF defined(USE_NO_ASM) || defined(USE_C_SORT) || "$(PLATFORM)" == "ia64" || "$(PLATFORM)" == "mips" || "$(PLATFORM)" == "arm" || "$(PLATFORM)" == "arm64" +C_OBJS = $(C_OBJS) \ +!ELSE +ASM_OBJS = $(ASM_OBJS) \ +!ENDIF + $O\Sort.obj diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Agent/AgentProxy.cpp 7zip-25.01+dfsg/CPP/7zip/UI/Agent/AgentProxy.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/Agent/AgentProxy.cpp 2024-01-23 20:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/Agent/AgentProxy.cpp 2025-08-02 18:00:00.000000000 +0000 @@ -636,7 +636,7 @@ file.Name = (const wchar_t *)p; file.NameLen = 0; if (size >= sizeof(wchar_t)) - file.NameLen = size / sizeof(wchar_t) - 1; + file.NameLen = size / (unsigned)sizeof(wchar_t) - 1; } else #endif diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Client7z/makefile.gcc 7zip-25.01+dfsg/CPP/7zip/UI/Client7z/makefile.gcc --- 7zip-24.09+dfsg/CPP/7zip/UI/Client7z/makefile.gcc 2024-11-29 11:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/Client7z/makefile.gcc 2025-08-02 18:00:00.000000000 +0000 @@ -24,7 +24,6 @@ SYS_OBJS = \ $O/MyWindows.o \ - $O/TimeUtils.o \ endif @@ -53,6 +52,7 @@ $O/FileName.o \ $O/PropVariant.o \ $O/PropVariantConv.o \ + $O/TimeUtils.o \ 7ZIP_COMMON_OBJS = \ $O/FileStreams.o \ diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/ArchiveCommandLine.cpp 7zip-25.01+dfsg/CPP/7zip/UI/Common/ArchiveCommandLine.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/Common/ArchiveCommandLine.cpp 2024-06-17 11:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/Common/ArchiveCommandLine.cpp 2025-08-01 16:00:00.000000000 +0000 @@ -63,17 +63,46 @@ #else -// #define MY_isatty_fileno(x) (isatty(fileno(x))) -// #define MY_IS_TERMINAL(x) (MY_isatty_fileno(x) != 0); -static inline bool MY_IS_TERMINAL(FILE *x) -{ - return ( - #if defined(_MSC_VER) && (_MSC_VER >= 1400) - _isatty(_fileno(x)) - #else - isatty(fileno(x)) - #endif - != 0); +static bool MY_IS_TERMINAL(FILE *x) +{ +#ifdef _WIN32 + /* +crt/stdio.h: +typedef struct _iobuf FILE; +#define stdin (&_iob[0]) +#define stdout (&_iob[1]) +#define stderr (&_iob[2]) +*/ + // fprintf(stderr, "\nMY_IS_TERMINAL = %p", x); + const int fd = _fileno(x); + /* (fd) is 0, 1 or 2 in console program. + docs: If stdout or stderr is not associated with + an output stream (for example, in a Windows application + without a console window), the file descriptor returned is -2. + In previous versions, the file descriptor returned was -1. + */ + if (fd < 0) // is not associated with an output stream application (without a console window) + return false; + // fprintf(stderr, "\n\nstderr _fileno(%p) = %d", x, fd); + if (!_isatty(fd)) + return false; + // fprintf(stderr, "\nisatty_val = true"); + const HANDLE h = (HANDLE)_get_osfhandle(fd); + /* _get_osfhandle() returns intptr_t in new SDK, or long in MSVC6. + Also it can return (INVALID_HANDLE_VALUE). + docs: _get_osfhandle also returns the special value -2 when + the file descriptor is not associated with a stream + in old msvcrt.dll: it returns (-1) for incorrect value + */ + // fprintf(stderr, "\n_get_osfhandle() = %p", (void *)h); + if (h == NULL || h == INVALID_HANDLE_VALUE) + return false; + DWORD st; + // fprintf(stderr, "\nGetConsoleMode() = %u", (unsigned)GetConsoleMode(h, &st)); + return GetConsoleMode(h, &st) != 0; +#else + return isatty(fileno(x)) != 0; +#endif } #endif @@ -312,7 +341,7 @@ { "spf", SWFRM_STRING_SINGL(0) }, { "snh", SWFRM_MINUS }, - { "snld", SWFRM_MINUS }, + { "snld", SWFRM_STRING }, { "snl", SWFRM_MINUS }, { "sni", SWFRM_SIMPLE }, @@ -1088,7 +1117,7 @@ const UString &s = parser[NKey::kLargePages].PostStrings[0]; if (s.IsEmpty()) slp = 1; - else if (s != L"-") + else if (!s.IsEqualTo("-")) { if (!StringToUInt32(s, slp)) throw CArcCmdLineException("Unsupported switch postfix for -slp", s); @@ -1338,7 +1367,7 @@ const UString &s = parser[NKey::kFullPathMode].PostStrings[0]; if (!s.IsEmpty()) { - if (s == L"2") + if (s.IsEqualTo("2")) censorPathMode = NWildcard::k_FullPath; else throw CArcCmdLineException("Unsupported -spf:", s); @@ -1400,6 +1429,7 @@ const bool isExtractGroupCommand = options.Command.IsFromExtractGroup(); const bool isExtractOrList = isExtractGroupCommand || options.Command.CommandType == NCommandType::kList; const bool isRename = options.Command.CommandType == NCommandType::kRename; + options.UpdateOptions.RenameMode = isRename; if ((isExtractOrList || isRename) && options.StdInMode) thereIsArchiveName = false; @@ -1449,14 +1479,8 @@ SetBoolPair(parser, NKey::kStoreOwnerId, options.StoreOwnerId); SetBoolPair(parser, NKey::kStoreOwnerName, options.StoreOwnerName); - - CBoolPair symLinks_AllowDangerous; - SetBoolPair(parser, NKey::kSymLinks_AllowDangerous, symLinks_AllowDangerous); - - /* bool supportSymLink = options.SymLinks.Val; - if (!options.SymLinks.Def) { if (isExtractOrList) @@ -1464,7 +1488,6 @@ else supportSymLink = false; } - #ifdef ENV_HAVE_LSTAT if (supportSymLink) global_use_lstat = 1; @@ -1473,7 +1496,6 @@ #endif */ - if (isExtractOrList) { CExtractOptionsBase &eo = options.ExtractOptions; @@ -1497,7 +1519,15 @@ if (!options.SymLinks.Def) nt.SymLinks.Val = true; - nt.SymLinks_AllowDangerous = symLinks_AllowDangerous; + if (parser[NKey::kSymLinks_AllowDangerous].ThereIs) + { + const UString &s = parser[NKey::kSymLinks_AllowDangerous].PostStrings[0]; + UInt32 v = 9; // default value for "-snld" instead of default = 5 without "-snld". + if (!s.IsEmpty()) + if (!StringToUInt32(s, v)) + throw CArcCmdLineException("Unsupported switch postfix -snld", s); + nt.SymLinks_DangerousLevel = (unsigned)v; + } nt.ReplaceColonForAltStream = parser[NKey::kReplaceColonForAltStream].ThereIs; nt.WriteToAltStreamIfColon = parser[NKey::kWriteToAltStreamIfColon].ThereIs; @@ -1516,9 +1546,9 @@ const UString &s = parser[NKey::kZoneFile].PostStrings[0]; if (!s.IsEmpty()) { - if (s == L"0") eo.ZoneMode = NExtract::NZoneIdMode::kNone; - else if (s == L"1") eo.ZoneMode = NExtract::NZoneIdMode::kAll; - else if (s == L"2") eo.ZoneMode = NExtract::NZoneIdMode::kOffice; + if (s.IsEqualTo("0")) eo.ZoneMode = NExtract::NZoneIdMode::kNone; + else if (s.IsEqualTo("1")) eo.ZoneMode = NExtract::NZoneIdMode::kAll; + else if (s.IsEqualTo("2")) eo.ZoneMode = NExtract::NZoneIdMode::kOffice; else throw CArcCmdLineException("Unsupported -snz:", s); } diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp 7zip-25.01+dfsg/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp 2024-10-11 16:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp 2025-08-02 18:42:00.000000000 +0000 @@ -6,12 +6,10 @@ #undef printf // #include -// #include "../../../../C/CpuTicks.h" #include "../../../../C/Alloc.h" #include "../../../../C/CpuArch.h" - #include "../../../Common/ComTry.h" #include "../../../Common/IntToString.h" #include "../../../Common/StringConvert.h" @@ -33,6 +31,8 @@ #include "../../Common/FilePathAutoRename.h" #include "../../Common/StreamUtils.h" +#include "../../Archive/Common/ItemNameUtils.h" + #include "../Common/ExtractingFilePath.h" #include "../Common/PropIDUtils.h" @@ -54,6 +54,23 @@ #ifdef SUPPORT_LINKS static const char * const kCantCreateHardLink = "Cannot create hard link"; static const char * const kCantCreateSymLink = "Cannot create symbolic link"; +static const char * const k_HardLink_to_SymLink_Ignored = "Hard link to symbolic link was ignored"; +static const char * const k_CantDelete_File_for_SymLink = "Cannot delete file for symbolic link creation"; +static const char * const k_CantDelete_Dir_for_SymLink = "Cannot delete directory for symbolic link creation"; +#endif + +static const unsigned k_LinkDataSize_LIMIT = 1 << 12; + +#ifdef SUPPORT_LINKS +#if WCHAR_PATH_SEPARATOR != L'/' + // we convert linux slashes to windows slashes for further processing. + // also we convert linux backslashes to BackslashReplacement character. + #define REPLACE_SLASHES_from_Linux_to_Sys(s) \ + { NArchive::NItemName::ReplaceToWinSlashes(s, true); } // useBackslashReplacement + // { s.Replace(L'/', WCHAR_PATH_SEPARATOR); } +#else + #define REPLACE_SLASHES_from_Linux_to_Sys(s) +#endif #endif #ifndef Z7_SFX @@ -217,7 +234,7 @@ if (!_arc->Ask_INode) return S_OK; - IInArchive *archive = _arc->Archive; + IInArchive * const archive = _arc->Archive; CRecordVector &hardIDs = _hardLinks.IDs; { @@ -313,13 +330,14 @@ _outFileStream.Release(); _bufPtrSeqOutStream.Release(); - #ifdef SUPPORT_LINKS +#ifdef SUPPORT_LINKS _hardLinks.Clear(); - #endif + _postLinks.Clear(); +#endif - #ifdef SUPPORT_ALT_STREAMS +#ifdef SUPPORT_ALT_STREAMS _renamedFiles.Clear(); - #endif +#endif _ntOptions = ntOptions; _wildcardCensor = wildcardCensor; @@ -442,7 +460,8 @@ } -void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath) +void CArchiveExtractCallback::CreateComplexDirectory( + const UStringVector &dirPathParts, bool isFinal, FString &fullPath) { // we use (_item.IsDir) in this function @@ -474,7 +493,7 @@ const UString &s = dirPathParts[i]; fullPath += us2fs(s); - const bool isFinalDir = (i == dirPathParts.Size() - 1 && _item.IsDir); + const bool isFinalDir = (i == dirPathParts.Size() - 1 && isFinal && _item.IsDir); if (fullPath.IsEmpty()) { @@ -535,7 +554,7 @@ s += fs2us(path); } -HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FString &path) +HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FString &path) const { UString s (message); AddPathToMessage(s, path); @@ -543,7 +562,7 @@ } -HRESULT CArchiveExtractCallback::SendMessageError_with_Error(HRESULT errorCode, const char *message, const FString &path) +HRESULT CArchiveExtractCallback::SendMessageError_with_Error(HRESULT errorCode, const char *message, const FString &path) const { UString s (message); if (errorCode != S_OK) @@ -555,13 +574,13 @@ return _extractCallback2->MessageError(s); } -HRESULT CArchiveExtractCallback::SendMessageError_with_LastError(const char *message, const FString &path) +HRESULT CArchiveExtractCallback::SendMessageError_with_LastError(const char *message, const FString &path) const { const HRESULT errorCode = GetLastError_noZero_HRESULT(); return SendMessageError_with_Error(errorCode, message, path); } -HRESULT CArchiveExtractCallback::SendMessageError2(HRESULT errorCode, const char *message, const FString &path1, const FString &path2) +HRESULT CArchiveExtractCallback::SendMessageError2(HRESULT errorCode, const char *message, const FString &path1, const FString &path2) const { UString s (message); if (errorCode != 0) @@ -574,6 +593,13 @@ return _extractCallback2->MessageError(s); } +HRESULT CArchiveExtractCallback::SendMessageError2_with_LastError( + const char *message, const FString &path1, const FString &path2) const +{ + const HRESULT errorCode = GetLastError_noZero_HRESULT(); + return SendMessageError2(errorCode, message, path1, path2); +} + #ifndef Z7_SFX Z7_CLASS_IMP_COM_1( @@ -604,38 +630,25 @@ #endif // Z7_SFX -#ifdef SUPPORT_LINKS - -static UString GetDirPrefixOf(const UString &src) -{ - UString s (src); - if (!s.IsEmpty()) - { - if (IsPathSepar(s.Back())) - s.DeleteBack(); - int pos = s.ReverseFind_PathSepar(); - s.DeleteFrom((unsigned)(pos + 1)); - } - return s; -} - -#endif // SUPPORT_LINKS - struct CLinkLevelsInfo { bool IsAbsolute; + bool ParentDirDots_after_NonParent; int LowLevel; int FinalLevel; - void Parse(const UString &path); + void Parse(const UString &path, bool isWSL); }; -void CLinkLevelsInfo::Parse(const UString &path) +void CLinkLevelsInfo::Parse(const UString &path, bool isWSL) { - IsAbsolute = NName::IsAbsolutePath(path); - + IsAbsolute = isWSL ? + IS_PATH_SEPAR(path[0]) : + NName::IsAbsolutePath(path); LowLevel = 0; FinalLevel = 0; + ParentDirDots_after_NonParent = false; + bool nonParentDir = false; UStringVector parts; SplitPathToParts(path, parts); @@ -650,32 +663,41 @@ IsAbsolute = true; continue; } - if (s == L".") + if (s.IsEqualTo(".")) continue; - if (s == L"..") + if (s.IsEqualTo("..")) { + if (IsAbsolute || nonParentDir) + ParentDirDots_after_NonParent = true; level--; if (LowLevel > level) - LowLevel = level; + LowLevel = level; } else + { + nonParentDir = true; level++; + } } FinalLevel = level; } -bool IsSafePath(const UString &path); -bool IsSafePath(const UString &path) +static bool IsSafePath(const UString &path, bool isWSL) { CLinkLevelsInfo levelsInfo; - levelsInfo.Parse(path); + levelsInfo.Parse(path, isWSL); return !levelsInfo.IsAbsolute && levelsInfo.LowLevel >= 0 && levelsInfo.FinalLevel > 0; } +bool IsSafePath(const UString &path); +bool IsSafePath(const UString &path) +{ + return IsSafePath(path, false); // isWSL +} bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include); bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include) @@ -791,159 +813,113 @@ HRESULT CArchiveExtractCallback::ReadLink() { - IInArchive *archive = _arc->Archive; + IInArchive * const archive = _arc->Archive; const UInt32 index = _index; - _link.Clear(); - + // _link.Clear(); // _link.Clear() was called already. { NCOM::CPropVariant prop; RINOK(archive->GetProperty(index, kpidHardLink, &prop)) if (prop.vt == VT_BSTR) { - _link.isHardLink = true; - // _link.isCopyLink = false; + _link.LinkType = k_LinkType_HardLink; _link.isRelative = false; // RAR5, TAR: hard links are from root folder of archive - _link.linkPath.SetFromBstr(prop.bstrVal); + _link.LinkPath.SetFromBstr(prop.bstrVal); + // 7-Zip 24-: tar handler returned original path (with linux slash in most case) + // 7-Zip 24-: rar5 handler returned path with system slash. + // 7-Zip 25+: tar/rar5 handlers return linux path in most cases. } else if (prop.vt != VT_EMPTY) return E_FAIL; } - /* { NCOM::CPropVariant prop; RINOK(archive->GetProperty(index, kpidCopyLink, &prop)); if (prop.vt == VT_BSTR) { - _link.isHardLink = false; - _link.isCopyLink = true; + _link.LinkType = k_LinkType_CopyLink; _link.isRelative = false; // RAR5: copy links are from root folder of archive - _link.linkPath.SetFromBstr(prop.bstrVal); + _link.LinkPath.SetFromBstr(prop.bstrVal); } else if (prop.vt != VT_EMPTY) return E_FAIL; } */ - { NCOM::CPropVariant prop; RINOK(archive->GetProperty(index, kpidSymLink, &prop)) if (prop.vt == VT_BSTR) { - _link.isHardLink = false; - // _link.isCopyLink = false; - _link.isRelative = true; // RAR5, TAR: symbolic links can be relative - _link.linkPath.SetFromBstr(prop.bstrVal); + _link.LinkType = k_LinkType_PureSymLink; + _link.isRelative = true; // RAR5, TAR: symbolic links are relative by default + _link.LinkPath.SetFromBstr(prop.bstrVal); + // 7-Zip 24-: (tar, cpio, xar, ext, iso) handlers returned returned original path (with linux slash in most case) + // 7-Zip 24-: rar5 handler returned path with system slash. + // 7-Zip 25+: all handlers return linux path in most cases. } else if (prop.vt != VT_EMPTY) return E_FAIL; } - NtReparse_Data = NULL; - NtReparse_Size = 0; - - if (_link.linkPath.IsEmpty() && _arc->GetRawProps) + // linux path separator in (_link.LinkPath) is expected for most cases, + // if new handler code is used, and if data in archive is correct. + // NtReparse_Data = NULL; + // NtReparse_Size = 0; + if (!_link.LinkPath.IsEmpty()) + { + REPLACE_SLASHES_from_Linux_to_Sys(_link.LinkPath) + } + else if (_arc->GetRawProps) { const void *data; - UInt32 dataSize; - UInt32 propType; - - _arc->GetRawProps->GetRawProp(_index, kpidNtReparse, &data, &dataSize, &propType); - - // if (dataSize == 1234567) // for debug: unpacking without reparse - if (dataSize != 0) + UInt32 dataSize, propType; + if (_arc->GetRawProps->GetRawProp(_index, kpidNtReparse, &data, &dataSize, &propType) == S_OK + // && dataSize == 1234567 // for debug: unpacking without reparse + && dataSize) { if (propType != NPropDataType::kRaw) return E_FAIL; - // 21.06: we need kpidNtReparse in linux for wim archives created in Windows - // #ifdef _WIN32 - - NtReparse_Data = data; - NtReparse_Size = dataSize; - - CReparseAttr reparse; - bool isOkReparse = reparse.Parse((const Byte *)data, dataSize); - if (isOkReparse) - { - _link.isHardLink = false; - // _link.isCopyLink = false; - _link.linkPath = reparse.GetPath(); - _link.isJunction = reparse.IsMountPoint(); - - if (reparse.IsSymLink_WSL()) - { - _link.isWSL = true; - _link.isRelative = reparse.IsRelative_WSL(); - } - else - _link.isRelative = reparse.IsRelative_Win(); - - // const AString s = GetAnsiString(_link.linkPath); - // printf("\n_link.linkPath: %s\n", s.Ptr()); - - #ifndef _WIN32 - _link.linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR); - #endif - } - // #endif + // NtReparse_Data = data; + // NtReparse_Size = dataSize; + // we ignore error code here, if there is failure of parsing: + _link.Parse_from_WindowsReparseData((const Byte *)data, dataSize); } } - if (_link.linkPath.IsEmpty()) + if (_link.LinkPath.IsEmpty()) return S_OK; - + // (_link.LinkPath) uses system path separator. + // windows: (_link.LinkPath) doesn't contain linux separator (slash). { - #ifdef _WIN32 - _link.linkPath.Replace(L'/', WCHAR_PATH_SEPARATOR); - #endif - - // rar5 uses "\??\" prefix for absolute links - if (_link.linkPath.IsPrefixedBy(WSTRING_PATH_SEPARATOR L"??" WSTRING_PATH_SEPARATOR)) - { - _link.isRelative = false; - _link.linkPath.DeleteFrontal(4); - } - - for (;;) - // while (NName::IsAbsolutePath(linkPath)) + // _link.LinkPath = "\\??\\r:\\1\\2"; // for debug + // rar5+ returns kpidSymLink absolute link path with "\??\" prefix. + // we normalize such prefix: + if (_link.LinkPath.IsPrefixedBy(STRING_PATH_SEPARATOR "??" STRING_PATH_SEPARATOR)) { - unsigned n = NName::GetRootPrefixSize(_link.linkPath); - if (n == 0) - break; _link.isRelative = false; - _link.linkPath.DeleteFrontal(n); - } - } - - if (_link.linkPath.IsEmpty()) - return S_OK; - - if (!_link.isRelative && _removePathParts.Size() != 0) - { - UStringVector pathParts; - SplitPathToParts(_link.linkPath, pathParts); - bool badPrefix = false; - FOR_VECTOR (i, _removePathParts) - { - if (CompareFileNames(_removePathParts[i], pathParts[i]) != 0) + // we normalize prefix from "\??\" to "\\?\": + _link.LinkPath.ReplaceOneCharAtPos(1, WCHAR_PATH_SEPARATOR); + _link.isWindowsPath = true; + if (_link.LinkPath.IsPrefixedBy_Ascii_NoCase( + STRING_PATH_SEPARATOR + STRING_PATH_SEPARATOR "?" + STRING_PATH_SEPARATOR "UNC" + STRING_PATH_SEPARATOR)) + { + // we normalize prefix from "\\?\UNC\path" to "\\path": + _link.LinkPath.DeleteFrontal(6); + _link.LinkPath.ReplaceOneCharAtPos(0, WCHAR_PATH_SEPARATOR); + } + else { - badPrefix = true; - break; + const unsigned k_prefix_Size = 4; + if (NName::IsDrivePath(_link.LinkPath.Ptr(k_prefix_Size))) + _link.LinkPath.DeleteFrontal(k_prefix_Size); } } - if (!badPrefix) - pathParts.DeleteFrontal(_removePathParts.Size()); - _link.linkPath = MakePathFromParts(pathParts); - } - - /* - if (!_link.linkPath.IsEmpty()) - { - printf("\n_link %s to -> %s\n", GetOemString(_item.Path).Ptr(), GetOemString(_link.linkPath).Ptr()); } - */ - + _link.Normalize_to_RelativeSafe(_removePathParts); return S_OK; } @@ -953,7 +929,7 @@ #ifndef _WIN32 static HRESULT GetOwner(IInArchive *archive, - UInt32 index, UInt32 pidName, UInt32 pidId, COwnerInfo &res) + UInt32 index, UInt32 pidName, UInt32 pidId, CProcessedFileInfo::COwnerInfo &res) { { NWindows::NCOM::CPropVariant prop; @@ -961,7 +937,7 @@ if (prop.vt == VT_UI4) { res.Id_Defined = true; - res.Id = prop.ulVal; // for debug + res.Id = prop.ulVal; // res.Id++; // for debug // if (pidId == kpidGroupId) res.Id += 7; // for debug // res.Id = 0; // for debug @@ -993,7 +969,7 @@ HRESULT CArchiveExtractCallback::Read_fi_Props() { - IInArchive *archive = _arc->Archive; + IInArchive * const archive = _arc->Archive; const UInt32 index = _index; _fi.Attrib_Defined = false; @@ -1085,7 +1061,7 @@ } -void CArchiveExtractCallback::GetFiTimesCAM(CFiTimesCAM &pt) +static void GetFiTimesCAM(const CProcessedFileInfo &fi, CFiTimesCAM &pt, const CArc &arc) { pt.CTime_Defined = false; pt.ATime_Defined = false; @@ -1093,27 +1069,27 @@ // if (Write_MTime) { - if (_fi.MTime.Def) + if (fi.MTime.Def) { - _fi.MTime.Write_To_FiTime(pt.MTime); + fi.MTime.Write_To_FiTime(pt.MTime); pt.MTime_Defined = true; } - else if (_arc->MTime.Def) + else if (arc.MTime.Def) { - _arc->MTime.Write_To_FiTime(pt.MTime); + arc.MTime.Write_To_FiTime(pt.MTime); pt.MTime_Defined = true; } } - if (/* Write_CTime && */ _fi.CTime.Def) + if (/* Write_CTime && */ fi.CTime.Def) { - _fi.CTime.Write_To_FiTime(pt.CTime); + fi.CTime.Write_To_FiTime(pt.CTime); pt.CTime_Defined = true; } - if (/* Write_ATime && */ _fi.ATime.Def) + if (/* Write_ATime && */ fi.ATime.Def) { - _fi.ATime.Write_To_FiTime(pt.ATime); + fi.ATime.Write_To_FiTime(pt.ATime); pt.ATime_Defined = true; } } @@ -1124,6 +1100,7 @@ // 21.04 : we don't change original (_item.PathParts) here UStringVector pathParts = _item.PathParts; + bool isFinal = true; // bool is_DirOp = false; if (!pathParts.IsEmpty()) { @@ -1133,12 +1110,15 @@ but if we create dir item here, it's not problem. */ if (!_item.IsDir #ifdef SUPPORT_LINKS - #ifndef WIN32 - || !_link.linkPath.IsEmpty() - #endif + // #ifndef WIN32 + || !_link.LinkPath.IsEmpty() + // #endif #endif ) + { pathParts.DeleteBack(); + isFinal = false; // last path part was excluded + } // else is_DirOp = true; } @@ -1162,7 +1142,7 @@ */ FString fullPathNew; - CreateComplexDirectory(pathParts, fullPathNew); + CreateComplexDirectory(pathParts, isFinal, fullPathNew); /* if (is_DirOp) @@ -1183,12 +1163,12 @@ return; CDirPathTime pt; - GetFiTimesCAM(pt); + GetFiTimesCAM(_fi, pt, *_arc); if (pt.IsSomeTimeDefined()) { pt.Path = fullPathNew; - pt.SetDirTime(); + pt.SetDirTime_to_FS_2(); _extractedFolders.Add(pt); } } @@ -1273,8 +1253,7 @@ // MyMoveFile can rename folders. So it's OK to use it for folders too if (!MyMoveFile(fullProcessedPath, existPath)) { - HRESULT errorCode = GetLastError_noZero_HRESULT(); - RINOK(SendMessageError2(errorCode, kCantRenameFile, existPath, fullProcessedPath)) + RINOK(SendMessageError2_with_LastError(kCantRenameFile, existPath, fullProcessedPath)) return E_FAIL; } } @@ -1331,9 +1310,11 @@ - - - +/* +return: + needExit = false: caller will use (outStreamLoc) and _hashStreamSpec + needExit = true : caller will not use (outStreamLoc) and _hashStreamSpec. +*/ HRESULT CArchiveExtractCallback::GetExtractStream(CMyComPtr &outStreamLoc, bool &needExit) { needExit = true; @@ -1341,7 +1322,7 @@ RINOK(Read_fi_Props()) #ifdef SUPPORT_LINKS - IInArchive *archive = _arc->Archive; + IInArchive * const archive = _arc->Archive; #endif const UInt32 index = _index; @@ -1387,7 +1368,7 @@ if (isAnti) RemoveDir(_diskFilePath); #ifdef SUPPORT_LINKS - if (_link.linkPath.IsEmpty()) + if (_link.LinkPath.IsEmpty()) #endif { if (!isAnti) @@ -1416,18 +1397,21 @@ #ifdef SUPPORT_LINKS - if (!_link.linkPath.IsEmpty()) + if (!_link.LinkPath.IsEmpty()) { #ifndef UNDER_CE { bool linkWasSet = false; - RINOK(SetFromLinkPath(fullProcessedPath, _link, linkWasSet)) + RINOK(SetLink(fullProcessedPath, _link, linkWasSet)) +/* + // we don't set attributes for placeholder. if (linkWasSet) { - _isSymLinkCreated = _link.IsSymLink(); + _isSymLinkCreated = _link.Is_AnySymLink(); SetAttrib(); // printf("\nlinkWasSet %s\n", GetAnsiString(_diskFilePath)); } +*/ } #endif // UNDER_CE @@ -1453,16 +1437,17 @@ hl = fullProcessedPath; else { - if (!MyCreateHardLink(fullProcessedPath, hl)) - { - const HRESULT errorCode = GetLastError_noZero_HRESULT(); - RINOK(SendMessageError2(errorCode, kCantCreateHardLink, fullProcessedPath, hl)) + bool link_was_Created = false; + RINOK(CreateHardLink2(fullProcessedPath, hl, link_was_Created)) + if (!link_was_Created) return S_OK; - } - // printf("\nHard linkWasSet Archive_Get_HardLinkNode %s\n", GetAnsiString(_diskFilePath)); // _needSetAttrib = true; // do we need to set attribute ? SetAttrib(); + /* if we set (needExit = false) here, _hashStreamSpec will be used, + and hash will be calulated for all hard links files (it's slower). + But "Test" operation also calculates hashes. + */ needExit = false; return S_OK; } @@ -1491,7 +1476,7 @@ bool is_SymLink_in_Data = false; - if (_curSize_Defined && _curSize > 0 && _curSize < (1 << 12)) + if (_curSize_Defined && _curSize && _curSize < k_LinkDataSize_LIMIT) { if (_fi.IsLinuxSymLink()) { @@ -1513,7 +1498,7 @@ _bufPtrSeqOutStream_Spec->Init(_outMemBuf, _outMemBuf.Size()); outStreamLoc = _bufPtrSeqOutStream; } - else // not reprase + else // not reparse { if (_ntOptions.PreAllocateOutFile && !_isSplit && _curSize_Defined && _curSize > (1 << 12)) { @@ -1568,7 +1553,7 @@ RINOK(outFileStream_Loc->Seek((Int64)_position, STREAM_SEEK_SET, NULL)) } outStreamLoc = outFileStream_Loc; - } // if not reprase + } // if not reparse _outFileStream = outFileStream_Loc; @@ -1620,8 +1605,7 @@ _fileLength_WasSet = false; _isRenamed = false; // _fi.Clear(); - _extractMode = false; - // _is_SymLink_in_Data = false; + _extractMode = false; _is_SymLink_in_Data_Linux = false; _needSetAttrib = false; _isSymLinkCreated = false; @@ -1661,7 +1645,7 @@ } - IInArchive *archive = _arc->Archive; + IInArchive * const archive = _arc->Archive; RINOK(GetItem(index)) @@ -1677,10 +1661,9 @@ } } - #ifdef SUPPORT_LINKS +#ifdef SUPPORT_LINKS RINOK(ReadLink()) - #endif // SUPPORT_LINKS - +#endif RINOK(Archive_GetItemBoolProp(archive, index, kpidEncrypted, _encrypted)) @@ -1989,7 +1972,7 @@ #endif CFiTimesCAM t; - GetFiTimesCAM(t); + GetFiTimesCAM(_fi, t, *_arc); // #ifdef _WIN32 if (t.IsSomeTimeDefined()) @@ -2016,88 +1999,275 @@ #ifdef SUPPORT_LINKS +static bool CheckLinkPath_in_FS_for_pathParts(const FString &path, const UStringVector &v) +{ + FString path2 = path; + FOR_VECTOR (i, v) + { + // if (i == v.Size() - 1) path = path2; // we don't need last part in returned path + path2 += us2fs(v[i]); + NFind::CFileInfo fi; + // printf("\nCheckLinkPath_in_FS_for_pathParts(): %s\n", GetOemString(path2).Ptr()); + if (fi.Find(path2) && fi.IsOsSymLink()) + return false; + path2.Add_PathSepar(); + } + return true; +} + +/* +link.isRelative / relative_item_PathPrefix + false / empty + true / item path without last part +*/ +static bool CheckLinkPath_in_FS( + const FString &pathPrefix_in_FS, + const CPostLink &postLink, + const UString &relative_item_PathPrefix) +{ + const CLinkInfo &link = postLink.LinkInfo; + if (postLink.item_PathParts.IsEmpty() || link.LinkPath.IsEmpty()) + return false; + FString path; + { + const UString &s = postLink.item_PathParts[0]; + if (!s.IsEmpty() && !NName::IsAbsolutePath(s)) + path = pathPrefix_in_FS; // item_PathParts is relative. So we use absolutre prefix + } + if (!CheckLinkPath_in_FS_for_pathParts(path, postLink.item_PathParts)) + return false; + path += us2fs(relative_item_PathPrefix); + UStringVector v; + SplitPathToParts(link.LinkPath, v); + // we check target paths: + return CheckLinkPath_in_FS_for_pathParts(path, v); +} + +static const unsigned k_DangLevel_MAX_for_Link_over_Link = 9; -HRESULT CArchiveExtractCallback::SetFromLinkPath( - const FString &fullProcessedPath, - const CLinkInfo &linkInfo, - bool &linkWasSet) +HRESULT CArchiveExtractCallback::CreateHardLink2( + const FString &newFilePath, const FString &existFilePath, bool &link_was_Created) const +{ + link_was_Created = false; + if (_ntOptions.SymLinks_DangerousLevel <= k_DangLevel_MAX_for_Link_over_Link) + { + NFind::CFileInfo fi; + if (fi.Find(existFilePath) && fi.IsOsSymLink()) + return SendMessageError2(0, k_HardLink_to_SymLink_Ignored, newFilePath, existFilePath); + } + if (!MyCreateHardLink(newFilePath, existFilePath)) + return SendMessageError2_with_LastError(kCantCreateHardLink, newFilePath, existFilePath); + link_was_Created = true; + return S_OK; +} + + + +HRESULT CArchiveExtractCallback::SetLink( + const FString &fullProcessedPath_from, + const CLinkInfo &link, + bool &linkWasSet) // placeholder was created { linkWasSet = false; - if (!_ntOptions.SymLinks.Val && !linkInfo.isHardLink) + if (link.LinkPath.IsEmpty()) + return S_OK; + if (!_ntOptions.SymLinks.Val && link.Is_AnySymLink()) return S_OK; + CPostLink postLink; + postLink.Index_in_Arc = _index; + postLink.item_IsDir = _item.IsDir; + postLink.item_Path = _item.Path; + postLink.item_PathParts = _item.PathParts; + postLink.item_FileInfo = _fi; + postLink.fullProcessedPath_from = fullProcessedPath_from; + postLink.LinkInfo = link; + _postLinks.Add(postLink); + + // file doesn't exist in most cases. So we don't check for error. + DeleteLinkFileAlways_or_RemoveEmptyDir(fullProcessedPath_from, false); // checkThatFileIsEmpty = false - UString relatPath; + NIO::COutFile outFile; + if (!outFile.Create_NEW(fullProcessedPath_from)) + return SendMessageError("Cannot create temporary link file", fullProcessedPath_from); +#if 0 // 1 for debug + // here we can write link path to temporary link file placeholder, + // but empty placeholder is better, because we don't want to get any non-eampty data instead of link file. + AString s; + ConvertUnicodeToUTF8(link.LinkPath, s); + outFile.WriteFull(s, s.Len()); +#endif + linkWasSet = true; + return S_OK; +} - /* if (linkInfo.isRelative) - linkInfo.linkPath is final link path that must be stored to file link field - else - linkInfo.linkPath is path from root of archive. So we must add _dirPathPrefix_Full before linkPath. - */ - - if (linkInfo.isRelative) - relatPath = GetDirPrefixOf(_item.Path); - relatPath += linkInfo.linkPath; - - if (!IsSafePath(relatPath)) + +// if file/dir is symbolic link it will remove only link itself +HRESULT CArchiveExtractCallback::DeleteLinkFileAlways_or_RemoveEmptyDir( + const FString &path, bool checkThatFileIsEmpty) const +{ + NFile::NFind::CFileInfo fi; + if (fi.Find(path)) // followLink = false { - return SendMessageError2( - 0, // errorCode - "Dangerous link path was ignored", - us2fs(_item.Path), - us2fs(linkInfo.linkPath)); // us2fs(relatPath) + if (fi.IsDir()) + { + if (RemoveDirAlways_if_Empty(path)) + return S_OK; + } + else + { + // link file placeholder must be empty + if (checkThatFileIsEmpty && !fi.IsOsSymLink() && fi.Size != 0) + return SendMessageError("Temporary link file is not empty", path); + if (DeleteFileAlways(path)) + return S_OK; + } + if (GetLastError() != ERROR_FILE_NOT_FOUND) + return SendMessageError_with_LastError( + fi.IsDir() ? + k_CantDelete_Dir_for_SymLink: + k_CantDelete_File_for_SymLink, + path); } + return S_OK; +} + - FString existPath; - if (linkInfo.isHardLink /* || linkInfo.IsCopyLink */ || !linkInfo.isRelative) +/* +in: + link.LinkPath : must be relative (non-absolute) path in any case !!! + link.isRelative / target path that must stored as created link: + == false / _dirPathPrefix_Full + link.LinkPath + == true / link.LinkPath +*/ +static HRESULT SetLink2(const CArchiveExtractCallback &callback, + const CPostLink &postLink, bool &linkWasSet) +{ + const CLinkInfo &link = postLink.LinkInfo; + const FString &fullProcessedPath_from = postLink.fullProcessedPath_from; // full file path in FS (fullProcessedPath_from) + + const unsigned level = callback._ntOptions.SymLinks_DangerousLevel; + if (level < 20) { - if (!NName::GetFullPath(_dirPathPrefix_Full, us2fs(relatPath), existPath)) + /* + We want to use additional check for links that can link to directory. + - linux: all symbolic links are files. + - windows: we can have file/directory symbolic link, + but file symbolic link works like directory link in windows. + So we use additional check for all relative links. + + We don't allow decreasing of final level of link. + So if some another extracted file will use this link, + then number of real path parts (after link redirection) cannot be + smaller than number of requested path parts from archive records. + + here we check only (link.LinkPath) without (_item.PathParts). + */ + CLinkLevelsInfo li; + li.Parse(link.LinkPath, link.Is_WSL()); + bool isDang; + UString relativePathPrefix; + if (li.IsAbsolute // unexpected + || li.ParentDirDots_after_NonParent + || (level <= 5 && link.isRelative && li.FinalLevel < 1) // final level lower + || (level <= 5 && link.isRelative && li.LowLevel < 0) // negative temporary levels + ) + isDang = true; + else // if (!isDang) { - RINOK(SendMessageError("Incorrect path", us2fs(relatPath))) - } + UString path; + if (link.isRelative) + { + // item_PathParts : parts that will be created in output folder. + // we want to get directory prefix of link item. + // so we remove file name (last non-empty part) from PathParts: + UStringVector v = postLink.item_PathParts; + while (!v.IsEmpty()) + { + const unsigned len = v.Back().Len(); + v.DeleteBack(); + if (len) + break; + } + path = MakePathFromParts(v); + NName::NormalizeDirPathPrefix(path); + relativePathPrefix = path; + } + path += link.LinkPath; + /* + path is calculated virtual target path of link + path is relative to root folder of extracted items + if (!link.isRelative), then (path == link.LinkPath) + */ + isDang = false; + if (!IsSafePath(path, link.Is_WSL())) + isDang = true; + } + const char *message = NULL; + if (isDang) + message = "Dangerous link path was ignored"; + else if (level <= k_DangLevel_MAX_for_Link_over_Link + && !CheckLinkPath_in_FS(callback._dirPathPrefix_Full, + postLink, relativePathPrefix)) + message = "Dangerous link via another link was ignored"; + if (message) + return callback.SendMessageError2(0, // errorCode + message, us2fs(postLink.item_Path), us2fs(link.LinkPath)); + } + + FString target; // target path that will be stored to link field + if (link.Is_HardLink() /* || link.IsCopyLink */ || !link.isRelative) + { + // isRelative == false + // all hard links and absolute symbolic links + // relatPath == link.LinkPath + // we get absolute link path for target: + if (!NName::GetFullPath(callback._dirPathPrefix_Full, us2fs(link.LinkPath), target)) + return callback.SendMessageError("Incorrect link path", us2fs(link.LinkPath)); + // (target) is (_dirPathPrefix_Full + relatPath) } else { - existPath = us2fs(linkInfo.linkPath); - // printf("\nlinkPath = : %s\n", GetOemString(linkInfo.linkPath).Ptr()); + // link.isRelative == true + // relative symbolic links only + target = us2fs(link.LinkPath); } - - if (existPath.IsEmpty()) - return SendMessageError("Empty link", fullProcessedPath); + if (target.IsEmpty()) + return callback.SendMessageError("Empty link", fullProcessedPath_from); - if (linkInfo.isHardLink /* || linkInfo.IsCopyLink */) + if (link.Is_HardLink() /* || link.IsCopyLink */) { - // if (linkInfo.isHardLink) + // if (link.isHardLink) { - if (!MyCreateHardLink(fullProcessedPath, existPath)) + RINOK(callback.DeleteLinkFileAlways_or_RemoveEmptyDir(fullProcessedPath_from, true)) // checkThatFileIsEmpty { - const HRESULT errorCode = GetLastError_noZero_HRESULT(); - RINOK(SendMessageError2(errorCode, kCantCreateHardLink, fullProcessedPath, existPath)) + // RINOK(SendMessageError_with_LastError(k_Cant_DeleteTempLinkFile, fullProcessedPath_from)) } + return callback.CreateHardLink2(fullProcessedPath_from, target, linkWasSet); /* RINOK(PrepareOperation(NArchive::NExtract::NAskMode::kExtract)) _op_WasReported = true; RINOK(SetOperationResult(NArchive::NExtract::NOperationResult::kOK)) - */ linkWasSet = true; return S_OK; + */ } /* // IsCopyLink { NFind::CFileInfo fi; - if (!fi.Find(existPath)) + if (!fi.Find(target)) { - RINOK(SendMessageError2("Cannot find the file for copying", existPath, fullProcessedPath)); + RINOK(SendMessageError2("Cannot find the file for copying", target, fullProcessedPath)); } else { if (_curSize_Defined && _curSize == fi.Size) - _copyFile_Path = existPath; + _copyFile_Path = target; else { - RINOK(SendMessageError2("File size collision for file copying", existPath, fullProcessedPath)); + RINOK(SendMessageError2("File size collision for file copying", target, fullProcessedPath)); } - // RINOK(MyCopyFile(existPath, fullProcessedPath)); + // RINOK(MyCopyFile(target, fullProcessedPath)); } } */ @@ -2111,127 +2281,227 @@ // Windows before Vista doesn't support symbolic links. // we could convert such symbolic links to Junction Points // isJunction = true; - // convertToAbs = true; } */ - if (!_ntOptions.SymLinks_AllowDangerous.Val) - { - #ifdef _WIN32 - if (_item.IsDir) - #endif - if (linkInfo.isRelative) - { - CLinkLevelsInfo levelsInfo; - levelsInfo.Parse(linkInfo.linkPath); - if (levelsInfo.FinalLevel < 1 || levelsInfo.IsAbsolute) - { - return SendMessageError2( - 0, // errorCode - "Dangerous symbolic link path was ignored", - us2fs(_item.Path), - us2fs(linkInfo.linkPath)); - } - } - } +#ifdef _WIN32 + const bool isDir = (postLink.item_IsDir || link.LinkType == k_LinkType_Junction); +#endif - - #ifdef _WIN32 - + +#ifdef _WIN32 CByteBuffer data; - // printf("\nFillLinkData(): %s\n", GetOemString(existPath).Ptr()); - if (!FillLinkData(data, fs2us(existPath), !linkInfo.isJunction, linkInfo.isWSL)) - return SendMessageError("Cannot fill link data", us2fs(_item.Path)); - - /* - if (NtReparse_Size != data.Size() || memcmp(NtReparse_Data, data, data.Size()) != 0) + // printf("\nFillLinkData(): %s\n", GetOemString(target).Ptr()); + if (link.Is_WSL()) { - SendMessageError("reconstructed Reparse is different", fs2us(existPath)); + Convert_WinPath_to_WslLinuxPath(target, !link.isRelative); + FillLinkData_WslLink(data, fs2us(target)); } + else + FillLinkData_WinLink(data, fs2us(target), link.LinkType != k_LinkType_Junction); + if (data.Size() == 0) + return callback.SendMessageError("Cannot fill link data", us2fs(postLink.item_Path)); + /* + if (NtReparse_Size != data.Size() || memcmp(NtReparse_Data, data, data.Size()) != 0) + SendMessageError("reconstructed Reparse is different", fs2us(target)); */ - - CReparseAttr attr; - if (!attr.Parse(data, data.Size())) { - RINOK(SendMessageError("Internal error for symbolic link file", us2fs(_item.Path))) - return S_OK; - } - if (!NFile::NIO::SetReparseData(fullProcessedPath, _item.IsDir, data, (DWORD)data.Size())) - { - RINOK(SendMessageError_with_LastError(kCantCreateSymLink, fullProcessedPath)) - return S_OK; + // we check that reparse data is correct, but we ignore attr.MinorError. + CReparseAttr attr; + if (!attr.Parse(data, data.Size())) + return callback.SendMessageError("Internal error for symbolic link file", us2fs(postLink.item_Path)); } - linkWasSet = true; - - return S_OK; - - - #else // ! _WIN32 +#endif - if (!NFile::NIO::SetSymLink(fullProcessedPath, existPath)) + RINOK(callback.DeleteLinkFileAlways_or_RemoveEmptyDir(fullProcessedPath_from, true)) // checkThatFileIsEmpty +#ifdef _WIN32 + if (!NFile::NIO::SetReparseData(fullProcessedPath_from, isDir, data, (DWORD)data.Size())) +#else // ! _WIN32 + if (!NFile::NIO::SetSymLink(fullProcessedPath_from, target)) +#endif // ! _WIN32 { - RINOK(SendMessageError_with_LastError(kCantCreateSymLink, fullProcessedPath)) - return S_OK; + return callback.SendMessageError_with_LastError(kCantCreateSymLink, fullProcessedPath_from); } linkWasSet = true; - return S_OK; - - #endif // ! _WIN32 } -bool CLinkInfo::Parse(const Byte *data, size_t dataSize, bool isLinuxData) -{ - Clear(); - // this->isLinux = isLinuxData; - - if (isLinuxData) - { - isJunction = false; - isHardLink = false; - AString utf; - if (dataSize >= (1 << 12)) - return false; - utf.SetFrom_CalcLen((const char *)data, (unsigned)dataSize); - UString u; - if (!ConvertUTF8ToUnicode(utf, u)) - return false; - linkPath = u; - - // in linux symbolic data: we expect that linux separator '/' is used - // if windows link was created, then we also must use linux separator - if (u.IsEmpty()) - return false; - const wchar_t c = u[0]; - isRelative = !IS_PATH_SEPAR(c); - return true; - } +bool CLinkInfo::Parse_from_WindowsReparseData(const Byte *data, size_t dataSize) +{ CReparseAttr reparse; if (!reparse.Parse(data, dataSize)) return false; - isHardLink = false; - // isCopyLink = false; - linkPath = reparse.GetPath(); - isJunction = reparse.IsMountPoint(); - + // const AString s = GetAnsiString(LinkPath); + // printf("\nlinkPath: %s\n", s.Ptr()); + LinkPath = reparse.GetPath(); if (reparse.IsSymLink_WSL()) { - isWSL = true; - isRelative = reparse.IsRelative_WSL(); + LinkType = k_LinkType_WSL; + isRelative = reparse.IsRelative_WSL(); // detected from LinkPath[0] + // LinkPath is original raw name converted to UString from AString + // Linux separator '/' is expected here. + REPLACE_SLASHES_from_Linux_to_Sys(LinkPath) } else - isRelative = reparse.IsRelative_Win(); - - // FIXME !!! - #ifndef _WIN32 - linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR); - #endif - + { + LinkType = reparse.IsMountPoint() ? k_LinkType_Junction : k_LinkType_PureSymLink; + isRelative = reparse.IsRelative_Win(); // detected by (Flags == Z7_WIN_SYMLINK_FLAG_RELATIVE) + isWindowsPath = true; + // LinkPath is original windows link path from raparse data with \??\ prefix removed. + // windows '\\' separator is expected here. + // linux '/' separator is not expected here. + // we translate both types of separators to system separator. + LinkPath.Replace( +#if WCHAR_PATH_SEPARATOR == L'\\' + L'/' +#else + L'\\' +#endif + , WCHAR_PATH_SEPARATOR); + } + // (LinkPath) uses system path separator. + // windows: (LinkPath) doesn't contain linux separator (slash). + return true; +} + + +bool CLinkInfo::Parse_from_LinuxData(const Byte *data, size_t dataSize) +{ + // Clear(); // *this object was cleared by constructor already. + LinkType = k_LinkType_PureSymLink; + AString utf; + if (dataSize >= k_LinkDataSize_LIMIT) + return false; + utf.SetFrom_CalcLen((const char *)data, (unsigned)dataSize); + UString u; + if (!ConvertUTF8ToUnicode(utf, u)) + return false; + if (u.IsEmpty()) + return false; + const wchar_t c = u[0]; + isRelative = (c != L'/'); + // linux path separator is expected + REPLACE_SLASHES_from_Linux_to_Sys(u) + LinkPath = u; + // (LinkPath) uses system path separator. + // windows: (LinkPath) doesn't contain linux separator (slash). return true; } + +// in/out: (LinkPath) uses system path separator +// in/out: windows: (LinkPath) doesn't contain linux separator (slash). +// out: (LinkPath) is relative path, and LinkPath[0] is not path separator +// out: isRelative changed to false, if any prefix was removed. +// note: absolute windows links "c:\" to root will be reduced to empty string: +void CLinkInfo::Remove_AbsPathPrefixes() +{ + while (!LinkPath.IsEmpty()) + { + unsigned n = 0; + if (!Is_WSL()) + { + n = +#ifndef _WIN32 + isWindowsPath ? + NName::GetRootPrefixSize_WINDOWS(LinkPath) : +#endif + NName::GetRootPrefixSize(LinkPath); +/* + // "c:path" will be ignored later as "Dangerous absolute path" + // so check is not required + if (n == 0 +#ifndef _WIN32 + && isWindowsPath +#endif + && NName::IsDrivePath2(LinkPath)) + n = 2; +*/ + } + if (n == 0) + { + if (!IS_PATH_SEPAR(LinkPath[0])) + break; + n = 1; + } + isRelative = false; // (LinkPath) will be treated as relative to root folder of archive + LinkPath.DeleteFrontal(n); + } +} + + +/* + it removes redundant separators, if there are double separators, + but it keeps double separators at start of string //name/. + in/out: system path separator is used + windows: slash character (linux separator) is not treated as separator + windows: (path) doesn't contain linux separator (slash). +*/ +static void RemoveRedundantPathSeparators(UString &path) +{ + wchar_t *dest = path.GetBuf(); + const wchar_t * const start = dest; + const wchar_t *src = dest; + for (;;) + { + wchar_t c = *src++; + if (c == 0) + break; + // if (IS_PATH_SEPAR(c)) // for Windows: we can change (/) to (\). + if (c == WCHAR_PATH_SEPARATOR) + { + if (dest - start >= 2 && dest[-1] == WCHAR_PATH_SEPARATOR) + continue; + // c = WCHAR_PATH_SEPARATOR; // for Windows: we can change (/) to (\). + } + *dest++ = c; + } + *dest = 0; + path.ReleaseBuf_SetLen((unsigned)(dest - path.Ptr())); +} + + +// in/out: (LinkPath) uses system path separator +// in/out: windows: (LinkPath) doesn't contain linux separator (slash). +// out: (LinkPath) is relative path, and LinkPath[0] is not path separator +void CLinkInfo::Normalize_to_RelativeSafe(UStringVector &removePathParts) +{ + // We WILL NOT WRITE original absolute link path from archive to filesystem. + // So here we remove all root prefixes from (LinkPath). + // If we see any absolute root prefix, then we suppose that this prefix is virtual prefix + // that shows that link is relative to root folder of archive + RemoveRedundantPathSeparators(LinkPath); + // LinkPath = "\\\\?\\r:test\\test2"; // for debug + Remove_AbsPathPrefixes(); + // (LinkPath) now is relative: + // if (isRelative == false), then (LinkPath) is relative to root folder of archive + // if (isRelative == true ), then (LinkPath) is relative to current item + if (LinkPath.IsEmpty() || isRelative || removePathParts.Size() == 0) + return; + + // if LinkPath is prefixed by _removePathParts, we remove these paths + UStringVector pathParts; + SplitPathToParts(LinkPath, pathParts); + bool badPrefix = false; + { + FOR_VECTOR (i, removePathParts) + { + if (i >= pathParts.Size() + || CompareFileNames(removePathParts[i], pathParts[i]) != 0) + { + badPrefix = true; + break; + } + } + } + if (!badPrefix) + pathParts.DeleteFrontal(removePathParts.Size()); + LinkPath = MakePathFromParts(pathParts); + Remove_AbsPathPrefixes(); +} + #endif // SUPPORT_LINKS @@ -2239,12 +2509,12 @@ { HRESULT res = S_OK; - #ifdef SUPPORT_LINKS +#ifdef SUPPORT_LINKS size_t reparseSize = 0; bool repraseMode = false; bool needSetReparse = false; - CLinkInfo linkInfo; + CLinkInfo link; if (_bufPtrSeqOutStream) { @@ -2258,15 +2528,19 @@ needSetReparse = reparse.Parse(_outMemBuf, reparseSize, errorCode); if (needSetReparse) { - UString linkPath = reparse.GetPath(); + UString LinkPath = reparse.GetPath(); #ifndef _WIN32 - linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR); + LinkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR); #endif } */ - needSetReparse = linkInfo.Parse(_outMemBuf, reparseSize, _is_SymLink_in_Data_Linux); + needSetReparse = _is_SymLink_in_Data_Linux ? + link.Parse_from_LinuxData(_outMemBuf, reparseSize) : + link.Parse_from_WindowsReparseData(_outMemBuf, reparseSize); if (!needSetReparse) res = SendMessageError_with_LastError("Incorrect reparse stream", us2fs(_item.Path)); + // (link.LinkPath) uses system path separator. + // windows: (link.LinkPath) doesn't contain linux separator (slash). } else { @@ -2281,25 +2555,21 @@ _bufPtrSeqOutStream.Release(); } - #endif // SUPPORT_LINKS - +#endif // SUPPORT_LINKS const HRESULT res2 = CloseFile(); - if (res == S_OK) res = res2; - RINOK(res) - #ifdef SUPPORT_LINKS +#ifdef SUPPORT_LINKS if (repraseMode) { _curSize = reparseSize; _curSize_Defined = true; - - #ifdef SUPPORT_LINKS if (needSetReparse) { + // empty file was created so we must delete it. // in Linux : we must delete empty file before symbolic link creation // in Windows : we can create symbolic link even without file deleting if (!DeleteFileAlways(_diskFilePath)) @@ -2307,42 +2577,57 @@ RINOK(SendMessageError_with_LastError("can't delete file", _diskFilePath)) } { - /* - // for DEBUG ONLY: we can extract sym links as WSL links - // to eliminate (non-admin) errors for sym links. - #ifdef _WIN32 - if (!linkInfo.isHardLink && !linkInfo.isJunction) - linkInfo.isWSL = true; - #endif - */ bool linkWasSet = false; - RINOK(SetFromLinkPath(_diskFilePath, linkInfo, linkWasSet)) + // link.LinkPath = "r:\\1\\2"; // for debug + // link.isJunction = true; // for debug + link.Normalize_to_RelativeSafe(_removePathParts); + RINOK(SetLink(_diskFilePath, link, linkWasSet)) +/* + // we don't set attributes for placeholder. if (linkWasSet) - _isSymLinkCreated = linkInfo.IsSymLink(); + _isSymLinkCreated = true; // link.IsSymLink(); else +*/ _needSetAttrib = false; } - /* - if (!NFile::NIO::SetReparseData(_diskFilePath, _item.IsDir, )) - { - res = SendMessageError_with_LastError(kCantCreateSymLink, _diskFilePath); - } - */ } - #endif } - #endif +#endif // SUPPORT_LINKS return res; } -void CArchiveExtractCallback::SetAttrib() +static void SetAttrib_Base(const FString &path, const CProcessedFileInfo &fi, + const CArchiveExtractCallback &callback) { - #ifndef _WIN32 +#ifndef _WIN32 + if (fi.Owner.Id_Defined && + fi.Group.Id_Defined) + { + if (my_chown(path, fi.Owner.Id, fi.Group.Id) != 0) + callback.SendMessageError_with_LastError("Cannot set owner", path); + } +#endif + + if (fi.Attrib_Defined) + { + // const AString s = GetAnsiString(_diskFilePath); + // printf("\nSetFileAttrib_PosixHighDetect: %s: hex:%x\n", s.Ptr(), _fi.Attrib); + if (!SetFileAttrib_PosixHighDetect(path, fi.Attrib)) + { + // do we need error message here in Windows and in posix? + callback.SendMessageError_with_LastError("Cannot set file attribute", path); + } + } +} + +void CArchiveExtractCallback::SetAttrib() const +{ +#ifndef _WIN32 // Linux now doesn't support permissions for symlinks if (_isSymLinkCreated) return; - #endif +#endif if (_itemFailure || _diskFilePath.IsEmpty() @@ -2350,29 +2635,39 @@ || !_extractMode) return; - #ifndef _WIN32 - if (_fi.Owner.Id_Defined && - _fi.Group.Id_Defined) - { - if (my_chown(_diskFilePath, _fi.Owner.Id, _fi.Group.Id) != 0) - { - SendMessageError_with_LastError("Cannot set owner", _diskFilePath); - } - } - #endif + SetAttrib_Base(_diskFilePath, _fi, *this); +} + - if (_fi.Attrib_Defined) +#ifdef Z7_USE_SECURITY_CODE +HRESULT CArchiveExtractCallback::SetSecurityInfo(UInt32 indexInArc, const FString &path) const +{ + if (!_stdOutMode && _extractMode && _ntOptions.NtSecurity.Val && _arc->GetRawProps) { - // const AString s = GetAnsiString(_diskFilePath); - // printf("\nSetFileAttrib_PosixHighDetect: %s: hex:%x\n", s.Ptr(), _fi.Attrib); - bool res = SetFileAttrib_PosixHighDetect(_diskFilePath, _fi.Attrib); - if (!res) + const void *data; + UInt32 dataSize; + UInt32 propType; + _arc->GetRawProps->GetRawProp(indexInArc, kpidNtSecure, &data, &dataSize, &propType); + if (dataSize != 0) { - // do we need error message here in Windows and in posix? - SendMessageError_with_LastError("Cannot set file attribute", _diskFilePath); + if (propType != NPropDataType::kRaw) + return E_FAIL; + if (CheckNtSecure((const Byte *)data, dataSize)) + { + SECURITY_INFORMATION securInfo = DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION; + if (_saclEnabled) + securInfo |= SACL_SECURITY_INFORMATION; + // if (! + ::SetFileSecurityW(fs2us(path), securInfo, (PSECURITY_DESCRIPTOR)(void *)(const Byte *)(data)); + { + // RINOK(SendMessageError_with_LastError("SetFileSecurity FAILS", path)) + } + } } } + return S_OK; } +#endif // Z7_USE_SECURITY_CODE Z7_COM7F_IMF(CArchiveExtractCallback::SetOperationResult(Int32 opRes)) @@ -2410,27 +2705,9 @@ RINOK(CloseReparseAndFile()) - #ifdef Z7_USE_SECURITY_CODE - if (!_stdOutMode && _extractMode && _ntOptions.NtSecurity.Val && _arc->GetRawProps) - { - const void *data; - UInt32 dataSize; - UInt32 propType; - _arc->GetRawProps->GetRawProp(_index, kpidNtSecure, &data, &dataSize, &propType); - if (dataSize != 0) - { - if (propType != NPropDataType::kRaw) - return E_FAIL; - if (CheckNtSecure((const Byte *)data, dataSize)) - { - SECURITY_INFORMATION securInfo = DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION; - if (_saclEnabled) - securInfo |= SACL_SECURITY_INFORMATION; - ::SetFileSecurityW(fs2us(_diskFilePath), securInfo, (PSECURITY_DESCRIPTOR)(void *)(const Byte *)(data)); - } - } - } - #endif // Z7_USE_SECURITY_CODE +#ifdef Z7_USE_SECURITY_CODE + RINOK(SetSecurityInfo(_index, _diskFilePath)) +#endif if (!_curSize_Defined) GetUnpackSize(); @@ -2674,15 +2951,58 @@ } -bool CDirPathTime::SetDirTime() const +bool CFiTimesCAM::SetDirTime_to_FS(CFSTR path) const { - return NDir::SetDirTime(Path, + // it's same function for dir and for file + return NDir::SetDirTime(path, CTime_Defined ? &CTime : NULL, ATime_Defined ? &ATime : NULL, MTime_Defined ? &MTime : NULL); } +#ifdef SUPPORT_LINKS + +bool CFiTimesCAM::SetLinkFileTime_to_FS(CFSTR path) const +{ + // it's same function for dir and for file + return NDir::SetLinkFileTime(path, + CTime_Defined ? &CTime : NULL, + ATime_Defined ? &ATime : NULL, + MTime_Defined ? &MTime : NULL); +} + +HRESULT CArchiveExtractCallback::SetPostLinks() const +{ + FOR_VECTOR (i, _postLinks) + { + const CPostLink &link = _postLinks[i]; + bool linkWasSet = false; + RINOK(SetLink2(*this, link, linkWasSet)) + if (linkWasSet) + { +#ifdef _WIN32 + // Linux now doesn't support permissions for symlinks + SetAttrib_Base(link.fullProcessedPath_from, link.item_FileInfo, *this); +#endif + + CFiTimesCAM pt; + GetFiTimesCAM(link.item_FileInfo, pt, *_arc); + if (pt.IsSomeTimeDefined()) + pt.SetLinkFileTime_to_FS(link.fullProcessedPath_from); + +#ifdef Z7_USE_SECURITY_CODE + // we set security information after timestamps setting + RINOK(SetSecurityInfo(link.Index_in_Arc, link.fullProcessedPath_from)) +#endif + } + } + return S_OK; +} + +#endif + + HRESULT CArchiveExtractCallback::SetDirsTimes() { if (!_arc) @@ -2706,7 +3026,7 @@ for (i = 0; i < pairs.Size(); i++) { const CDirPathTime &dpt = _extractedFolders[pairs[i].Index]; - if (!dpt.SetDirTime()) + if (!dpt.SetDirTime_to_FS_2()) { // result = E_FAIL; // do we need error message here in Windows and in posix? @@ -2738,10 +3058,20 @@ HRESULT CArchiveExtractCallback::CloseArc() { + // we call CloseReparseAndFile() here because we can have non-closed file in some cases? HRESULT res = CloseReparseAndFile(); - const HRESULT res2 = SetDirsTimes(); - if (res == S_OK) - res = res2; +#ifdef SUPPORT_LINKS + { + const HRESULT res2 = SetPostLinks(); + if (res == S_OK) + res = res2; + } +#endif + { + const HRESULT res2 = SetDirsTimes(); + if (res == S_OK) + res = res2; + } _arc = NULL; return res; } diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/ArchiveExtractCallback.h 7zip-25.01+dfsg/CPP/7zip/UI/Common/ArchiveExtractCallback.h --- 7zip-24.09+dfsg/CPP/7zip/UI/Common/ArchiveExtractCallback.h 2024-10-11 15:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/Common/ArchiveExtractCallback.h 2025-08-02 18:00:00.000000000 +0000 @@ -52,7 +52,6 @@ { CBoolPair NtSecurity; CBoolPair SymLinks; - CBoolPair SymLinks_AllowDangerous; CBoolPair HardLinks; CBoolPair AltStreams; bool ReplaceColonForAltStream; @@ -66,6 +65,8 @@ bool PreserveATime; bool OpenShareForWrite; + unsigned SymLinks_DangerousLevel; + UInt64 MemLimit; CExtractNtOptions(): @@ -74,10 +75,10 @@ ExtractOwner(false), PreserveATime(false), OpenShareForWrite(false), + SymLinks_DangerousLevel(5), MemLimit((UInt64)(Int64)-1) { SymLinks.Val = true; - SymLinks_AllowDangerous.Val = false; HardLinks.Val = true; AltStreams.Val = true; @@ -166,53 +167,79 @@ ATime_Defined | MTime_Defined; } + bool SetDirTime_to_FS(CFSTR path) const; +#ifdef SUPPORT_LINKS + bool SetLinkFileTime_to_FS(CFSTR path) const; +#endif }; struct CDirPathTime: public CFiTimesCAM { FString Path; - bool SetDirTime() const; + bool SetDirTime_to_FS_2() const { return SetDirTime_to_FS(Path); } }; #ifdef SUPPORT_LINKS +enum ELinkType +{ + k_LinkType_HardLink, + k_LinkType_PureSymLink, + k_LinkType_Junction, + k_LinkType_WSL + // , k_LinkType_CopyLink; +}; + + struct CLinkInfo { - // bool isCopyLink; - bool isHardLink; - bool isJunction; + ELinkType LinkType; bool isRelative; - bool isWSL; - UString linkPath; + // if (isRelative == false), then (LinkPath) is relative to root folder of archive + // if (isRelative == true ), then (LinkPath) is relative to current item + bool isWindowsPath; + UString LinkPath; + + bool Is_HardLink() const { return LinkType == k_LinkType_HardLink; } + bool Is_AnySymLink() const { return LinkType != k_LinkType_HardLink; } - bool IsSymLink() const { return !isHardLink; } + bool Is_WSL() const { return LinkType == k_LinkType_WSL; } CLinkInfo(): - // IsCopyLink(false), - isHardLink(false), - isJunction(false), + LinkType(k_LinkType_PureSymLink), isRelative(false), - isWSL(false) + isWindowsPath(false) {} void Clear() { - // IsCopyLink = false; - isHardLink = false; - isJunction = false; + LinkType = k_LinkType_PureSymLink; isRelative = false; - isWSL = false; - linkPath.Empty(); + isWindowsPath = false; + LinkPath.Empty(); } - bool Parse(const Byte *data, size_t dataSize, bool isLinuxData); + bool Parse_from_WindowsReparseData(const Byte *data, size_t dataSize); + bool Parse_from_LinuxData(const Byte *data, size_t dataSize); + void Normalize_to_RelativeSafe(UStringVector &removePathParts); +private: + void Remove_AbsPathPrefixes(); }; #endif // SUPPORT_LINKS + +struct CProcessedFileInfo +{ + CArcTime CTime; + CArcTime ATime; + CArcTime MTime; + UInt32 Attrib; + bool Attrib_Defined; + #ifndef _WIN32 struct COwnerInfo @@ -229,8 +256,76 @@ } }; + COwnerInfo Owner; + COwnerInfo Group; #endif + void Clear() + { +#ifndef _WIN32 + Attrib_Defined = false; + Owner.Clear(); +#endif + } + + bool IsReparse() const + { + return (Attrib_Defined && (Attrib & FILE_ATTRIBUTE_REPARSE_POINT) != 0); + } + + bool IsLinuxSymLink() const + { + return (Attrib_Defined && MY_LIN_S_ISLNK(Attrib >> 16)); + } + + void SetFromPosixAttrib(UInt32 a) + { + // here we set only part of combined attribute required by SetFileAttrib() call + #ifdef _WIN32 + // Windows sets FILE_ATTRIBUTE_NORMAL, if we try to set 0 as attribute. + Attrib = MY_LIN_S_ISDIR(a) ? + FILE_ATTRIBUTE_DIRECTORY : + FILE_ATTRIBUTE_ARCHIVE; + if ((a & 0222) == 0) // (& S_IWUSR) in p7zip + Attrib |= FILE_ATTRIBUTE_READONLY; + // 22.00 : we need type bits for (MY_LIN_S_IFLNK) for IsLinuxSymLink() + a &= MY_LIN_S_IFMT; + if (a == MY_LIN_S_IFLNK) + Attrib |= (a << 16); + #else + Attrib = (a << 16) | FILE_ATTRIBUTE_UNIX_EXTENSION; + #endif + Attrib_Defined = true; + } +}; + + +#ifdef SUPPORT_LINKS + +struct CPostLink +{ + UInt32 Index_in_Arc; + bool item_IsDir; // _item.IsDir + UString item_Path; // _item.Path; + UStringVector item_PathParts; // _item.PathParts; + CProcessedFileInfo item_FileInfo; // _fi + FString fullProcessedPath_from; // full file path in FS + CLinkInfo LinkInfo; +}; + +/* +struct CPostLinks +{ + void Clear() + { + Links.Clear(); + } +}; +*/ + +#endif // SUPPORT_LINKS + + class CArchiveExtractCallback Z7_final: public IArchiveExtractCallback, @@ -278,8 +373,9 @@ private: const CArc *_arc; +public: CExtractNtOptions _ntOptions; - +private: bool _encrypted; bool _isSplit; bool _curSize_Defined; @@ -287,8 +383,8 @@ bool _isRenamed; bool _extractMode; - // bool _is_SymLink_in_Data; - bool _is_SymLink_in_Data_Linux; // false = WIN32, true = LINUX + bool _is_SymLink_in_Data_Linux; // false = WIN32, true = LINUX. + // _is_SymLink_in_Data_Linux is detected from Windows/Linux part of attributes of file. bool _needSetAttrib; bool _isSymLinkCreated; bool _itemFailure; @@ -311,7 +407,9 @@ CMyComPtr _cryptoGetTextPassword; FString _dirPathPrefix; +public: FString _dirPathPrefix_Full; +private: #ifndef Z7_SFX @@ -323,49 +421,7 @@ CReadArcItem _item; FString _diskFilePath; - struct CProcessedFileInfo - { - CArcTime CTime; - CArcTime ATime; - CArcTime MTime; - UInt32 Attrib; - bool Attrib_Defined; - - #ifndef _WIN32 - COwnerInfo Owner; - COwnerInfo Group; - #endif - - bool IsReparse() const - { - return (Attrib_Defined && (Attrib & FILE_ATTRIBUTE_REPARSE_POINT) != 0); - } - - bool IsLinuxSymLink() const - { - return (Attrib_Defined && MY_LIN_S_ISLNK(Attrib >> 16)); - } - - void SetFromPosixAttrib(UInt32 a) - { - // here we set only part of combined attribute required by SetFileAttrib() call - #ifdef _WIN32 - // Windows sets FILE_ATTRIBUTE_NORMAL, if we try to set 0 as attribute. - Attrib = MY_LIN_S_ISDIR(a) ? - FILE_ATTRIBUTE_DIRECTORY : - FILE_ATTRIBUTE_ARCHIVE; - if ((a & 0222) == 0) // (& S_IWUSR) in p7zip - Attrib |= FILE_ATTRIBUTE_READONLY; - // 22.00 : we need type bits for (MY_LIN_S_IFLNK) for IsLinuxSymLink() - a &= MY_LIN_S_IFMT; - if (a == MY_LIN_S_IFLNK) - Attrib |= (a << 16); - #else - Attrib = (a << 16) | FILE_ATTRIBUTE_UNIX_EXTENSION; - #endif - Attrib_Defined = true; - } - } _fi; + CProcessedFileInfo _fi; UInt64 _position; UInt64 _curSize; @@ -407,19 +463,21 @@ // CObjectVector _delayedSymLinks; #endif - void CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath); + void CreateComplexDirectory( + const UStringVector &dirPathParts, bool isFinal, FString &fullPath); HRESULT GetTime(UInt32 index, PROPID propID, CArcTime &ft); HRESULT GetUnpackSize(); FString Hash_GetFullFilePath(); - void SetAttrib(); + void SetAttrib() const; public: - HRESULT SendMessageError(const char *message, const FString &path); - HRESULT SendMessageError_with_Error(HRESULT errorCode, const char *message, const FString &path); - HRESULT SendMessageError_with_LastError(const char *message, const FString &path); - HRESULT SendMessageError2(HRESULT errorCode, const char *message, const FString &path1, const FString &path2); + HRESULT SendMessageError(const char *message, const FString &path) const; + HRESULT SendMessageError_with_Error(HRESULT errorCode, const char *message, const FString &path) const; + HRESULT SendMessageError_with_LastError(const char *message, const FString &path) const; + HRESULT SendMessageError2(HRESULT errorCode, const char *message, const FString &path1, const FString &path2) const; + HRESULT SendMessageError2_with_LastError(const char *message, const FString &path1, const FString &path2) const; #if defined(_WIN32) && !defined(UNDER_CE) && !defined(Z7_SFX) NExtract::NZoneIdMode::EEnum ZoneMode; @@ -482,23 +540,32 @@ UInt64 packSize); - #ifdef SUPPORT_LINKS +#ifdef SUPPORT_LINKS private: CHardLinks _hardLinks; + CObjectVector _postLinks; CLinkInfo _link; + // const void *NtReparse_Data; + // UInt32 NtReparse_Size; // FString _copyFile_Path; // HRESULT MyCopyFile(ISequentialOutStream *outStream); - HRESULT Link(const FString &fullProcessedPath); HRESULT ReadLink(); + HRESULT SetLink( + const FString &fullProcessedPath_from, + const CLinkInfo &linkInfo, + bool &linkWasSet); + HRESULT SetPostLinks() const; public: - // call PrepareHardLinks() after Init() + HRESULT CreateHardLink2(const FString &newFilePath, + const FString &existFilePath, bool &link_was_Created) const; + HRESULT DeleteLinkFileAlways_or_RemoveEmptyDir(const FString &path, bool checkThatFileIsEmpty) const; HRESULT PrepareHardLinks(const CRecordVector *realIndices); // NULL means all items +#endif - #endif - +private: #ifdef SUPPORT_ALT_STREAMS CObjectVector _renamedFiles; @@ -506,6 +573,7 @@ // call it after Init() +public: #ifndef Z7_SFX void SetBaseParentFolderIndex(UInt32 indexInArc) { @@ -527,7 +595,6 @@ HRESULT Read_fi_Props(); void CorrectPathParts(); - void GetFiTimesCAM(CFiTimesCAM &pt); void CreateFolders(); HRESULT CheckExistFile(FString &fullProcessedPath, bool &needExit); @@ -536,18 +603,8 @@ HRESULT CloseFile(); HRESULT CloseReparseAndFile(); - HRESULT CloseReparseAndFile2(); HRESULT SetDirsTimes(); - - const void *NtReparse_Data; - UInt32 NtReparse_Size; - - #ifdef SUPPORT_LINKS - HRESULT SetFromLinkPath( - const FString &fullProcessedPath, - const CLinkInfo &linkInfo, - bool &linkWasSet); - #endif + HRESULT SetSecurityInfo(UInt32 indexInArc, const FString &path) const; }; diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/Bench.cpp 7zip-25.01+dfsg/CPP/7zip/UI/Common/Bench.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/Common/Bench.cpp 2024-11-25 14:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/Common/Bench.cpp 2025-06-30 16:00:00.000000000 +0000 @@ -871,14 +871,27 @@ unsigned NumCoreThreads; unsigned NumCores; // unsigned DivideNum; + +#ifdef _WIN32 + unsigned NumGroups; +#endif + UInt32 Sizes[NUM_CPU_LEVELS_MAX]; void SetLevels(unsigned numCores, unsigned numCoreThreads); DWORD_PTR GetAffinityMask(UInt32 bundleIndex, CCpuSet *cpuSet) const; bool NeedAffinity() const { return NumBundleThreads != 0; } +#ifdef _WIN32 + bool NeedGroupsMode() const { return NumGroups > 1; } +#endif + WRes CreateThread_WithAffinity(NWindows::CThread &thread, THREAD_FUNC_TYPE startAddress, LPVOID parameter, UInt32 bundleIndex) const { +#ifdef _WIN32 + if (NeedGroupsMode()) // we need fix for bundleIndex usage + return thread.Create_With_Group(startAddress, parameter, bundleIndex % NumGroups); +#endif if (NeedAffinity()) { CCpuSet cpuSet; @@ -892,6 +905,9 @@ NumBundleThreads(0), NumLevels(0), NumCoreThreads(1) +#ifdef _WIN32 + , NumGroups(0) +#endif // DivideNum(1) {} }; @@ -1288,22 +1304,28 @@ if (scp) { const UInt64 reduceSize = kBufferSize; - - /* in posix new thread uses same affinity as parent thread, + /* in posix : new thread uses same affinity as parent thread, so we don't need to send affinity to coder in posix */ - UInt64 affMask; - #if !defined(Z7_ST) && defined(_WIN32) + UInt64 affMask = 0; + UInt32 affinityGroup = (UInt32)(Int32)-1; + // UInt64 affinityInGroup = 0; +#if !defined(Z7_ST) && defined(_WIN32) { CCpuSet cpuSet; - affMask = AffinityMode.GetAffinityMask(EncoderIndex, &cpuSet); + if (AffinityMode.NeedGroupsMode()) // we need fix for affinityInGroup also + affinityGroup = EncoderIndex % AffinityMode.NumGroups; + else + affMask = AffinityMode.GetAffinityMask(EncoderIndex, &cpuSet); } - #else - affMask = 0; - #endif - // affMask <<= 3; // debug line: to test no affinity in coder; - // affMask = 0; - - RINOK(method.SetCoderProps_DSReduce_Aff(scp, &reduceSize, (affMask != 0 ? &affMask : NULL))) +#endif + // affMask <<= 3; // debug line: to test no affinity in coder + // affMask = 0; // for debug + // affinityGroup = 0; // for debug + // affinityInGroup = 1; // for debug + RINOK(method.SetCoderProps_DSReduce_Aff(scp, &reduceSize, + affMask != 0 ? &affMask : NULL, + affinityGroup != (UInt32)(Int32)-1 ? &affinityGroup : NULL, + /* affinityInGroup != 0 ? &affinityInGroup : */ NULL)) } else { @@ -2962,7 +2984,7 @@ { AString s; // s.Add_UInt32(ti.numProcessThreads); - unsigned numSysThreads = ti.GetNumSystemThreads(); + const unsigned numSysThreads = ti.GetNumSystemThreads(); if (ti.GetNumProcessThreads() != numSysThreads) { // if (ti.numProcessThreads != ti.numSysThreads) @@ -2992,6 +3014,35 @@ } #endif } +#ifdef _WIN32 + if (ti.Groups.GroupSizes.Size() > 1 || + (ti.Groups.GroupSizes.Size() == 1 + && ti.Groups.NumThreadsTotal != numSysThreads)) + { + s += " : "; + s.Add_UInt32(ti.Groups.GroupSizes.Size()); + s += " groups : "; + if (ti.Groups.NumThreadsTotal == numSysThreads) + { + s.Add_UInt32(ti.Groups.NumThreadsTotal); + s += " c : "; + } + UInt32 minSize, maxSize; + ti.Groups.Get_GroupSize_Min_Max(minSize, maxSize); + if (minSize == maxSize) + { + s.Add_UInt32(ti.Groups.GroupSizes[0]); + s += " c/g"; + } + else + FOR_VECTOR (i, ti.Groups.GroupSizes) + { + if (i != 0) + s.Add_Char(' '); + s.Add_UInt32(ti.Groups.GroupSizes[i]); + } + } +#endif return s; } @@ -3753,9 +3804,13 @@ UInt64 complexInCommands = kComplexInCommands; UInt32 numThreads_Start = 1; - #ifndef Z7_ST +#ifndef Z7_ST CAffinityMode affinityMode; - #endif +#ifdef _WIN32 + if (threadsInfo.IsGroupMode && threadsInfo.Groups.GroupSizes.Size() > 1) + affinityMode.NumGroups = threadsInfo.Groups.GroupSizes.Size(); +#endif +#endif COneMethodInfo method; @@ -4861,7 +4916,7 @@ if (AreSameMethodNames(benchMethod, methodName)) { if (benchProps.IsEmpty() - || (benchProps == "x5" && method.PropsString.IsEmpty()) + || (benchProps.IsEqualTo("x5") && method.PropsString.IsEmpty()) || method.PropsString.IsPrefixedBy_Ascii_NoCase(benchProps)) { callback.BenchProps.EncComplex = h.EncComplex; diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/EnumDirItems.cpp 7zip-25.01+dfsg/CPP/7zip/UI/Common/EnumDirItems.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/Common/EnumDirItems.cpp 2024-10-06 09:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/Common/EnumDirItems.cpp 2025-06-20 10:00:00.000000000 +0000 @@ -1213,11 +1213,13 @@ // continue; // for debug if (!item.Has_Attrib_ReparsePoint()) continue; - + /* + We want to get properties of target file instead of properies of symbolic link. + Probably this code is unused, because + CFileInfo::Find(with followLink = true) called Fill_From_ByHandleFileInfo() already. + */ // if (item.IsDir()) continue; - const FString phyPath = GetPhyPath(i); - NFind::CFileInfo fi; if (fi.Fill_From_ByHandleFileInfo(phyPath)) // item.IsDir() { @@ -1228,38 +1230,13 @@ item.Attrib = fi.Attrib; continue; } - - /* - // we request properties of target file instead of properies of symbolic link - // here we also can manually parse unsupported links (like WSL links) - NIO::CInFile inFile; - if (inFile.Open(phyPath)) - { - BY_HANDLE_FILE_INFORMATION info; - if (inFile.GetFileInformation(&info)) - { - // Stat.FilesSize doesn't contain item.Size already - // Stat.FilesSize -= item.Size; - item.Size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow; - Stat.FilesSize += item.Size; - item.CTime = info.ftCreationTime; - item.ATime = info.ftLastAccessTime; - item.MTime = info.ftLastWriteTime; - item.Attrib = info.dwFileAttributes; - continue; - } - } - */ - RINOK(AddError(phyPath)) continue; } - // (SymLinks == true) here - + // (SymLinks == true) if (item.ReparseData.Size() == 0) continue; - // if (item.Size == 0) { // 20.03: we use Reparse Data instead of real data @@ -1277,7 +1254,7 @@ /* imagex/WIM reduces absolute paths in links (raparse data), if we archive non root folder. We do same thing here */ - bool isWSL = false; + // bool isWSL = false; if (attr.IsSymLink_WSL()) { // isWSL = true; @@ -1314,21 +1291,27 @@ continue; if (rootPrefixSize == prefix.Len()) continue; // simple case: paths are from root - if (link.Len() <= prefix.Len()) continue; - if (CompareFileNames(link.Left(prefix.Len()), prefix) != 0) continue; UString newLink = prefix.Left(rootPrefixSize); newLink += link.Ptr(prefix.Len()); - CByteBuffer data; - bool isSymLink = !attr.IsMountPoint(); - if (!FillLinkData(data, newLink, isSymLink, isWSL)) + CByteBuffer &data = item.ReparseData2; +/* + if (isWSL) + { + Convert_WinPath_to_WslLinuxPath(newLink, true); // is absolute : change it + FillLinkData_WslLink(data, newLink); + } + else +*/ + FillLinkData_WinLink(data, newLink, !attr.IsMountPoint()); + if (data.Size() == 0) continue; - item.ReparseData2 = data; + // item.ReparseData2 = data; } return S_OK; } diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/Extract.cpp 7zip-25.01+dfsg/CPP/7zip/UI/Common/Extract.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/Common/Extract.cpp 2024-02-10 09:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/Common/Extract.cpp 2025-06-16 08:00:00.000000000 +0000 @@ -389,7 +389,7 @@ { UString s = arcPath.Ptr(pos + 1); int index = codecs->FindFormatForExtension(s); - if (index >= 0 && s == L"001") + if (index >= 0 && s.IsEqualTo("001")) { s = arcPath.Left(pos); pos = s.ReverseFind(L'.'); diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/ExtractingFilePath.cpp 7zip-25.01+dfsg/CPP/7zip/UI/Common/ExtractingFilePath.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/Common/ExtractingFilePath.cpp 2023-03-06 16:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/Common/ExtractingFilePath.cpp 2025-06-16 06:00:00.000000000 +0000 @@ -208,7 +208,7 @@ if (parts.Size() > 1 && parts[1].IsEmpty()) { i = 2; - if (parts.Size() > 2 && parts[2] == L"?") + if (parts.Size() > 2 && parts[2].IsEqualTo("?")) { i = 3; if (parts.Size() > 3 && NWindows::NFile::NName::IsDrivePath2(parts[3])) diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/HashCalc.cpp 7zip-25.01+dfsg/CPP/7zip/UI/Common/HashCalc.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/Common/HashCalc.cpp 2024-11-12 11:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/Common/HashCalc.cpp 2025-06-16 07:00:00.000000000 +0000 @@ -62,7 +62,7 @@ if (m.MethodName.IsEmpty()) m.MethodName = k_DefaultHashMethod; - if (m.MethodName == "*") + if (m.MethodName.IsEqualTo("*")) { CRecordVector tempMethods; GetHashMethods(EXTERNAL_CODECS_LOC_VARS tempMethods); @@ -431,6 +431,19 @@ } +static void Convert_TagName_to_MethodName(AString &method) +{ + // we need to convert at least SHA512/256 to SHA512-256, and SHA512/224 to SHA512-224 + // but we convert any '/' to '-'. + method.Replace('/', '-'); +} + +static void Convert_MethodName_to_TagName(AString &method) +{ + if (method.IsPrefixedBy_Ascii_NoCase("SHA512-2")) + method.ReplaceOneCharAtPos(6, '/'); +} + static void WriteLine(CDynLimBuf &hashFileString, const CHashOptionsLocal &options, @@ -440,8 +453,10 @@ { AString methodName; if (!hb.Hashers.IsEmpty()) + { methodName = hb.Hashers[0].Name; - + Convert_MethodName_to_TagName(methodName); + } AString hashesString; AddHashResultLine(hashesString, hb.Hashers); WriteLine(hashFileString, options, path, isDir, methodName, hashesString); @@ -752,7 +767,7 @@ Name = end; Hash.Alloc(4); - SetBe32(Hash, crc) + SetBe32a(Hash, crc) Size_from_Arc = size; Size_from_Arc_Defined = true; @@ -773,56 +788,87 @@ { "sha256" , "sha224" -// , "sha512-224" -// , "sha512-256" + , "sha512-224" + , "sha512-256" , "sha384" , "sha512" -// , "sha3-224" + , "sha3-224" , "sha3-256" -// , "sha3-384" -// , "sha3-512" + , "sha3-384" + , "sha3-512" // , "shake128" // , "shake256" , "sha1" + , "sha2" + , "sha3" + , "sha" , "md5" - , "blake2sp" + , "blake2s" , "blake2b" + , "blake2sp" , "xxh64" - , "crc64" , "crc32" + , "crc64" , "cksum" }; -static UString GetMethod_from_FileName(const UString &name) + +// returns true, if (method) is known hash method or hash method group name. +static bool GetMethod_from_FileName(const UString &name, AString &method) { + method.Empty(); AString s; ConvertUnicodeToUTF8(name, s); const int dotPos = s.ReverseFind_Dot(); - const char *src = s.Ptr(); - bool isExtension = false; if (dotPos >= 0) { - isExtension = true; - src = s.Ptr(dotPos + 1); + method = s.Ptr(dotPos + 1); + if (method.IsEqualTo_Ascii_NoCase("txt") || + method.IsEqualTo_Ascii_NoCase("asc")) + { + method.Empty(); + const int dotPos2 = s.Find('.'); + if (dotPos2 >= 0) + s.DeleteFrom(dotPos2); + } + } + if (method.IsEmpty()) + { + // we support file names with "sum" and "sums" postfixes: "sha256sum", "sha256sums" + unsigned size; + if (s.Len() > 4 && StringsAreEqualNoCase_Ascii(s.RightPtr(4), "sums")) + size = 4; + else if (s.Len() > 3 && StringsAreEqualNoCase_Ascii(s.RightPtr(3), "sum")) + size = 3; + else + return false; + method = s; + method.DeleteFrom(s.Len() - size); } - const char *m = ""; + unsigned i; for (i = 0; i < Z7_ARRAY_SIZE(k_CsumMethodNames); i++) { - m = k_CsumMethodNames[i]; - if (isExtension) + const char *m = k_CsumMethodNames[i]; + if (method.IsEqualTo_Ascii_NoCase(m)) { - if (StringsAreEqual_Ascii(src, m)) - break; + // method = m; // we can get lowcase + return true; + } + } + +/* + for (i = 0; i < Z7_ARRAY_SIZE(k_CsumMethodNames); i++) + { + const char *m = k_CsumMethodNames[i]; + if (method.IsPrefixedBy_Ascii_NoCase(m)) + { + method = m; // we get lowcase + return true; } - else if (IsString1PrefixedByString2_NoCase_Ascii(src, m)) - if (StringsAreEqual_Ascii(src + strlen(m), "sums")) - break; } - UString res; - if (i != Z7_ARRAY_SIZE(k_CsumMethodNames)) - res = m; - return res; +*/ + return false; } @@ -1047,7 +1093,7 @@ if (propID == kpidChecksum) { const CHashPair &hp = HashPairs[index]; - if (hp.Hash.Size() > 0) + if (hp.Hash.Size() != 0) { *data = hp.Hash; *dataSize = (UInt32)hp.Hash.Size(); @@ -1100,11 +1146,6 @@ s.Add_UInt32(_hashSize * 8); s += "-bit"; } - if (!_nameExtenstion.IsEmpty()) - { - s.Add_Space_if_NotEmpty(); - s += _nameExtenstion; - } if (_is_PgpMethod) { Add_OptSpace_String(s, "PGP"); @@ -1120,6 +1161,18 @@ Add_OptSpace_String(s, "TAG"); if (_are_there_Dirs) Add_OptSpace_String(s, "DIRS"); + if (!_method_from_FileName.IsEmpty()) + { + Add_OptSpace_String(s, "filename_method:"); + s += _method_from_FileName; + if (!_is_KnownMethod_in_FileName) + s += ":UNKNOWN"; + } + if (!_methods.IsEmpty()) + { + Add_OptSpace_String(s, "cmd_method:"); + s += _methods[0]; + } prop = s; break; } @@ -1228,6 +1281,15 @@ } +static bool isThere_Zero_Byte(const Byte *data, size_t size) +{ + for (size_t i = 0; i < size; i++) + if (data[i] == 0) + return true; + return false; +} + + Z7_COM7F_IMF(CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *openCallback)) { COM_TRY_BEGIN @@ -1239,17 +1301,9 @@ CObjectVector &pairs = HashPairs; - bool zeroMode = false; - bool cr_lf_Mode = false; - { - for (size_t i = 0; i < buf.Size(); i++) - if (buf.ConstData()[i] == 0) - { - zeroMode = true; - break; - } - } + const bool zeroMode = isThere_Zero_Byte(buf, buf.Size()); _is_ZeroMode = zeroMode; + bool cr_lf_Mode = false; if (!zeroMode) cr_lf_Mode = Is_CR_LF_Data(buf, buf.Size()); @@ -1263,13 +1317,21 @@ NCOM::CPropVariant prop; RINOK(openVolumeCallback->GetProperty(kpidName, &prop)) if (prop.vt == VT_BSTR) - _nameExtenstion = GetMethod_from_FileName(prop.bstrVal); + _is_KnownMethod_in_FileName = GetMethod_from_FileName(prop.bstrVal, _method_from_FileName); } } - bool cksumMode = false; - if (_nameExtenstion.IsEqualTo_Ascii_NoCase("cksum")) - cksumMode = true; + if (!_methods.IsEmpty()) + { + ConvertUnicodeToUTF8(_methods[0], _method_for_Extraction); + } + if (_method_for_Extraction.IsEmpty()) + { + // if (_is_KnownMethod_in_FileName) + _method_for_Extraction = _method_from_FileName; + } + + const bool cksumMode = _method_for_Extraction.IsEqualTo_Ascii_NoCase("cksum"); _is_CksumMode = cksumMode; size_t pos = 0; @@ -1366,6 +1428,7 @@ _is_ZeroMode = false; _are_there_Tags = false; _are_there_Dirs = false; + _is_KnownMethod_in_FileName = false; _hashSize_Defined = false; _hashSize = 0; } @@ -1374,7 +1437,8 @@ Z7_COM7F_IMF(CHandler::Close()) { ClearVars(); - _nameExtenstion.Empty(); + _method_from_FileName.Empty(); + _method_for_Extraction.Empty(); _pgpMethod.Empty(); HashPairs.Clear(); return S_OK; @@ -1401,19 +1465,73 @@ } -static void AddDefaultMethod(UStringVector &methods, unsigned size) +static void AddDefaultMethod(UStringVector &methods, + const char *name, unsigned size) { + int shaVersion = -1; + if (name) + { + if (StringsAreEqualNoCase_Ascii(name, "sha")) + { + shaVersion = 0; + if (size == 0) + size = 32; + } + else if (StringsAreEqualNoCase_Ascii(name, "sha1")) + { + shaVersion = 1; + if (size == 0) + size = 20; + } + else if (StringsAreEqualNoCase_Ascii(name, "sha2")) + { + shaVersion = 2; + if (size == 0) + size = 32; + } + else if (StringsAreEqualNoCase_Ascii(name, "sha3")) + { + if (size == 0 || + size == 32) name = "sha3-256"; + else if (size == 28) name = "sha3-224"; + else if (size == 48) name = "sha3-384"; + else if (size == 64) name = "sha3-512"; + } + else if (StringsAreEqualNoCase_Ascii(name, "sha512")) + { + // we allow any sha512 derived hash inside .sha512 file: + if (size == 48) name = "sha384"; + else if (size == 32) name = "sha512-256"; + else if (size == 28) name = "sha512-224"; + } + if (shaVersion >= 0) + name = NULL; + } + const char *m = NULL; - if (size == 32) m = "sha256"; - else if (size == 20) m = "sha1"; - else if (size == 16) m = "md5"; - else if (size == 8) m = "crc64"; - else if (size == 4) m = "crc32"; + if (name) + m = name; else + { + if (size == 64) m = "sha512"; + else if (size == 48) m = "sha384"; + else if (size == 32) m = "sha256"; + else if (size == 28) m = "sha224"; + else if (size == 20) m = "sha1"; + else if (shaVersion < 0) + { + if (size == 16) m = "md5"; + else if (size == 8) m = "crc64"; + else if (size == 4) m = "crc32"; + } + } + + if (!m) return; - #ifdef Z7_EXTERNAL_CODECS + +#ifdef Z7_EXTERNAL_CODECS const CExternalCodecs *_externalCodecs = g_ExternalCodecs_Ptr; - #endif +#endif CMethodId id; if (FindHashMethod(EXTERNAL_CODECS_LOC_VARS AString(m), id)) @@ -1444,15 +1562,15 @@ CHashBundle hb_Glob; // UStringVector methods = options.Methods; UStringVector methods; - - if (methods.IsEmpty() && !_nameExtenstion.IsEmpty()) + +/* + if (methods.IsEmpty() && !utf_nameExtenstion.IsEmpty() && !_hashSize_Defined) { - AString utf; - ConvertUnicodeToUTF8(_nameExtenstion, utf); CMethodId id; - if (FindHashMethod(EXTERNAL_CODECS_LOC_VARS utf, id)) + if (FindHashMethod(EXTERNAL_CODECS_LOC_VARS utf_nameExtenstion, id)) methods.Add(_nameExtenstion); } +*/ if (methods.IsEmpty() && !_pgpMethod.IsEmpty()) { @@ -1461,12 +1579,21 @@ methods.Add(UString(_pgpMethod)); } +/* if (methods.IsEmpty() && _pgpMethod.IsEmpty() && _hashSize_Defined) - AddDefaultMethod(methods, _hashSize); + { + AddDefaultMethod(methods, + utf_nameExtenstion.IsEmpty() ? NULL : utf_nameExtenstion.Ptr(), + _hashSize); + } +*/ - RINOK(hb_Glob.SetMethods( + if (!methods.IsEmpty()) + { + RINOK(hb_Glob.SetMethods( EXTERNAL_CODECS_LOC_VARS methods)) + } Z7_DECL_CMyComPtr_QI_FROM( IArchiveUpdateCallbackFile, @@ -1561,9 +1688,11 @@ { hb_Use = &hb_Loc; CMethodId id; - if (FindHashMethod(EXTERNAL_CODECS_LOC_VARS hp.Method, id)) + AString methodName = hp.Method; + Convert_TagName_to_MethodName(methodName); + if (FindHashMethod(EXTERNAL_CODECS_LOC_VARS methodName, id)) { - methods_loc.Add(UString(hp.Method)); + methods_loc.Add(UString(methodName)); RINOK(hb_Loc.SetMethods( EXTERNAL_CODECS_LOC_VARS methods_loc)) @@ -1573,7 +1702,10 @@ } else if (methods.IsEmpty()) { - AddDefaultMethod(methods_loc, (unsigned)hp.Hash.Size()); + AddDefaultMethod(methods_loc, + _method_for_Extraction.IsEmpty() ? NULL : + _method_for_Extraction.Ptr(), + (unsigned)hp.Hash.Size()); if (!methods_loc.IsEmpty()) { hb_Use = &hb_Loc; @@ -1621,7 +1753,7 @@ Int32 opRes = NArchive::NExtract::NOperationResult::kUnsupportedMethod; if (isSupportedMode && res_SetMethods != E_NOTIMPL - && hb_Use->Hashers.Size() > 0 + && !hb_Use->Hashers.IsEmpty() ) { const CHasherState &hs = hb_Use->Hashers[0]; @@ -1774,10 +1906,6 @@ methods.Add(_methods[k]); } } - else if (_crcSize_WasSet) - { - AddDefaultMethod(methods, _crcSize); - } else { Z7_DECL_CMyComPtr_QI_FROM( @@ -1789,12 +1917,23 @@ RINOK(getRootProps->GetRootProp(kpidArcFileName, &prop)) if (prop.vt == VT_BSTR) { - const UString method = GetMethod_from_FileName(prop.bstrVal); + AString method; + /* const bool isKnownMethod = */ GetMethod_from_FileName(prop.bstrVal, method); if (!method.IsEmpty()) - methods.Add(method); + { + AddDefaultMethod(methods, method, _crcSize_WasSet ? _crcSize : 0); + if (methods.IsEmpty()) + return E_NOTIMPL; + } } } } + if (methods.IsEmpty() && _crcSize_WasSet) + { + AddDefaultMethod(methods, + NULL, // name + _crcSize); + } RINOK(hb.SetMethods(EXTERNAL_CODECS_LOC_VARS methods)) @@ -2038,6 +2177,15 @@ } +void CHandler::InitProps() +{ + _supportWindowsBackslash = true; + _crcSize_WasSet = false; + _crcSize = 4; + _methods.Clear(); + _options.Init_HashOptionsLocal(); +} + Z7_COM7F_IMF(CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps)) { COM_TRY_BEGIN @@ -2088,22 +2236,27 @@ " sha512" " sha384" " sha224" - // " sha512-224" - // " sha512-256" - // " sha3-224" + " sha512-224" + " sha512-256" + " sha3-224" " sha3-256" - // " sha3-384" - // " sha3-512" + " sha3-384" + " sha3-512" // " shake128" // " shake256" " sha1" + " sha2" + " sha3" " sha" " md5" + " blake2s" + " blake2b" " blake2sp" " xxh64" - " crc32 crc64" - " asc" + " crc32" + " crc64" " cksum" + " asc" // " b2sum" ), UString()); diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/HashCalc.h 7zip-25.01+dfsg/CPP/7zip/UI/Common/HashCalc.h --- 7zip-24.09+dfsg/CPP/7zip/UI/Common/HashCalc.h 2024-02-09 18:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/Common/HashCalc.h 2024-12-09 09:00:00.000000000 +0000 @@ -279,32 +279,25 @@ bool _isArc; bool _supportWindowsBackslash; bool _crcSize_WasSet; - UInt64 _phySize; - CObjectVector HashPairs; - UString _nameExtenstion; - // UString _method_fromName; - AString _pgpMethod; bool _is_CksumMode; bool _is_PgpMethod; bool _is_ZeroMode; bool _are_there_Tags; bool _are_there_Dirs; + bool _is_KnownMethod_in_FileName; bool _hashSize_Defined; unsigned _hashSize; UInt32 _crcSize; + UInt64 _phySize; + CObjectVector HashPairs; UStringVector _methods; + AString _method_from_FileName; + AString _pgpMethod; + AString _method_for_Extraction; CHashOptionsLocal _options; void ClearVars(); - - void InitProps() - { - _supportWindowsBackslash = true; - _crcSize_WasSet = false; - _crcSize = 4; - _methods.Clear(); - _options.Init_HashOptionsLocal(); - } + void InitProps(); bool CanUpdate() const { diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/LoadCodecs.cpp 7zip-25.01+dfsg/CPP/7zip/UI/Common/LoadCodecs.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/Common/LoadCodecs.cpp 2023-12-03 18:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/Common/LoadCodecs.cpp 2025-06-16 06:00:00.000000000 +0000 @@ -170,7 +170,7 @@ if (i < addExts.Size()) { extInfo.AddExt = addExts[i]; - if (extInfo.AddExt == L"*") + if (extInfo.AddExt.IsEqualTo("*")) extInfo.AddExt.Empty(); } Exts.Add(extInfo); @@ -931,8 +931,8 @@ const UString name = arcType.Mid(pos, (unsigned)pos2 - pos); if (name.IsEmpty()) return false; - int index = FindFormatForArchiveType(name); - if (index < 0 && name != L"*") + const int index = FindFormatForArchiveType(name); + if (index < 0 && !name.IsEqualTo("*")) { formatIndices.Clear(); return false; diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/Update.cpp 7zip-25.01+dfsg/CPP/7zip/UI/Common/Update.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/Common/Update.cpp 2024-10-22 09:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/Common/Update.cpp 2024-12-30 13:00:00.000000000 +0000 @@ -474,7 +474,7 @@ CArcToDoStat stat2; - if (options.RenamePairs.Size() != 0) + if (options.RenameMode || options.RenamePairs.Size() != 0) { FOR_VECTOR (i, arcItems) { @@ -1920,7 +1920,7 @@ if (NFind::DoesDirExist(phyPath)) { RINOK(callback->DeletingAfterArchiving(phyPath, true)) - RemoveDir(phyPath); + RemoveDirAlways_if_Empty(phyPath); } } diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/Update.h 7zip-25.01+dfsg/CPP/7zip/UI/Common/Update.h --- 7zip-24.09+dfsg/CPP/7zip/UI/Common/Update.h 2024-10-22 09:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/Common/Update.h 2024-12-30 13:00:00.000000000 +0000 @@ -94,6 +94,7 @@ bool DeleteAfterCompressing; bool SetArcMTime; + bool RenameMode; CBoolPair NtSecurity; CBoolPair AltStreams; @@ -139,6 +140,7 @@ DeleteAfterCompressing(false), SetArcMTime(false), + RenameMode(false), ArcNameMode(k_ArcNameMode_Smart), PathMode(NWildcard::k_RelatPath) diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/UpdateCallback.cpp 7zip-25.01+dfsg/CPP/7zip/UI/Common/UpdateCallback.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/Common/UpdateCallback.cpp 2024-03-03 16:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/Common/UpdateCallback.cpp 2025-06-18 12:00:00.000000000 +0000 @@ -32,6 +32,7 @@ #include "../../../Windows/PropVariant.h" #include "../../Common/StreamObjects.h" +#include "../../Archive/Common/ItemNameUtils.h" #include "UpdateCallback.h" @@ -306,7 +307,7 @@ #if defined(_WIN32) && !defined(UNDER_CE) -static UString GetRelativePath(const UString &to, const UString &from) +static UString GetRelativePath(const UString &to, const UString &from, bool isWSL) { UStringVector partsTo, partsFrom; SplitPathToParts(to, partsTo); @@ -324,11 +325,12 @@ if (i == 0) { - #ifdef _WIN32 - if (NName::IsDrivePath(to) || - NName::IsDrivePath(from)) +#ifdef _WIN32 + if (isWSL || + (NName::IsDrivePath(to) || + NName::IsDrivePath(from))) return to; - #endif +#endif } UString s; @@ -373,54 +375,87 @@ return S_OK; } - #if !defined(UNDER_CE) - +#if !defined(UNDER_CE) if (up.DirIndex >= 0) { const CDirItem &di = DirItems->Items[(unsigned)up.DirIndex]; - - #ifdef _WIN32 - // if (di.IsDir()) + if (di.ReparseData.Size()) { +#ifdef _WIN32 CReparseAttr attr; if (attr.Parse(di.ReparseData, di.ReparseData.Size())) { - const UString simpleName = attr.GetPath(); - if (!attr.IsSymLink_WSL() && attr.IsRelative_Win()) - prop = simpleName; - else + UString path = attr.GetPath(); + if (!path.IsEmpty()) { - const FString phyPath = DirItems->GetPhyPath((unsigned)up.DirIndex); - FString fullPath; - if (NDir::MyGetFullPathName(phyPath, fullPath)) + bool isWSL = attr.IsSymLink_WSL(); + if (isWSL) + NArchive::NItemName::ReplaceToWinSlashes(path, true); // useBackslashReplacement + // it's expected that (path) now uses windows slashes. + // CReparseAttr::IsRelative_Win() returns true if FLAG_RELATIVE is set + // CReparseAttr::IsRelative_Win() returns true for "\dir1\path" + // but we want to store real relative paths without "\" root prefix. + // so we parse path instead of IsRelative_Win() calling. + if (// attr.IsRelative_Win() || + (isWSL ? + IS_PATH_SEPAR(path[0]) : + NName::IsAbsolutePath(path))) { - prop = GetRelativePath(simpleName, fs2us(fullPath)); + // (path) is abolute path or relative to root: "\path" + // we try to convert (path) to relative path for writing to archive. + const FString phyPath = DirItems->GetPhyPath((unsigned)up.DirIndex); + FString fullPath; + if (NDir::MyGetFullPathName(phyPath, fullPath)) + { + if (IS_PATH_SEPAR(path[0]) && + !IS_PATH_SEPAR(path[1])) + { + // path is relative to root of (fullPath): "\path" + const unsigned prefixSize = NName::GetRootPrefixSize(fullPath); + if (prefixSize) + { + path.DeleteFrontal(1); + path.Insert(0, fs2us(fullPath.Left(prefixSize))); + // we have changed "\" prefix to drive prefix "c:\" in (path). + // (path) is Windows path now. + isWSL = false; + } + } + } + path = GetRelativePath(path, fs2us(fullPath), isWSL); } +#if WCHAR_PATH_SEPARATOR != L'/' + // 7-Zip's TAR handler in Windows replaces windows slashes to linux slashes. + // so we can return any slashes to TAR handler. + // or we can convert to linux slashes here, + // because input IInArchive handler uses linux slashes for kpidSymLink. + // path.Replace(WCHAR_PATH_SEPARATOR, L'/'); +#endif + if (!path.IsEmpty()) + prop = path; } - prop.Detach(value); - return S_OK; } - } - - #else // _WIN32 - - if (di.ReparseData.Size() != 0) - { +#else // ! _WIN32 AString utf; utf.SetFrom_CalcLen((const char *)(const Byte *)di.ReparseData, (unsigned)di.ReparseData.Size()); - + #if 0 // 0 - for debug + // it's expected that link data uses system codepage. + // fs2us() ignores conversion errors. But we want correct path + UString us (fs2us(utf)); + #else UString us; if (ConvertUTF8ToUnicode(utf, us)) + #endif { - prop = us; - prop.Detach(value); - return S_OK; + if (!us.IsEmpty()) + prop = us; } +#endif // ! _WIN32 } - - #endif // _WIN32 + prop.Detach(value); + return S_OK; } - #endif // !defined(UNDER_CE) +#endif // !defined(UNDER_CE) } else if (propID == kpidHardLink) { @@ -428,7 +463,12 @@ { const CKeyKeyValPair &pair = _map[_hardIndex_To]; const CUpdatePair2 &up2 = (*UpdatePairs)[pair.Value]; - prop = DirItems->GetLogPath((unsigned)up2.DirIndex); + const UString path = DirItems->GetLogPath((unsigned)up2.DirIndex); +#if WCHAR_PATH_SEPARATOR != L'/' + // 7-Zip's TAR handler in Windows replaces windows slashes to linux slashes. + // path.Replace(WCHAR_PATH_SEPARATOR, L'/'); +#endif + prop = path; prop.Detach(value); return S_OK; } @@ -438,7 +478,7 @@ return S_OK; } } - } + } // if (up.NewData) if (up.IsAnti && propID != kpidIsDir diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Console/Main.cpp 7zip-25.01+dfsg/CPP/7zip/UI/Console/Main.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/Console/Main.cpp 2024-06-17 11:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/Console/Main.cpp 2025-01-24 07:00:00.000000000 +0000 @@ -908,9 +908,12 @@ if (options.EnableHeaders) { - ShowCopyrightAndHelp(g_StdStream, false); - if (!parser.Parse1Log.IsEmpty()) - *g_StdStream << parser.Parse1Log; + if (g_StdStream) + { + ShowCopyrightAndHelp(g_StdStream, false); + if (!parser.Parse1Log.IsEmpty()) + *g_StdStream << parser.Parse1Log; + } } parser.Parse2(options); diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Console/makefile 7zip-25.01+dfsg/CPP/7zip/UI/Console/makefile --- 7zip-24.09+dfsg/CPP/7zip/UI/Console/makefile 2023-01-29 17:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/Console/makefile 2025-07-01 15:00:00.000000000 +0000 @@ -59,10 +59,10 @@ C_OBJS = $(C_OBJS) \ $O\Alloc.obj \ $O\CpuArch.obj \ - $O\Sort.obj \ $O\Threads.obj \ !include "../../Crc.mak" +!include "../../Sort.mak" !include "Console.mak" !include "../../7zip.mak" diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Explorer/makefile 7zip-25.01+dfsg/CPP/7zip/UI/Explorer/makefile --- 7zip-24.09+dfsg/CPP/7zip/UI/Explorer/makefile 2024-03-20 07:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/Explorer/makefile 2025-07-01 15:00:00.000000000 +0000 @@ -72,7 +72,7 @@ C_OBJS = \ $O\CpuArch.obj \ - $O\Sort.obj \ $O\Threads.obj \ +!include "../../Sort.mak" !include "../../7zip.mak" diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Far/Plugin.cpp 7zip-25.01+dfsg/CPP/7zip/UI/Far/Plugin.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/Far/Plugin.cpp 2024-01-23 20:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/Far/Plugin.cpp 2025-06-16 07:00:00.000000000 +0000 @@ -61,7 +61,6 @@ } #define kDotsReplaceString "[[..]]" -#define kDotsReplaceStringU L"[[..]]" static void CopyStrLimited(char *dest, const AString &src, unsigned len) { @@ -84,7 +83,7 @@ throw 272340; AString oemString (UnicodeStringToMultiByte(prop.bstrVal, CP_OEMCP)); - if (oemString == "..") + if (oemString.IsEqualTo("..")) oemString = kDotsReplaceString; COPY_STR_LIMITED(panelItem.FindData.cFileName, oemString); @@ -193,7 +192,7 @@ { CMyComPtr newFolder; UString s = dirName; - if (dirName == kDotsReplaceStringU) + if (dirName.IsEqualTo(kDotsReplaceString)) s = ".."; _folder->BindToFolder(s, &newFolder); if (!newFolder) @@ -209,12 +208,12 @@ int CPlugin::SetDirectory(const char *aszDir, int /* opMode */) { UString path = MultiByteToUnicodeString(aszDir, CP_OEMCP); - if (path == WSTRING_PATH_SEPARATOR) + if (path.IsEqualTo(STRING_PATH_SEPARATOR)) { _folder.Release(); m_ArchiveHandler->BindToRootFolder(&_folder); } - else if (path == L"..") + else if (path.IsEqualTo("..")) { CMyComPtr newFolder; _folder->BindToParentFolder(&newFolder); diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Far/makefile 7zip-25.01+dfsg/CPP/7zip/UI/Far/makefile --- 7zip-24.09+dfsg/CPP/7zip/UI/Far/makefile 2024-01-27 10:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/Far/makefile 2025-07-01 15:00:00.000000000 +0000 @@ -99,9 +99,9 @@ C_OBJS = \ $O\Alloc.obj \ $O\CpuArch.obj \ - $O\Sort.obj \ $O\Threads.obj \ !include "../../Crc.mak" +!include "../../Sort.mak" !include "../../7zip.mak" diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/FM.cpp 7zip-25.01+dfsg/CPP/7zip/UI/FileManager/FM.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/FM.cpp 2024-10-21 07:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/FileManager/FM.cpp 2025-06-15 08:00:00.000000000 +0000 @@ -651,7 +651,7 @@ SplitStringToTwoStrings(commandsString, paramString, tailString); paramString.Trim(); tailString.Trim(); - if (tailString.IsPrefixedBy(L"-t")) + if (tailString.IsPrefixedBy("-t")) g_ArcFormat = tailString.Ptr(2); /* diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/LangUtils.cpp 7zip-25.01+dfsg/CPP/7zip/UI/FileManager/LangUtils.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/LangUtils.cpp 2024-03-15 07:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/FileManager/LangUtils.cpp 2025-01-07 20:00:00.000000000 +0000 @@ -309,15 +309,13 @@ { g_Lang.Clear(); ReadRegLang(g_LangID); - #ifndef _UNICODE - if (g_IsNT) - #endif + if (g_LangID.IsEmpty()) { - if (g_LangID.IsEmpty()) - { +#ifndef _UNICODE + if (g_IsNT) +#endif OpenDefaultLang(); - return; - } + return; } if (g_LangID.Len() > 1 || g_LangID[0] != L'-') { diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/LinkDialog.cpp 7zip-25.01+dfsg/CPP/7zip/UI/FileManager/LinkDialog.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/LinkDialog.cpp 2023-03-19 17:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/FileManager/LinkDialog.cpp 2025-06-20 10:00:00.000000000 +0000 @@ -45,28 +45,24 @@ CByteBuffer buf; if (!NIO::GetReparseData(path, buf, NULL)) return false; - if (!attr.Parse(buf, buf.Size())) { SetLastError(attr.ErrorCode); return false; } - CByteBuffer data2; - if (!FillLinkData(data2, attr.GetPath(), - !attr.IsMountPoint(), attr.IsSymLink_WSL())) + FillLinkData(data2, attr.GetPath(), + !attr.IsMountPoint(), attr.IsSymLink_WSL()); + if (data2.Size() == 0) { errorMessage = "Cannot reproduce reparse point"; return false; } - - if (data2.Size() != buf.Size() || - memcmp(data2, buf, buf.Size()) != 0) + if (data2 != buf) { errorMessage = "mismatch for reproduced reparse point"; return false; } - return true; } @@ -113,8 +109,8 @@ const bool res = GetSymLink(us2fs(FilePath), attr, error); if (!res && error.IsEmpty()) { - DWORD lastError = GetLastError(); - if (lastError != 0) + const DWORD lastError = GetLastError(); + if (lastError) error = NError::MyFormatMessage(lastError); } @@ -319,10 +315,10 @@ return; } - const bool isSymLink = (idb != IDR_LINK_TYPE_JUNCTION); - CByteBuffer data; - if (!FillLinkData(data, to, isSymLink, isWSL)) + const bool isSymLink = (idb != IDR_LINK_TYPE_JUNCTION); + FillLinkData(data, to, isSymLink, isWSL); + if (data.Size() == 0) { ShowError(L"Incorrect link"); return; @@ -386,6 +382,9 @@ path = destPanel.GetFsPath(); } + CSelectedState srcSelState; + srcPanel.SaveSelectedState(srcSelState); + CLinkDialog dlg; dlg.CurDirPrefix = fsPrefix; dlg.FilePath = srcPath + itemName; @@ -394,7 +393,10 @@ if (dlg.Create(srcPanel.GetParent()) != IDOK) return; - // fix it: we should refresh panel with changed link + // we refresh srcPanel to show changes in "Link" (kpidNtReparse) column. + // maybe we should refresh another panel also? + if (srcPanel._visibleColumns.FindItem_for_PropID(kpidNtReparse) >= 0) + srcPanel.RefreshListCtrl(srcSelState); RefreshTitleAlways(); } diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/Panel.h 7zip-25.01+dfsg/CPP/7zip/UI/FileManager/Panel.h --- 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/Panel.h 2024-10-13 10:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/FileManager/Panel.h 2025-06-16 06:00:00.000000000 +0000 @@ -711,8 +711,8 @@ } // bool IsFsOrDrivesFolder() const { return IsFSFolder() || IsFSDrivesFolder(); } - bool IsDeviceDrivesPrefix() const { return _currentFolderPrefix == L"\\\\.\\"; } - bool IsSuperDrivesPrefix() const { return _currentFolderPrefix == L"\\\\?\\"; } + bool IsDeviceDrivesPrefix() const { return _currentFolderPrefix.IsEqualTo("\\\\.\\"); } + bool IsSuperDrivesPrefix() const { return _currentFolderPrefix.IsEqualTo("\\\\?\\"); } /* c:\Dir diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/PanelCopy.cpp 7zip-25.01+dfsg/CPP/7zip/UI/FileManager/PanelCopy.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/PanelCopy.cpp 2024-10-22 08:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/FileManager/PanelCopy.cpp 2025-06-16 06:00:00.000000000 +0000 @@ -284,7 +284,7 @@ if (options.hashMethods.Size() == 1) { const UString &s = options.hashMethods[0]; - if (s != L"*") + if (!s.IsEqualTo("*")) title = s; } } diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/PanelFolderChange.cpp 7zip-25.01+dfsg/CPP/7zip/UI/FileManager/PanelFolderChange.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/PanelFolderChange.cpp 2024-07-09 11:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/FileManager/PanelFolderChange.cpp 2025-06-16 08:00:00.000000000 +0000 @@ -428,7 +428,7 @@ UString name_Computer = RootFolder_GetName_Computer(iconIndex); name_Computer.Add_PathSepar(); if (path == name_Computer - || path == L"\\\\?\\") + || path.IsEqualTo("\\\\?\\")) item.iImage = iconIndex; else { @@ -639,7 +639,7 @@ unsigned indent = 0; { UString path = _currentFolderPrefix; - // path = L"\\\\.\\y:\\"; // for debug + // path = "\\\\.\\y:\\"; // for debug UString prefix0; if (path.IsPrefixedBy_Ascii_NoCase("\\\\")) { @@ -702,7 +702,7 @@ int iconIndex_Computer; const UString name_Computer = RootFolder_GetName_Computer(iconIndex_Computer); - // const bool is_devicePrefix = (sumPath == L"\\\\.\\"); + // const bool is_devicePrefix = (sumPath.IsEqualTo("\\\\.\\")); if (pathParts.Size() > 1) if (!sumPath.IsEmpty() @@ -901,8 +901,8 @@ { s = _currentFolderPrefix; s.DeleteBack(); - if (s != L"\\\\." && - s != L"\\\\?") + if (!s.IsEqualTo("\\\\.") && + !s.IsEqualTo("\\\\?")) { int pos = s.ReverseFind_PathSepar(); if (pos >= 0) @@ -935,8 +935,8 @@ } else */ - if (focusedName != L"\\\\." && - focusedName != L"\\\\?") + if (!focusedName.IsEqualTo("\\\\.") && + !focusedName.IsEqualTo("\\\\?")) { const int pos = focusedName.ReverseFind_PathSepar(); if (pos >= 0) diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/PanelOperations.cpp 7zip-25.01+dfsg/CPP/7zip/UI/FileManager/PanelOperations.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/PanelOperations.cpp 2024-10-04 17:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/FileManager/PanelOperations.cpp 2025-06-16 07:00:00.000000000 +0000 @@ -275,8 +275,8 @@ { const UString lastPart = name.Ptr((unsigned)(name.ReverseFind_PathSepar() + 1)); return - lastPart != L"." && - lastPart != L".."; + !lastPart.IsEqualTo(".") && + !lastPart.IsEqualTo(".."); } bool CorrectFsPath(const UString &relBase, const UString &path, UString &result); diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/RootFolder.cpp 7zip-25.01+dfsg/CPP/7zip/UI/FileManager/RootFolder.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/RootFolder.cpp 2024-07-09 11:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/FileManager/RootFolder.cpp 2025-06-16 06:00:00.000000000 +0000 @@ -249,7 +249,7 @@ AreEqualNames(name2, L"Documents")) return BindToFolder((UInt32)ROOT_INDEX_DOCUMENTS, resultFolder); #else - if (name2 == WSTRING_PATH_SEPARATOR) + if (name2.IsEqualTo(STRING_PATH_SEPARATOR)) return BindToFolder((UInt32)ROOT_INDEX_COMPUTER, resultFolder); #endif @@ -257,7 +257,7 @@ AreEqualNames(name2, L"Computer")) return BindToFolder((UInt32)ROOT_INDEX_COMPUTER, resultFolder); - if (name2 == WSTRING_PATH_SEPARATOR) + if (name2.IsEqualTo(STRING_PATH_SEPARATOR)) { CMyComPtr subFolder = this; *resultFolder = subFolder.Detach(); diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/makefile 7zip-25.01+dfsg/CPP/7zip/UI/FileManager/makefile --- 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/makefile 2024-01-27 09:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/FileManager/makefile 2025-07-01 15:00:00.000000000 +0000 @@ -104,7 +104,7 @@ C_OBJS = $(C_OBJS) \ $O\Alloc.obj \ $O\CpuArch.obj \ - $O\Sort.obj \ $O\Threads.obj \ +!include "../../Sort.mak" !include "../../7zip.mak" diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/GUI/BenchmarkDialog.cpp 7zip-25.01+dfsg/CPP/7zip/UI/GUI/BenchmarkDialog.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/GUI/BenchmarkDialog.cpp 2024-11-08 17:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/GUI/BenchmarkDialog.cpp 2025-06-16 07:00:00.000000000 +0000 @@ -1856,7 +1856,7 @@ const CProperty &prop = props[i]; UString name = prop.Name; name.MakeLower_Ascii(); - if (name.IsEqualTo_Ascii_NoCase("m") && prop.Value == L"*") + if (name.IsEqualTo_Ascii_NoCase("m") && prop.Value.IsEqualTo("*")) { bd.TotalMode = true; continue; @@ -1865,7 +1865,7 @@ NCOM::CPropVariant propVariant; if (!prop.Value.IsEmpty()) ParseNumberString(prop.Value, propVariant); - if (name.IsPrefixedBy(L"mt")) + if (name.IsPrefixedBy("mt")) { #ifndef Z7_ST RINOK(ParseMtProp(name.Ptr(2), propVariant, numCPUs, numThreads)) diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/GUI/BenchmarkDialog.rc 7zip-25.01+dfsg/CPP/7zip/UI/GUI/BenchmarkDialog.rc --- 7zip-24.09+dfsg/CPP/7zip/UI/GUI/BenchmarkDialog.rc 2021-05-26 11:06:11.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/GUI/BenchmarkDialog.rc 2025-07-03 12:00:00.000000000 +0000 @@ -81,7 +81,7 @@ LTEXT "&Number of CPU threads:", IDT_BENCH_NUM_THREADS, m, 30, g0xs, 8 COMBOBOX IDC_BENCH_NUM_THREADS, g1x, 29, g1xs, 140, MY_COMBO - LTEXT "", IDT_BENCH_HARDWARE_THREADS, gc2x, 30, g7xs, MY_TEXT_NOPREFIX + LTEXT "", IDT_BENCH_HARDWARE_THREADS, gc2x, 30, g7xs, 24, SS_NOPREFIX RTEXT "Size", IDT_BENCH_SIZE, xSize, 54, sSize, MY_TEXT_NOPREFIX RTEXT "CPU Usage", IDT_BENCH_USAGE_LABEL, xUsage, 54, sUsage, MY_TEXT_NOPREFIX diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/GUI/CompressDialog.cpp 7zip-25.01+dfsg/CPP/7zip/UI/GUI/CompressDialog.cpp --- 7zip-24.09+dfsg/CPP/7zip/UI/GUI/CompressDialog.cpp 2024-11-29 16:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/GUI/CompressDialog.cpp 2025-07-04 09:00:00.000000000 +0000 @@ -2600,11 +2600,17 @@ UInt32 numAlgoThreadsMax = numHardwareThreads * 2; const int methodID = GetMethodID(); - switch (methodID) + const bool isZip = IsZipFormat(); + if (isZip) + numAlgoThreadsMax = + 8 << (sizeof(size_t) / 2); // 32 threads for 32-bit : 128 threads for 64-bit + else if (IsXzFormat()) + numAlgoThreadsMax = 256 * 2; + else switch (methodID) { case kLZMA: numAlgoThreadsMax = 2; break; case kLZMA2: numAlgoThreadsMax = 256; break; - case kBZip2: numAlgoThreadsMax = 32; break; + case kBZip2: numAlgoThreadsMax = 64; break; // case kZSTD: numAlgoThreadsMax = num_ZSTD_threads_MAX; break; case kCopy: case kPPMd: @@ -2613,17 +2619,6 @@ case kPPMdZip: numAlgoThreadsMax = 1; } - const bool isZip = IsZipFormat(); - if (isZip) - { - numAlgoThreadsMax = - #ifdef _WIN32 - 64; // _WIN32 supports only 64 threads in one group. So no need for more threads here - #else - 128; - #endif - } - UInt32 autoThreads = numHardwareThreads; if (autoThreads > numAlgoThreadsMax) autoThreads = numAlgoThreadsMax; @@ -3008,7 +3003,7 @@ else { size += numBlockThreads * (size1 + chunkSize); - UInt32 numPackChunks = numBlockThreads + (numBlockThreads / 8) + 1; + const UInt32 numPackChunks = numBlockThreads + (numBlockThreads / 8) + 1; if (chunkSize < ((UInt32)1 << 26)) numBlockThreads++; if (chunkSize < ((UInt32)1 << 24)) numBlockThreads++; if (chunkSize < ((UInt32)1 << 22)) numBlockThreads++; diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/GUI/makefile 7zip-25.01+dfsg/CPP/7zip/UI/GUI/makefile --- 7zip-24.09+dfsg/CPP/7zip/UI/GUI/makefile 2024-03-14 11:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/7zip/UI/GUI/makefile 2025-07-01 15:00:00.000000000 +0000 @@ -141,10 +141,9 @@ $O\Alloc.obj \ $O\CpuArch.obj \ $O\DllSecur.obj \ - $O\Sort.obj \ $O\Threads.obj \ !include "../../Crc.mak" - +!include "../../Sort.mak" !include "../../7zip.mak" diff -Nru 7zip-24.09+dfsg/CPP/Build.mak 7zip-25.01+dfsg/CPP/Build.mak --- 7zip-24.09+dfsg/CPP/Build.mak 2024-05-02 12:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/Build.mak 2025-07-02 05:00:00.000000000 +0000 @@ -111,7 +111,13 @@ !IFNDEF UNDER_CE !IF "$(CC)" != "clang-cl" -CFLAGS = $(CFLAGS) -MP4 +MP_NPROC = 16 +!IFDEF NUMBER_OF_PROCESSORS +!IF $(NUMBER_OF_PROCESSORS) < $(MP_NPROC) +MP_NPROC = $(NUMBER_OF_PROCESSORS) +!ENDIF +!ENDIF +CFLAGS = $(CFLAGS) -MP$(MP_NPROC) !ENDIF !IFNDEF PLATFORM # CFLAGS = $(CFLAGS) -arch:IA32 diff -Nru 7zip-24.09+dfsg/CPP/Common/MyString.cpp 7zip-25.01+dfsg/CPP/Common/MyString.cpp --- 7zip-24.09+dfsg/CPP/Common/MyString.cpp 2024-02-19 09:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/Common/MyString.cpp 2025-06-15 08:00:00.000000000 +0000 @@ -208,35 +208,6 @@ // ---------- ASCII ---------- -bool AString::IsPrefixedBy_Ascii_NoCase(const char *s) const throw() -{ - const char *s1 = _chars; - for (;;) - { - const char c2 = *s++; - if (c2 == 0) - return true; - const char c1 = *s1++; - if (MyCharLower_Ascii(c1) != - MyCharLower_Ascii(c2)) - return false; - } -} - -bool UString::IsPrefixedBy_Ascii_NoCase(const char *s) const throw() -{ - const wchar_t *s1 = _chars; - for (;;) - { - const char c2 = *s++; - if (c2 == 0) - return true; - const wchar_t c1 = *s1++; - if (MyCharLower_Ascii(c1) != (unsigned char)MyCharLower_Ascii(c2)) - return false; - } -} - bool StringsAreEqual_Ascii(const char *u, const char *a) throw() { for (;;) diff -Nru 7zip-24.09+dfsg/CPP/Common/MyString.h 7zip-25.01+dfsg/CPP/Common/MyString.h --- 7zip-24.09+dfsg/CPP/Common/MyString.h 2024-01-22 09:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/Common/MyString.h 2025-07-01 10:00:00.000000000 +0000 @@ -429,11 +429,11 @@ // int CompareNoCase(const char *s) const { return MyStringCompareNoCase(_chars, s); } // int CompareNoCase(const AString &s) const { return MyStringCompareNoCase(_chars, s._chars); } bool IsPrefixedBy(const char *s) const { return IsString1PrefixedByString2(_chars, s); } - bool IsPrefixedBy_Ascii_NoCase(const char *s) const throw(); + bool IsPrefixedBy_Ascii_NoCase(const char *s) const { return IsString1PrefixedByString2_NoCase_Ascii(_chars, s); } bool IsAscii() const { - unsigned len = Len(); + const unsigned len = Len(); const char *s = _chars; for (unsigned i = 0; i < len; i++) if ((unsigned char)s[i] >= 0x80) @@ -727,22 +727,23 @@ // int CompareNoCase(const wchar_t *s) const { return MyStringCompareNoCase(_chars, s); } // int CompareNoCase(const UString &s) const { return MyStringCompareNoCase(_chars, s._chars); } bool IsPrefixedBy(const wchar_t *s) const { return IsString1PrefixedByString2(_chars, s); } + bool IsPrefixedBy(const char *s) const { return IsString1PrefixedByString2(_chars, s); } bool IsPrefixedBy_NoCase(const wchar_t *s) const { return IsString1PrefixedByString2_NoCase(_chars, s); } - bool IsPrefixedBy_Ascii_NoCase(const char *s) const throw(); + bool IsPrefixedBy_Ascii_NoCase(const char *s) const { return IsString1PrefixedByString2_NoCase_Ascii(_chars, s); } bool IsAscii() const { - unsigned len = Len(); + const unsigned len = Len(); const wchar_t *s = _chars; for (unsigned i = 0; i < len; i++) - if (s[i] >= 0x80) + if ((unsigned)(int)s[i] >= 0x80) return false; return true; } int Find(wchar_t c) const { return FindCharPosInString(_chars, c); } int Find(wchar_t c, unsigned startIndex) const { - int pos = FindCharPosInString(_chars + startIndex, c); + const int pos = FindCharPosInString(_chars + startIndex, c); return pos < 0 ? -1 : (int)startIndex + pos; } diff -Nru 7zip-24.09+dfsg/CPP/Common/MyXml.cpp 7zip-25.01+dfsg/CPP/Common/MyXml.cpp --- 7zip-24.09+dfsg/CPP/Common/MyXml.cpp 2024-01-21 20:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/Common/MyXml.cpp 2025-06-16 09:00:00.000000000 +0000 @@ -24,7 +24,7 @@ int CXmlItem::FindProp(const char *propName) const throw() { FOR_VECTOR (i, Props) - if (Props[i].Name == propName) + if (Props[i].Name.IsEqualTo(propName)) return (int)i; return -1; } @@ -39,7 +39,7 @@ bool CXmlItem::IsTagged(const char *tag) const throw() { - return (IsTag && Name == tag); + return (IsTag && Name.IsEqualTo(tag)); } int CXmlItem::FindSubTag(const char *tag) const throw() diff -Nru 7zip-24.09+dfsg/CPP/Common/Sha3Reg.cpp 7zip-25.01+dfsg/CPP/Common/Sha3Reg.cpp --- 7zip-24.09+dfsg/CPP/Common/Sha3Reg.cpp 2024-11-26 09:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/Common/Sha3Reg.cpp 2024-12-01 09:00:00.000000000 +0000 @@ -58,7 +58,7 @@ static IHasher *CreateHasherSpec() \ { return new CSha3Hasher(digestSize / 8, isShake, \ SHA3_BLOCK_SIZE_FROM_DIGEST_SIZE(digestSize_for_blockSize / 8)); } \ - static const CHasherInfo g_HasherInfo = { CreateHasherSpec, id, name, digestSize }; \ + static const CHasherInfo g_HasherInfo = { CreateHasherSpec, id, name, digestSize / 8 }; \ struct REGISTER_HASHER_NAME(cls) { REGISTER_HASHER_NAME(cls)() { RegisterHasher(&g_HasherInfo); }}; \ static REGISTER_HASHER_NAME(cls) g_RegisterHasher; } diff -Nru 7zip-24.09+dfsg/CPP/Common/Wildcard.cpp 7zip-25.01+dfsg/CPP/Common/Wildcard.cpp --- 7zip-24.09+dfsg/CPP/Common/Wildcard.cpp 2022-12-30 15:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/Common/Wildcard.cpp 2025-06-16 10:00:00.000000000 +0000 @@ -255,7 +255,8 @@ bool CItem::AreAllAllowed() const { - return ForFile && ForDir && WildcardMatching && PathParts.Size() == 1 && PathParts.Front() == L"*"; + return ForFile && ForDir && WildcardMatching + && PathParts.Size() == 1 && PathParts.Front().IsEqualTo("*"); } bool CItem::CheckPath(const UStringVector &pathParts, bool isFile) const @@ -542,7 +543,7 @@ { if (pathParts.Size() < 4 || !pathParts[1].IsEmpty() - || pathParts[2] != L"?") + || !pathParts[2].IsEqualTo("?")) return 0; testIndex = 3; } @@ -574,11 +575,11 @@ return 1; if (pathParts.Size() == 2) return 2; - if (pathParts[2] == L".") + if (pathParts[2].IsEqualTo(".")) return 3; unsigned networkParts = 2; - if (pathParts[2] == L"?") + if (pathParts[2].IsEqualTo("?")) { if (pathParts.Size() == 3) return 3; @@ -642,7 +643,7 @@ if (pathParts.Size() >= 3 && pathParts[0].IsEmpty() && pathParts[1].IsEmpty() - && pathParts[2] == L"?") + && pathParts[2].IsEqualTo("?")) ignoreWildcardIndex = 2; // #endif @@ -665,7 +666,7 @@ for (unsigned i = numPrefixParts; i < pathParts.Size(); i++) { const UString &part = pathParts[i]; - if (part == L".." || part == L".") + if (part.IsEqualTo("..") || part.IsEqualTo(".")) dotsIndex = (int)i; } diff -Nru 7zip-24.09+dfsg/CPP/Windows/FileDir.cpp 7zip-25.01+dfsg/CPP/Windows/FileDir.cpp --- 7zip-24.09+dfsg/CPP/Windows/FileDir.cpp 2024-10-24 05:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/Windows/FileDir.cpp 2025-07-24 13:00:00.000000000 +0000 @@ -124,7 +124,7 @@ #endif // UNDER_CE -bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime) +static bool SetFileTime_Base(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime, DWORD dwFlagsAndAttributes) { #ifndef _UNICODE if (!g_IsNT) @@ -137,14 +137,14 @@ HANDLE hDir = INVALID_HANDLE_VALUE; IF_USE_MAIN_PATH hDir = ::CreateFileW(fs2us(path), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + NULL, OPEN_EXISTING, dwFlagsAndAttributes, NULL); #ifdef Z7_LONG_PATH if (hDir == INVALID_HANDLE_VALUE && USE_SUPER_PATH) { UString superPath; if (GetSuperPath(path, superPath, USE_MAIN_PATH)) hDir = ::CreateFileW(superPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + NULL, OPEN_EXISTING, dwFlagsAndAttributes, NULL); } #endif @@ -157,6 +157,15 @@ return res; } +bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime) +{ + return SetFileTime_Base(path, cTime, aTime, mTime, FILE_FLAG_BACKUP_SEMANTICS); +} + +bool SetLinkFileTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime) +{ + return SetFileTime_Base(path, cTime, aTime, mTime, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT); +} bool SetFileAttrib(CFSTR path, DWORD attrib) @@ -651,6 +660,35 @@ return RemoveDir(path); } +bool RemoveDirAlways_if_Empty(const FString &path) +{ + const DWORD attrib = NFind::GetFileAttrib(path); + if (attrib != INVALID_FILE_ATTRIBUTES + && (attrib & FILE_ATTRIBUTE_READONLY)) + { + bool need_ClearAttrib = true; + if ((attrib & FILE_ATTRIBUTE_REPARSE_POINT) == 0) + { + FString s (path); + s.Add_PathSepar(); + NFind::CEnumerator enumerator; + enumerator.SetDirPrefix(s); + NFind::CDirEntry fi; + if (enumerator.Next(fi)) + { + // we don't want to change attributes, if there are files + // in directory, because RemoveDir(path) will fail. + need_ClearAttrib = false; + // SetLastError(ERROR_DIR_NOT_EMPTY); + // return false; + } + } + if (need_ClearAttrib) + SetFileAttrib(path, 0); // we clear read-only attrib to remove read-only dir + } + return RemoveDir(path); +} + #endif // _WIN32 #ifdef UNDER_CE @@ -1144,17 +1182,15 @@ -bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime) +static bool SetFileTime_Base(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime, const int flags) { // need testing /* struct utimbuf buf; struct stat st; UNUSED_VAR(cTime) - printf("\nstat = %s\n", path); int ret = stat(path, &st); - if (ret == 0) { buf.actime = st.st_atime; @@ -1166,47 +1202,42 @@ buf.actime = cur_time; buf.modtime = cur_time; } - if (aTime) { UInt32 ut; if (NTime::FileTimeToUnixTime(*aTime, ut)) buf.actime = ut; } - if (mTime) { UInt32 ut; if (NTime::FileTimeToUnixTime(*mTime, ut)) buf.modtime = ut; } - return utime(path, &buf) == 0; */ // if (!aTime && !mTime) return true; - struct timespec times[2]; UNUSED_VAR(cTime) - bool needChange; needChange = FiTime_To_timespec(aTime, times[0]); needChange |= FiTime_To_timespec(mTime, times[1]); - - /* - if (mTime) - { - printf("\n time = %ld.%9ld\n", mTime->tv_sec, mTime->tv_nsec); - } - */ - + // if (mTime) { printf("\n time = %ld.%9ld\n", mTime->tv_sec, mTime->tv_nsec); } if (!needChange) return true; - const int flags = 0; // follow link - // = AT_SYMLINK_NOFOLLOW; // don't follow link return utimensat(AT_FDCWD, path, times, flags) == 0; } +bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime) +{ + return SetFileTime_Base(path, cTime, aTime, mTime, 0); // (flags = 0) means follow_link +} + +bool SetLinkFileTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime) +{ + return SetFileTime_Base(path, cTime, aTime, mTime, AT_SYMLINK_NOFOLLOW); +} struct C_umask diff -Nru 7zip-24.09+dfsg/CPP/Windows/FileDir.h 7zip-25.01+dfsg/CPP/Windows/FileDir.h --- 7zip-24.09+dfsg/CPP/Windows/FileDir.h 2024-10-17 07:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/Windows/FileDir.h 2025-07-24 13:00:00.000000000 +0000 @@ -18,9 +18,20 @@ WIN32 API : SetFileTime() doesn't allow to set zero timestamps in file but linux : allows unix time = 0 in filesystem */ - +/* +SetDirTime() can be used to set time for file or for dir. +If path is symbolic link, SetDirTime() will follow symbolic link, +and it will set timestamps of symbolic link's target file or dir. +*/ bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime); +/* +SetLinkFileTime() doesn't follow symbolic link, +and it sets timestamps for symbolic link file itself. +If (path) is not symbolic link, it still can work (at least in some new OS versions). +*/ +bool SetLinkFileTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime); + #ifdef _WIN32 @@ -78,6 +89,11 @@ bool DeleteFileAlways(CFSTR name); bool RemoveDirWithSubItems(const FString &path); +#ifdef _WIN32 +bool RemoveDirAlways_if_Empty(const FString &path); +#else +#define RemoveDirAlways_if_Empty RemoveDir +#endif bool MyGetFullPathName(CFSTR path, FString &resFullPath); bool GetFullPathAndSplit(CFSTR path, FString &resDirPrefix, FString &resFileName); diff -Nru 7zip-24.09+dfsg/CPP/Windows/FileFind.cpp 7zip-25.01+dfsg/CPP/Windows/FileFind.cpp --- 7zip-24.09+dfsg/CPP/Windows/FileFind.cpp 2024-02-12 10:47:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/Windows/FileFind.cpp 2025-06-20 11:00:00.000000000 +0000 @@ -731,7 +731,7 @@ bool isOK = false; if (finder.FindFirst(s, *this)) { - if (Name == FTEXT(".")) + if (Name.IsEqualTo(".")) { Name = path + prefixSize; return true; @@ -769,6 +769,13 @@ // return FollowReparse(path, IsDir()); return Fill_From_ByHandleFileInfo(path); +/* + // Fill_From_ByHandleFileInfo returns false (with Access Denied error), + // if there is reparse link file (not directory reparse item). + if (Fill_From_ByHandleFileInfo(path)) + return true; + return HasReparsePoint(); +*/ } bool CFileInfoBase::Fill_From_ByHandleFileInfo(CFSTR path) diff -Nru 7zip-24.09+dfsg/CPP/Windows/FileIO.h 7zip-25.01+dfsg/CPP/Windows/FileIO.h --- 7zip-24.09+dfsg/CPP/Windows/FileIO.h 2024-02-10 16:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/Windows/FileIO.h 2025-06-20 11:15:00.000000000 +0000 @@ -11,8 +11,7 @@ #define Z7_WIN_SYMLINK_FLAG_RELATIVE 1 -// what the meaning of that FLAG or field (2)? -#define Z7_WIN_LX_SYMLINK_FLAG 2 +#define Z7_WIN_LX_SYMLINK_VERSION_2 2 #ifdef _WIN32 @@ -44,7 +43,33 @@ namespace NFile { #if defined(_WIN32) && !defined(UNDER_CE) -bool FillLinkData(CByteBuffer &dest, const wchar_t *path, bool isSymLink, bool isWSL); +/* + in: (CByteBuffer &dest) is empty + in: (path) uses Windows path separator (\). + out: (path) uses Linux path separator (/). + if (isAbsPath == true), then "c:\\" prefix is replaced to "/mnt/c/" prefix +*/ +void Convert_WinPath_to_WslLinuxPath(FString &path, bool convertDrivePath); +// (path) must use Linux path separator (/). +void FillLinkData_WslLink(CByteBuffer &dest, const wchar_t *path); + +/* + in: (CByteBuffer &dest) is empty + if (isSymLink == false) : MOUNT_POINT : (path) must be absolute. + if (isSymLink == true) : SYMLINK : Windows + (path) must use Windows path separator (\). + (path) must be without link "\\??\\" prefix. + link "\\??\\" prefix will be added inside FillLinkData(), if path is absolute. +*/ +void FillLinkData_WinLink(CByteBuffer &dest, const wchar_t *path, bool isSymLink); +// in: (CByteBuffer &dest) is empty +inline void FillLinkData(CByteBuffer &dest, const wchar_t *path, bool isSymLink, bool isWSL) +{ + if (isWSL) + FillLinkData_WslLink(dest, path); + else + FillLinkData_WinLink(dest, path, isSymLink); +} #endif struct CReparseShortInfo @@ -61,7 +86,6 @@ UInt32 Flags; UString SubsName; UString PrintName; - AString WslName; bool HeaderError; @@ -71,8 +95,7 @@ CReparseAttr(): Tag(0), Flags(0) {} - // Parse() - // returns (true) and (ErrorCode = 0), if (it'a correct known link) + // returns (true) and (ErrorCode = 0), if (it's correct known link) // returns (false) and (ErrorCode = ERROR_REPARSE_TAG_INVALID), if unknown tag bool Parse(const Byte *p, size_t size); @@ -80,18 +103,14 @@ bool IsSymLink_Win() const { return Tag == Z7_WIN_IO_REPARSE_TAG_SYMLINK; } bool IsSymLink_WSL() const { return Tag == Z7_WIN_IO_REPARSE_TAG_LX_SYMLINK; } + // note: "/dir1/path" is marked as relative. bool IsRelative_Win() const { return Flags == Z7_WIN_SYMLINK_FLAG_RELATIVE; } bool IsRelative_WSL() const { - if (WslName.IsEmpty()) - return true; - char c = WslName[0]; - return !IS_PATH_SEPAR(c); + return WslName[0] != '/'; // WSL uses unix path separator } - // bool IsVolume() const; - bool IsOkNamePair() const; UString GetPath() const; }; diff -Nru 7zip-24.09+dfsg/CPP/Windows/FileLink.cpp 7zip-25.01+dfsg/CPP/Windows/FileLink.cpp --- 7zip-24.09+dfsg/CPP/Windows/FileLink.cpp 2023-09-05 06:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/Windows/FileLink.cpp 2025-06-20 11:00:00.000000000 +0000 @@ -39,12 +39,24 @@ using namespace NName; /* +Win10 Junctions/SymLinks: + - (/) slash doesn't work as path separator + - Win10 preinstalled junctions don't use tail backslash, but tail backslashes also work. + - double backslash works only after drive prefix "c:\\dir1\dir2\", + and doesn't work in another places. + - absolute path without \??\ prefix doesn't work + - absolute path "c:" doesn't work +*/ + +/* Reparse Points (Junctions and Symbolic Links): struct { UInt32 Tag; UInt16 Size; // not including starting 8 bytes - UInt16 Reserved; // = 0 + UInt16 Reserved; // = 0, DOCs: // Length, in bytes, of the unparsed portion of + // the file name pointed to by the FileName member of the associated file object. + // This member is only valid for create operations when the I/O fails with STATUS_REPARSE. UInt16 SubstituteOffset; // offset in bytes from start of namesChars UInt16 SubstituteLen; // size in bytes, it doesn't include tailed NUL @@ -68,6 +80,16 @@ 2) Default Order in table: Print Path Substitute Path + +DOCS: + The print name SHOULD be an informative pathname, suitable for display + to a user, that also identifies the target of the mount point. + Neither of these pathnames can contain dot directory names. + +reparse tags, with the exception of IO_REPARSE_TAG_SYMLINK, +are processed on the server and are not processed by a client +after transmission over the wire. +Clients SHOULD treat associated reparse data as opaque data. */ /* @@ -93,7 +115,8 @@ #define Get16(p) GetUi16(p) #define Get32(p) GetUi32(p) -static const wchar_t * const k_LinkPrefix = L"\\??\\"; +static const char * const k_LinkPrefix = "\\??\\"; +static const char * const k_LinkPrefix_UNC = "\\??\\UNC\\"; static const unsigned k_LinkPrefix_Size = 4; static bool IsLinkPrefix(const wchar_t *s) @@ -102,7 +125,7 @@ } /* -static const wchar_t * const k_VolumePrefix = L"Volume{"; +static const char * const k_VolumePrefix = "Volume{"; static const bool IsVolumeName(const wchar_t *s) { return IsString1PrefixedByString2(s, k_VolumePrefix); @@ -118,7 +141,7 @@ { for (;;) { - wchar_t c = *path++; + const wchar_t c = *path++; if (c == 0) return; Set16(dest, (UInt16)c) @@ -126,62 +149,103 @@ } } -bool FillLinkData(CByteBuffer &dest, const wchar_t *path, bool isSymLink, bool isWSL) +#ifdef _WIN32 +void Convert_WinPath_to_WslLinuxPath(FString &s, bool convertDrivePath) { - bool isAbs = IsAbsolutePath(path); - if (!isAbs && !isSymLink) - return false; - - if (isWSL) + if (convertDrivePath && IsDrivePath(s)) { - // unsupported characters probably use Replacement Character UTF-16 0xFFFD - AString utf; - ConvertUnicodeToUTF8(path, utf); - const size_t size = 4 + utf.Len(); - if (size != (UInt16)size) - return false; - dest.Alloc(8 + size); - Byte *p = dest; - Set32(p, Z7_WIN_IO_REPARSE_TAG_LX_SYMLINK) - Set16(p + 4, (UInt16)(size)) - Set16(p + 6, 0) - Set32(p + 8, Z7_WIN_LX_SYMLINK_FLAG) - memcpy(p + 12, utf.Ptr(), utf.Len()); - return true; + FChar c = s[0]; + c = MyCharLower_Ascii(c); + s.DeleteFrontal(2); + s.InsertAtFront(c); + s.Insert(0, FTEXT("/mnt/")); } + s.Replace(FCHAR_PATH_SEPARATOR, FTEXT('/')); +} +#endif - // usual symbolic LINK (NOT WSL) - bool needPrintName = true; +static const unsigned k_Link_Size_Limit = 1u << 16; // 16-bit field is used for size. + +void FillLinkData_WslLink(CByteBuffer &dest, const wchar_t *path) +{ + // dest.Free(); // it's empty already + // WSL probably uses Replacement Character UTF-16 0xFFFD for unsupported characters? + AString utf; + ConvertUnicodeToUTF8(path, utf); + const unsigned size = 4 + utf.Len(); + if (size >= k_Link_Size_Limit) + return; + dest.Alloc(8 + size); + Byte *p = dest; + Set32(p, Z7_WIN_IO_REPARSE_TAG_LX_SYMLINK) + // Set32(p + 4, (UInt32)size) + Set16(p + 4, (UInt16)size) + Set16(p + 6, 0) + Set32(p + 8, Z7_WIN_LX_SYMLINK_VERSION_2) + memcpy(p + 12, utf.Ptr(), utf.Len()); +} + - if (IsSuperPath(path)) +void FillLinkData_WinLink(CByteBuffer &dest, const wchar_t *path, bool isSymLink) +{ + // dest.Free(); // it's empty already + bool isAbs = false; + if (IS_PATH_SEPAR(path[0])) + { + // root paths "\dir1\path" are marked as relative + if (IS_PATH_SEPAR(path[1])) + isAbs = true; + } + else + isAbs = IsAbsolutePath(path); + if (!isAbs && !isSymLink) { - path += kSuperPathPrefixSize; - if (!IsDrivePath(path)) - needPrintName = false; + // Win10 allows us to create relative MOUNT_POINT. + // But relative MOUNT_POINT will not work when accessing it. + // So we prevent useless creation of a relative MOUNT_POINT. + return; } - const unsigned add_Prefix_Len = isAbs ? k_LinkPrefix_Size : 0; - + bool needPrintName = true; + UString subs (path); + if (isAbs) + { + const bool isSuperPath = IsSuperPath(path); + if (!isSuperPath && NName::IsNetworkPath(us2fs(path))) + { + subs = k_LinkPrefix_UNC; + subs += (path + 2); + } + else + { + if (isSuperPath) + { + // we remove super prefix: + path += kSuperPathPrefixSize; + // we want to get correct abolute path in PrintName still. + if (!IsDrivePath(path)) + needPrintName = false; // we need "\\server\path" for print name. + } + subs = k_LinkPrefix; + subs += path; + } + } + const size_t len1 = subs.Len() * 2; size_t len2 = (size_t)MyStringLen(path) * 2; - const size_t len1 = len2 + add_Prefix_Len * 2; if (!needPrintName) len2 = 0; - - size_t totalNamesSize = (len1 + len2); - + size_t totalNamesSize = len1 + len2; /* some WIM imagex software uses old scheme for symbolic links. - so we can old scheme for byte to byte compatibility */ - - bool newOrderScheme = isSymLink; + so we can use old scheme for byte to byte compatibility */ + const bool newOrderScheme = isSymLink; // newOrderScheme = false; - if (!newOrderScheme) - totalNamesSize += 2 * 2; + totalNamesSize += 2 * 2; // we use NULL terminators in old scheme. const size_t size = 8 + 8 + (isSymLink ? 4 : 0) + totalNamesSize; - if (size != (UInt16)size) - return false; + if (size >= k_Link_Size_Limit) + return; dest.Alloc(size); memset(dest, 0, size); const UInt32 tag = isSymLink ? @@ -189,6 +253,7 @@ Z7_WIN_IO_REPARSE_TAG_MOUNT_POINT; Byte *p = dest; Set32(p, tag) + // Set32(p + 4, (UInt32)(size - 8)) Set16(p + 4, (UInt16)(size - 8)) Set16(p + 6, 0) p += 8; @@ -204,21 +269,16 @@ Set16(p + 2, (UInt16)len1) Set16(p + 4, (UInt16)printOffs) Set16(p + 6, (UInt16)len2) - p += 8; if (isSymLink) { - UInt32 flags = isAbs ? 0 : Z7_WIN_SYMLINK_FLAG_RELATIVE; + const UInt32 flags = isAbs ? 0 : Z7_WIN_SYMLINK_FLAG_RELATIVE; Set32(p, flags) p += 4; } - - if (add_Prefix_Len != 0) - WriteString(p + subOffs, k_LinkPrefix); - WriteString(p + subOffs + add_Prefix_Len * 2, path); + WriteString(p + subOffs, subs); if (needPrintName) WriteString(p + printOffs, path); - return true; } #endif // defined(_WIN32) && !defined(UNDER_CE) @@ -230,7 +290,7 @@ unsigned i; for (i = 0; i < len; i++) { - wchar_t c = Get16(p + i * 2); + const wchar_t c = Get16(p + (size_t)i * 2); if (c == 0) break; s[i] = c; @@ -239,6 +299,7 @@ res.ReleaseBuf_SetLen(i); } + bool CReparseAttr::Parse(const Byte *p, size_t size) { ErrorCode = (DWORD)ERROR_INVALID_REPARSE_DATA; @@ -250,7 +311,12 @@ return false; Tag = Get32(p); if (Get16(p + 6) != 0) // padding - return false; + { + // DOCs: Reserved : the field SHOULD be set to 0 + // and MUST be ignored (by parser). + // Win10 ignores it. + MinorError = true; // optional + } unsigned len = Get16(p + 4); p += 8; size -= 8; @@ -262,8 +328,6 @@ (type & kReparseFlags_Microsoft) == 0 || (type & 0xFFFF) != 3) */ - - HeaderError = false; if ( Tag != Z7_WIN_IO_REPARSE_TAG_MOUNT_POINT @@ -282,8 +346,7 @@ { if (len < 4) return false; - Flags = Get32(p); // maybe it's not Flags - if (Flags != Z7_WIN_LX_SYMLINK_FLAG) + if (Get32(p) != Z7_WIN_LX_SYMLINK_VERSION_2) return false; len -= 4; p += 4; @@ -291,12 +354,13 @@ unsigned i; for (i = 0; i < len; i++) { - char c = (char)p[i]; + const char c = (char)p[i]; s[i] = c; if (c == 0) break; } - WslName.ReleaseBuf_SetEnd(i); + s[i] = 0; + WslName.ReleaseBuf_SetLen(i); MinorError = (i != len); ErrorCode = 0; return true; @@ -304,10 +368,10 @@ if (len < 8) return false; - unsigned subOffs = Get16(p); - unsigned subLen = Get16(p + 2); - unsigned printOffs = Get16(p + 4); - unsigned printLen = Get16(p + 6); + const unsigned subOffs = Get16(p); + const unsigned subLen = Get16(p + 2); + const unsigned printOffs = Get16(p + 4); + const unsigned printLen = Get16(p + 6); len -= 8; p += 8; @@ -335,15 +399,17 @@ bool CReparseShortInfo::Parse(const Byte *p, size_t size) { - const Byte *start = p; - Offset= 0; + const Byte * const start = p; + Offset = 0; Size = 0; if (size < 8) return false; - UInt32 Tag = Get32(p); + const UInt32 Tag = Get32(p); UInt32 len = Get16(p + 4); + /* if (len + 8 > size) return false; + */ /* if ((type & kReparseFlags_Alias) == 0 || (type & kReparseFlags_Microsoft) == 0 || @@ -353,16 +419,14 @@ Tag != Z7_WIN_IO_REPARSE_TAG_SYMLINK) // return true; return false; - + /* if (Get16(p + 6) != 0) // padding return false; - + */ p += 8; size -= 8; - if (len != size) // do we need that check? return false; - if (len < 8) return false; unsigned subOffs = Get16(p); @@ -396,10 +460,14 @@ { if (IsLinkPrefix(SubsName)) { + if (PrintName == GetPath()) + return true; +/* if (!IsDrivePath(SubsName.Ptr(k_LinkPrefix_Size))) return PrintName.IsEmpty(); if (wcscmp(SubsName.Ptr(k_LinkPrefix_Size), PrintName) == 0) return true; +*/ } return wcscmp(SubsName, PrintName) == 0; } @@ -415,21 +483,26 @@ UString CReparseAttr::GetPath() const { + UString s (SubsName); if (IsSymLink_WSL()) { - UString u; // if (CheckUTF8(attr.WslName) - if (!ConvertUTF8ToUnicode(WslName, u)) - MultiByteToUnicodeString2(u, WslName); - return u; + if (!ConvertUTF8ToUnicode(WslName, s)) + MultiByteToUnicodeString2(s, WslName); } - - UString s (SubsName); - if (IsLinkPrefix(s)) + else if (IsLinkPrefix(s)) { - s.ReplaceOneCharAtPos(1, '\\'); // we normalize prefix from "\??\" to "\\?\" - if (IsDrivePath(s.Ptr(k_LinkPrefix_Size))) - s.DeleteFrontal(k_LinkPrefix_Size); + if (IsString1PrefixedByString2_NoCase_Ascii(s.Ptr(), k_LinkPrefix_UNC)) + { + s.DeleteFrontal(6); + s.ReplaceOneCharAtPos(0, '\\'); + } + else + { + s.ReplaceOneCharAtPos(1, '\\'); // we normalize prefix from "\??\" to "\\?\" + if (IsDrivePath(s.Ptr(k_LinkPrefix_Size))) + s.DeleteFrontal(k_LinkPrefix_Size); + } } return s; } @@ -468,7 +541,7 @@ static bool CreatePrefixDirOfFile(CFSTR path) { FString path2 (path); - int pos = path2.ReverseFind_PathSepar(); + const int pos = path2.ReverseFind_PathSepar(); if (pos < 0) return true; #ifdef _WIN32 @@ -494,6 +567,8 @@ } +// MOUNT_POINT (Junction Point) and LX_SYMLINK (WSL) can be written without administrator rights. +// SYMLINK requires administrator rights. // If there is Reparse data already, it still writes new Reparse data bool SetReparseData(CFSTR path, bool isDir, const void *data, DWORD size) { @@ -540,10 +615,11 @@ SetLastError(ERROR_INVALID_REPARSE_DATA); return false; } - BYTE buf[my_REPARSE_DATA_BUFFER_HEADER_SIZE]; - memset(buf, 0, sizeof(buf)); - memcpy(buf, reparseData, 4); // tag - return OutIoReparseData(my_FSCTL_DELETE_REPARSE_POINT, path, buf, sizeof(buf)); + // BYTE buf[my_REPARSE_DATA_BUFFER_HEADER_SIZE]; + // memset(buf, 0, sizeof(buf)); + // memcpy(buf, reparseData, 4); // tag + memset(reparseData + 4, 0, my_REPARSE_DATA_BUFFER_HEADER_SIZE - 4); + return OutIoReparseData(my_FSCTL_DELETE_REPARSE_POINT, path, reparseData, my_REPARSE_DATA_BUFFER_HEADER_SIZE); } } diff -Nru 7zip-24.09+dfsg/CPP/Windows/FileName.cpp 7zip-25.01+dfsg/CPP/Windows/FileName.cpp --- 7zip-24.09+dfsg/CPP/Windows/FileName.cpp 2024-10-06 10:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/Windows/FileName.cpp 2025-06-14 13:39:00.000000000 +0000 @@ -65,8 +65,15 @@ dirPath.Add_PathSepar(); } + +#define IS_LETTER_CHAR(c) ((((unsigned)(int)(c) | 0x20) - (unsigned)'a' <= (unsigned)('z' - 'a'))) +bool IsDrivePath (const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && IS_SEPAR(s[2]); } +// bool IsDriveName2(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && s[2] == 0; } + #ifdef _WIN32 +bool IsDrivePath2(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':'; } + #ifndef USE_UNICODE_FSTRING #ifdef Z7_LONG_PATH static void NormalizeDirSeparators(UString &s) @@ -87,13 +94,6 @@ s.ReplaceOneCharAtPos(i, FCHAR_PATH_SEPARATOR); } -#endif - - -#define IS_LETTER_CHAR(c) ((((unsigned)(int)(c) | 0x20) - (unsigned)'a' <= (unsigned)('z' - 'a'))) - -bool IsDrivePath(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && IS_SEPAR(s[2]); } - bool IsAltPathPrefix(CFSTR s) throw() { unsigned len = MyStringLen(s); @@ -117,16 +117,23 @@ return true; } -#if defined(_WIN32) && !defined(UNDER_CE) +#endif // _WIN32 + -const char * const kSuperPathPrefix = "\\\\?\\"; +const char * const kSuperPathPrefix = + STRING_PATH_SEPARATOR + STRING_PATH_SEPARATOR "?" + STRING_PATH_SEPARATOR; #ifdef Z7_LONG_PATH -static const char * const kSuperUncPrefix = "\\\\?\\UNC\\"; +static const char * const kSuperUncPrefix = + STRING_PATH_SEPARATOR + STRING_PATH_SEPARATOR "?" + STRING_PATH_SEPARATOR "UNC" + STRING_PATH_SEPARATOR; #endif #define IS_DEVICE_PATH(s) (IS_SEPAR((s)[0]) && IS_SEPAR((s)[1]) && (s)[2] == '.' && IS_SEPAR((s)[3])) #define IS_SUPER_PREFIX(s) (IS_SEPAR((s)[0]) && IS_SEPAR((s)[1]) && (s)[2] == '?' && IS_SEPAR((s)[3])) -#define IS_SUPER_OR_DEVICE_PATH(s) (IS_SEPAR((s)[0]) && IS_SEPAR((s)[1]) && ((s)[2] == '?' || (s)[2] == '.') && IS_SEPAR((s)[3])) #define IS_UNC_WITH_SLASH(s) ( \ ((s)[0] == 'U' || (s)[0] == 'u') \ @@ -134,6 +141,16 @@ && ((s)[2] == 'C' || (s)[2] == 'c') \ && IS_SEPAR((s)[3])) +static const unsigned kDrivePrefixSize = 3; /* c:\ */ + +bool IsSuperPath(const wchar_t *s) throw(); +bool IsSuperPath(const wchar_t *s) throw() { return IS_SUPER_PREFIX(s); } +// bool IsSuperUncPath(const wchar_t *s) throw() { return (IS_SUPER_PREFIX(s) && IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize)); } + +#if defined(_WIN32) && !defined(UNDER_CE) + +#define IS_SUPER_OR_DEVICE_PATH(s) (IS_SEPAR((s)[0]) && IS_SEPAR((s)[1]) && ((s)[2] == '?' || (s)[2] == '.') && IS_SEPAR((s)[3])) +bool IsSuperOrDevicePath(const wchar_t *s) throw() { return IS_SUPER_OR_DEVICE_PATH(s); } bool IsDevicePath(CFSTR s) throw() { #ifdef UNDER_CE @@ -154,7 +171,7 @@ if (!IS_DEVICE_PATH(s)) return false; - unsigned len = MyStringLen(s); + const unsigned len = MyStringLen(s); if (len == 6 && s[5] == ':') return true; if (len < 18 || len > 22 || !IsString1PrefixedByString2(s + kDevicePathPrefixSize, "PhysicalDrive")) @@ -174,7 +191,7 @@ return false; if (IsSuperUncPath(s)) return true; - FChar c = s[2]; + const FChar c = s[2]; return (c != '.' && c != '?'); } @@ -187,7 +204,7 @@ prefixSize = kSuperUncPathPrefixSize; else { - FChar c = s[2]; + const FChar c = s[2]; if (c == '.' || c == '?') return 0; } @@ -209,14 +226,6 @@ return s[(unsigned)pos + 1] == 0; } -static const unsigned kDrivePrefixSize = 3; /* c:\ */ - -bool IsDrivePath2(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':'; } -// bool IsDriveName2(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && s[2] == 0; } -bool IsSuperPath(const wchar_t *s) throw() { return IS_SUPER_PREFIX(s); } -bool IsSuperOrDevicePath(const wchar_t *s) throw() { return IS_SUPER_OR_DEVICE_PATH(s); } -// bool IsSuperUncPath(const wchar_t *s) throw() { return (IS_SUPER_PREFIX(s) && IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize)); } - bool IsAltStreamPrefixWithColon(const UString &s) throw() { if (s.IsEmpty()) @@ -349,14 +358,16 @@ } #endif // USE_UNICODE_FSTRING +#endif // _WIN32 + static unsigned GetRootPrefixSize_Of_NetworkPath(const wchar_t *s) throw() { // Network path: we look "server\path\" as root prefix - int pos = FindSepar(s); + const int pos = FindSepar(s); if (pos < 0) return 0; - int pos2 = FindSepar(s + (unsigned)pos + 1); + const int pos2 = FindSepar(s + (unsigned)pos + 1); if (pos2 < 0) return 0; return (unsigned)(pos + pos2 + 2); @@ -370,7 +381,7 @@ return 0; if (s[1] == 0 || !IS_SEPAR(s[1])) return 1; - unsigned size = GetRootPrefixSize_Of_NetworkPath(s + 2); + const unsigned size = GetRootPrefixSize_Of_NetworkPath(s + 2); return (size == 0) ? 0 : 2 + size; } @@ -378,17 +389,21 @@ { if (IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize)) { - unsigned size = GetRootPrefixSize_Of_NetworkPath(s + kSuperUncPathPrefixSize); + const unsigned size = GetRootPrefixSize_Of_NetworkPath(s + kSuperUncPathPrefixSize); return (size == 0) ? 0 : kSuperUncPathPrefixSize + size; } // we support \\?\c:\ paths and volume GUID paths \\?\Volume{GUID}\" - int pos = FindSepar(s + kSuperPathPrefixSize); + const int pos = FindSepar(s + kSuperPathPrefixSize); if (pos < 0) return 0; return kSuperPathPrefixSize + (unsigned)(pos + 1); } +#ifdef _WIN32 unsigned GetRootPrefixSize(const wchar_t *s) throw() +#else +unsigned GetRootPrefixSize_WINDOWS(const wchar_t *s) throw() +#endif { if (IS_DEVICE_PATH(s)) return kDevicePathPrefixSize; @@ -397,7 +412,7 @@ return GetRootPrefixSize_Of_SimplePath(s); } -#else // _WIN32 +#ifndef _WIN32 bool IsAbsolutePath(const wchar_t *s) throw() { return IS_SEPAR(s[0]); } diff -Nru 7zip-24.09+dfsg/CPP/Windows/FileName.h 7zip-25.01+dfsg/CPP/Windows/FileName.h --- 7zip-24.09+dfsg/CPP/Windows/FileName.h 2023-04-24 18:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/Windows/FileName.h 2025-06-20 11:00:00.000000000 +0000 @@ -25,13 +25,13 @@ bool IsAltPathPrefix(CFSTR s) throw(); /* name: */ -#if defined(_WIN32) && !defined(UNDER_CE) - extern const char * const kSuperPathPrefix; /* \\?\ */ const unsigned kDevicePathPrefixSize = 4; const unsigned kSuperPathPrefixSize = 4; const unsigned kSuperUncPathPrefixSize = kSuperPathPrefixSize + 4; +#if defined(_WIN32) && !defined(UNDER_CE) + bool IsDevicePath(CFSTR s) throw(); /* \\.\ */ bool IsSuperUncPath(CFSTR s) throw(); /* \\?\UNC\ */ bool IsNetworkPath(CFSTR s) throw(); /* \\?\UNC\ or \\SERVER */ @@ -86,6 +86,15 @@ bool IsAbsolutePath(const wchar_t *s) throw(); unsigned GetRootPrefixSize(const wchar_t *s) throw(); +#ifndef _WIN32 +/* GetRootPrefixSize_WINDOWS() is called in linux, but it parses path by windows rules. + It supports only paths system (linux) slash separators (STRING_PATH_SEPARATOR), + It doesn't parses paths with backslash (windows) separators. + "c:/dir/file" is supported. +*/ +unsigned GetRootPrefixSize_WINDOWS(const wchar_t *s) throw(); +#endif + #ifdef Z7_LONG_PATH const int kSuperPathType_UseOnlyMain = 0; diff -Nru 7zip-24.09+dfsg/CPP/Windows/System.cpp 7zip-25.01+dfsg/CPP/Windows/System.cpp --- 7zip-24.09+dfsg/CPP/Windows/System.cpp 2024-10-24 05:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/Windows/System.cpp 2025-06-30 15:00:00.000000000 +0000 @@ -25,6 +25,69 @@ #ifdef _WIN32 +/* +note: returned value in 32-bit version can be limited by value 32. + while 64-bit version returns full value. +GetMaximumProcessorCount(groupNumber) can return higher value than +GetActiveProcessorCount(groupNumber) in some cases, because CPUs can be added. +*/ +// typedef DWORD (WINAPI *Func_GetMaximumProcessorCount)(WORD GroupNumber); +typedef DWORD (WINAPI *Func_GetActiveProcessorCount)(WORD GroupNumber); +typedef WORD (WINAPI *Func_GetActiveProcessorGroupCount)(VOID); +/* +#if 0 && defined(ALL_PROCESSOR_GROUPS) +#define MY_ALL_PROCESSOR_GROUPS ALL_PROCESSOR_GROUPS +#else +#define MY_ALL_PROCESSOR_GROUPS 0xffff +#endif +*/ + +Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION + +bool CCpuGroups::Load() +{ + NumThreadsTotal = 0; + GroupSizes.Clear(); + const HMODULE hmodule = ::GetModuleHandleA("kernel32.dll"); + // Is_Win11_Groups = GetProcAddress(hmodule, "SetThreadSelectedCpuSetMasks") != NULL; + const + Func_GetActiveProcessorGroupCount + fn_GetActiveProcessorGroupCount = Z7_GET_PROC_ADDRESS( + Func_GetActiveProcessorGroupCount, hmodule, + "GetActiveProcessorGroupCount"); + const + Func_GetActiveProcessorCount + fn_GetActiveProcessorCount = Z7_GET_PROC_ADDRESS( + Func_GetActiveProcessorCount, hmodule, + "GetActiveProcessorCount"); + if (!fn_GetActiveProcessorGroupCount || + !fn_GetActiveProcessorCount) + return false; + + const unsigned numGroups = fn_GetActiveProcessorGroupCount(); + if (numGroups == 0) + return false; + UInt32 sum = 0; + for (unsigned i = 0; i < numGroups; i++) + { + const UInt32 num = fn_GetActiveProcessorCount((WORD)i); + /* + if (num == 0) + { + // it means error + // but is it possible that some group is empty by some reason? + // GroupSizes.Clear(); + // return false; + } + */ + sum += num; + GroupSizes.Add(num); + } + NumThreadsTotal = sum; + // NumThreadsTotal = fn_GetActiveProcessorCount(MY_ALL_PROCESSOR_GROUPS); + return true; +} + UInt32 CountAffinity(DWORD_PTR mask) { UInt32 num = 0; @@ -38,31 +101,62 @@ BOOL CProcessAffinity::Get() { - #ifndef UNDER_CE - return GetProcessAffinityMask(GetCurrentProcess(), &processAffinityMask, &systemAffinityMask); - #else - return FALSE; - #endif + IsGroupMode = false; + Groups.Load(); + // SetThreadAffinityMask(GetCurrentThread(), 1); + // SetProcessAffinityMask(GetCurrentProcess(), 1); + BOOL res = GetProcessAffinityMask(GetCurrentProcess(), + &processAffinityMask, &systemAffinityMask); + /* DOCs: On a system with more than 64 processors, if the threads + of the calling process are in a single processor group, the + function sets the variables pointed to by lpProcessAffinityMask + and lpSystemAffinityMask to the process affinity mask and the + processor mask of active logical processors for that group. + If the calling process contains threads in multiple groups, + the function returns zero for both affinity masks + + note: tested in Win10: GetProcessAffinityMask() doesn't return 0 + in (processAffinityMask) and (systemAffinityMask) masks. + We need to test it in Win11: how to get mask==0 from GetProcessAffinityMask()? + */ + if (!res) + { + processAffinityMask = 0; + systemAffinityMask = 0; + } + if (Groups.GroupSizes.Size() > 1 && Groups.NumThreadsTotal) + if (// !res || + processAffinityMask == 0 || // to support case described in DOCs and for (!res) case + processAffinityMask == systemAffinityMask) // for default nonchanged affinity + { + // we set IsGroupMode only if processAffinity is default (not changed). + res = TRUE; + IsGroupMode = true; + } + return res; } +UInt32 CProcessAffinity::Load_and_GetNumberOfThreads() +{ + if (Get()) + { + const UInt32 numProcessors = GetNumProcessThreads(); + if (numProcessors) + return numProcessors; + } + SYSTEM_INFO systemInfo; + GetSystemInfo(&systemInfo); + // the number of logical processors in the current group + return systemInfo.dwNumberOfProcessors; +} + UInt32 GetNumberOfProcessors() { // We need to know how many threads we can use. // By default the process is assigned to one group. - // So we get the number of logical processors (threads) - // assigned to current process in the current group. - // Group size can be smaller than total number logical processors, for exammple, 2x36 - CProcessAffinity pa; - - if (pa.Get() && pa.processAffinityMask != 0) - return pa.GetNumProcessThreads(); - - SYSTEM_INFO systemInfo; - GetSystemInfo(&systemInfo); - // the number of logical processors in the current group - return (UInt32)systemInfo.dwNumberOfProcessors; + return pa.Load_and_GetNumberOfThreads(); } #else diff -Nru 7zip-24.09+dfsg/CPP/Windows/System.h 7zip-25.01+dfsg/CPP/Windows/System.h --- 7zip-24.09+dfsg/CPP/Windows/System.h 2024-10-02 08:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/Windows/System.h 2025-06-30 10:00:00.000000000 +0000 @@ -9,6 +9,7 @@ #endif #include "../Common/MyTypes.h" +#include "../Common/MyVector.h" #include "../Common/MyWindows.h" namespace NWindows { @@ -16,6 +17,34 @@ #ifdef _WIN32 +struct CCpuGroups +{ + CRecordVector GroupSizes; + UInt32 NumThreadsTotal; // sum of threads in all groups + // bool Is_Win11_Groups; // useless + + void Get_GroupSize_Min_Max(UInt32 &minSize, UInt32 &maxSize) const + { + unsigned num = GroupSizes.Size(); + UInt32 minSize2 = 0, maxSize2 = 0; + if (num) + { + minSize2 = (UInt32)0 - 1; + do + { + const UInt32 v = GroupSizes[--num]; + if (minSize2 > v) minSize2 = v; + if (maxSize2 < v) maxSize2 = v; + } + while (num); + } + minSize = minSize2; + maxSize = maxSize2; + } + bool Load(); + CCpuGroups(): NumThreadsTotal(0) {} +}; + UInt32 CountAffinity(DWORD_PTR mask); struct CProcessAffinity @@ -25,14 +54,28 @@ DWORD_PTR processAffinityMask; DWORD_PTR systemAffinityMask; + CCpuGroups Groups; + bool IsGroupMode; + /* + IsGroupMode == true, if + Groups.GroupSizes.Size() > 1) && { dafalt affinity was not changed } + IsGroupMode == false, if single group or affinity was changed + */ + + UInt32 Load_and_GetNumberOfThreads(); + void InitST() { // numProcessThreads = 1; // numSysThreads = 1; processAffinityMask = 1; systemAffinityMask = 1; + IsGroupMode = false; + // Groups.NumThreadsTotal = 0; + // Groups.Is_Win11_Groups = false; } +/* void CpuZero() { processAffinityMask = 0; @@ -42,9 +85,23 @@ { processAffinityMask |= ((DWORD_PTR)1 << cpuIndex); } +*/ - UInt32 GetNumProcessThreads() const { return CountAffinity(processAffinityMask); } - UInt32 GetNumSystemThreads() const { return CountAffinity(systemAffinityMask); } + UInt32 GetNumProcessThreads() const + { + if (IsGroupMode) + return Groups.NumThreadsTotal; + // IsGroupMode == false + // so we don't want to use groups + // we return number of threads in default primary group: + return CountAffinity(processAffinityMask); + } + UInt32 GetNumSystemThreads() const + { + if (Groups.GroupSizes.Size() > 1 && Groups.NumThreadsTotal) + return Groups.NumThreadsTotal; + return CountAffinity(systemAffinityMask); + } BOOL Get(); diff -Nru 7zip-24.09+dfsg/CPP/Windows/Thread.h 7zip-25.01+dfsg/CPP/Windows/Thread.h --- 7zip-24.09+dfsg/CPP/Windows/Thread.h 2023-03-04 09:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/Windows/Thread.h 2025-06-30 15:00:00.000000000 +0000 @@ -26,8 +26,10 @@ { return Thread_Create_With_Affinity(&thread, startAddress, param, affinity); } WRes Create_With_CpuSet(THREAD_FUNC_TYPE startAddress, LPVOID param, const CCpuSet *cpuSet) { return Thread_Create_With_CpuSet(&thread, startAddress, param, cpuSet); } - - #ifdef _WIN32 + +#ifdef _WIN32 + WRes Create_With_Group(THREAD_FUNC_TYPE startAddress, LPVOID param, unsigned group, CAffinityMask affinity = 0) + { return Thread_Create_With_Group(&thread, startAddress, param, group, affinity); } operator HANDLE() { return thread; } void Attach(HANDLE handle) { thread = handle; } HANDLE Detach() { HANDLE h = thread; thread = NULL; return h; } @@ -36,7 +38,7 @@ bool Terminate(DWORD exitCode) { return BOOLToBool(::TerminateThread(thread, exitCode)); } int GetPriority() { return ::GetThreadPriority(thread); } bool SetPriority(int priority) { return BOOLToBool(::SetThreadPriority(thread, priority)); } - #endif +#endif }; } diff -Nru 7zip-24.09+dfsg/CPP/Windows/TimeUtils.cpp 7zip-25.01+dfsg/CPP/Windows/TimeUtils.cpp --- 7zip-24.09+dfsg/CPP/Windows/TimeUtils.cpp 2024-04-13 09:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/CPP/Windows/TimeUtils.cpp 2025-06-12 08:00:00.000000000 +0000 @@ -258,8 +258,9 @@ FreeBSD 11.0, NetBSD 7.1, OpenBSD 6.0, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, Solaris 11.3, Cygwin 2.9, mingw, MSVC 14, Android 9.0. + Android NDK defines TIME_UTC but doesn't have the timespec_get(). */ -#if defined(TIME_UTC) +#if defined(TIME_UTC) && !defined(__ANDROID__) #define ZIP7_USE_timespec_get // #pragma message("ZIP7_USE_timespec_get") #elif defined(CLOCK_REALTIME) diff -Nru 7zip-24.09+dfsg/DOC/7zip.wxs 7zip-25.01+dfsg/DOC/7zip.wxs --- 7zip-24.09+dfsg/DOC/7zip.wxs 2024-11-28 15:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/DOC/7zip.wxs 2025-08-03 11:00:00.000000000 +0000 @@ -1,7 +1,7 @@ - - + + diff -Nru 7zip-24.09+dfsg/DOC/License.txt 7zip-25.01+dfsg/DOC/License.txt --- 7zip-24.09+dfsg/DOC/License.txt 2024-01-28 14:22:46.000000000 +0000 +++ 7zip-25.01+dfsg/DOC/License.txt 2025-07-05 13:56:54.000000000 +0000 @@ -3,7 +3,7 @@ License for use and distribution ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 7-Zip Copyright (C) 1999-2024 Igor Pavlov. + 7-Zip Copyright (C) 1999-2025 Igor Pavlov. The licenses for files are: @@ -58,7 +58,7 @@ Copyright (c) 2015-2016, Apple Inc. All rights reserved. Copyright (c) Facebook, Inc. All rights reserved. - Copyright (c) 2023-2024 Igor Pavlov. + Copyright (c) 2023-2025 Igor Pavlov. Text of the "BSD 3-clause License" ---------------------------------- @@ -102,7 +102,7 @@ XXH64 code in 7-Zip was derived from the original XXH64 code developed by Yann Collet. Copyright (c) 2012-2021 Yann Collet. - Copyright (c) 2023-2024 Igor Pavlov. + Copyright (c) 2023-2025 Igor Pavlov. Text of the "BSD 2-clause License" ---------------------------------- diff -Nru 7zip-24.09+dfsg/DOC/readme.txt 7zip-25.01+dfsg/DOC/readme.txt --- 7zip-24.09+dfsg/DOC/readme.txt 2024-11-29 16:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/DOC/readme.txt 2025-08-03 11:00:00.000000000 +0000 @@ -1,9 +1,9 @@ -7-Zip 24.09 Sources +7-Zip 25.01 Sources ------------------- 7-Zip is a file archiver for Windows. -7-Zip Copyright (C) 1999-2024 Igor Pavlov. +7-Zip Copyright (C) 1999-2025 Igor Pavlov. License Info @@ -73,8 +73,8 @@ optimization options. -How to compile with makefile ----------------------------- +How to compile with makefile in Windows +--------------------------------------- Some macronames can be defined for compiling with makefile: @@ -88,6 +88,23 @@ for dynamic linking to the run-time library (msvcrt.dll). The default makefile option is static linking to the run-time library. +To compile all 7-Zip files for x64 with Visual Studio 2022, +use the following command sequence: + + cd SRC\CPP\7zip + %comspec% /k "C:\Program Files\VS2022\VC\Auxiliary\Build\vcvars64.bat" + nmake + +You can use another "vcvars*.bat" files from "VS2022\VC\Auxiliary\Build" directory +to compile for other platforms: + vcvars64.bat + vcvarsamd64_arm64.bat + vcvarsamd64_x86.bat + +Also you can compile single binary from directory with related project. +For example, to compile 7za.exe, use the following command sequence: + cd SRC\CPP\7zip\Bundles\Alone\ + nmake Compiling 7-Zip for Unix/Linux diff -Nru 7zip-24.09+dfsg/DOC/src-history.txt 7zip-25.01+dfsg/DOC/src-history.txt --- 7zip-24.09+dfsg/DOC/src-history.txt 2024-11-29 16:00:00.000000000 +0000 +++ 7zip-25.01+dfsg/DOC/src-history.txt 2025-08-03 11:00:00.000000000 +0000 @@ -1,6 +1,28 @@ HISTORY of the 7-Zip source code -------------------------------- +25.01 2025-08-03 +------------------------- +- The code for handling symbolic links has been changed + to provide greater security when extracting files from archives. + Command line switch -snld20 can be used to bypass default security + checks when creating symbolic links. + + +25.00 2025-07-05 +------------------------- +- 7-Zip for Windows can now use more than 64 CPU threads for compression + to zip/7z/xz archives and for the 7-Zip benchmark. + If there are more than one processor group in Windows (on systems with more than + 64 cpu threads), 7-Zip distributes running CPU threads across different processor groups. +- bzip2 compression speed was increased by 15-40%. +- deflate (zip/gz) compression speed was increased by 1-3%. +- improved support for zip, cpio and fat archives. +- fixed some bugs and vulnerabilities. +- the bug was fixed : CVE-2025-53816 : 7-Zip could work incorrectly for some incorrect RAR archives. +- the bug was fixed : CVE-2025-53817 : 7-Zip could crash for some incorrect COM (Compound File) archives. + + 24.09 2024-11-29 ------------------------- - The default dictionary size values for LZMA/LZMA2 compression methods were increased: diff -Nru 7zip-24.09+dfsg/debian/changelog 7zip-25.01+dfsg/debian/changelog --- 7zip-24.09+dfsg/debian/changelog 2025-05-08 10:46:16.000000000 +0000 +++ 7zip-25.01+dfsg/debian/changelog 2025-10-13 15:21:03.000000000 +0000 @@ -1,3 +1,10 @@ +7zip (25.01+dfsg-1~deb13u1) trixie; urgency=medium + + * New upstream version 25.01+dfsg + * Fix CVE-2025-55188, CVE-2025-11002, CVE-2025-11001 + + -- YOKOTA Hiroshi Tue, 14 Oct 2025 00:21:03 +0900 + 7zip (24.09+dfsg-8) unstable; urgency=medium [ Roger Shimizu ] diff -Nru 7zip-24.09+dfsg/debian/patches/0001-Accept-Debian-build-flags.patch 7zip-25.01+dfsg/debian/patches/0001-Accept-Debian-build-flags.patch --- 7zip-24.09+dfsg/debian/patches/0001-Accept-Debian-build-flags.patch 2025-02-26 05:57:23.000000000 +0000 +++ 7zip-25.01+dfsg/debian/patches/0001-Accept-Debian-build-flags.patch 2025-10-13 15:21:03.000000000 +0000 @@ -8,7 +8,7 @@ 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CPP/7zip/7zip_gcc.mak b/CPP/7zip/7zip_gcc.mak -index fcb580a..51c73b5 100644 +index 8fbef14..2756ba4 100644 --- a/CPP/7zip/7zip_gcc.mak +++ b/CPP/7zip/7zip_gcc.mak @@ -45,7 +45,7 @@ CFLAGS_DEBUG = -g diff -Nru 7zip-24.09+dfsg/debian/patches/0002-Use-getcwd-3-POSIX-extension-to-avoid-PATH_MAX-macro.patch 7zip-25.01+dfsg/debian/patches/0002-Use-getcwd-3-POSIX-extension-to-avoid-PATH_MAX-macro.patch --- 7zip-24.09+dfsg/debian/patches/0002-Use-getcwd-3-POSIX-extension-to-avoid-PATH_MAX-macro.patch 2025-02-26 05:57:23.000000000 +0000 +++ 7zip-25.01+dfsg/debian/patches/0002-Use-getcwd-3-POSIX-extension-to-avoid-PATH_MAX-macro.patch 2025-10-13 15:21:03.000000000 +0000 @@ -10,10 +10,10 @@ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/CPP/Windows/FileDir.cpp b/CPP/Windows/FileDir.cpp -index 5063568..f1efd80 100644 +index 4a4bf52..e1747fc 100644 --- a/CPP/Windows/FileDir.cpp +++ b/CPP/Windows/FileDir.cpp -@@ -1103,22 +1103,11 @@ bool GetCurrentDir(FString &path) +@@ -1141,22 +1141,11 @@ bool GetCurrentDir(FString &path) { path.Empty(); diff -Nru 7zip-24.09+dfsg/debian/patches/0005-Add-note-for-unexpected-recursive-operations-behavio.patch 7zip-25.01+dfsg/debian/patches/0005-Add-note-for-unexpected-recursive-operations-behavio.patch --- 7zip-24.09+dfsg/debian/patches/0005-Add-note-for-unexpected-recursive-operations-behavio.patch 2025-03-31 15:09:57.000000000 +0000 +++ 7zip-25.01+dfsg/debian/patches/0005-Add-note-for-unexpected-recursive-operations-behavio.patch 2025-10-13 15:21:03.000000000 +0000 @@ -9,7 +9,7 @@ 1 file changed, 4 insertions(+) diff --git a/CPP/7zip/UI/Console/Main.cpp b/CPP/7zip/UI/Console/Main.cpp -index 4fa5c35..86cffb3 100644 +index 5094452..7511322 100644 --- a/CPP/7zip/UI/Console/Main.cpp +++ b/CPP/7zip/UI/Console/Main.cpp @@ -133,6 +133,10 @@ static const char * const kHelpString = diff -Nru 7zip-24.09+dfsg/debian/patches/0006-Use-c-flags-for-asmc.patch 7zip-25.01+dfsg/debian/patches/0006-Use-c-flags-for-asmc.patch --- 7zip-24.09+dfsg/debian/patches/0006-Use-c-flags-for-asmc.patch 2025-03-31 15:09:57.000000000 +0000 +++ 7zip-25.01+dfsg/debian/patches/0006-Use-c-flags-for-asmc.patch 2025-10-13 15:21:03.000000000 +0000 @@ -8,7 +8,7 @@ 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CPP/7zip/7zip_gcc.mak b/CPP/7zip/7zip_gcc.mak -index 51c73b5..4b0cc27 100644 +index 2756ba4..5d05cf0 100644 --- a/CPP/7zip/7zip_gcc.mak +++ b/CPP/7zip/7zip_gcc.mak @@ -194,7 +194,7 @@ AFLAGS_ABI = -elf -DABI_LINUX -DABI_CDECL diff -Nru 7zip-24.09+dfsg/debian/patches/0007-Add-fpic-for-Asmc-options.patch 7zip-25.01+dfsg/debian/patches/0007-Add-fpic-for-Asmc-options.patch --- 7zip-24.09+dfsg/debian/patches/0007-Add-fpic-for-Asmc-options.patch 2025-03-31 15:09:57.000000000 +0000 +++ 7zip-25.01+dfsg/debian/patches/0007-Add-fpic-for-Asmc-options.patch 2025-10-13 15:21:03.000000000 +0000 @@ -9,7 +9,7 @@ 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CPP/7zip/7zip_gcc.mak b/CPP/7zip/7zip_gcc.mak -index 4b0cc27..792ad13 100644 +index 5d05cf0..3fada36 100644 --- a/CPP/7zip/7zip_gcc.mak +++ b/CPP/7zip/7zip_gcc.mak @@ -194,7 +194,7 @@ AFLAGS_ABI = -elf -DABI_LINUX -DABI_CDECL