From d8c22c55da4af8046f6797ffa2c3254ea51cb77b Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sun, 1 Mar 2026 19:51:37 +0800 Subject: [PATCH] [Deflate][Huffman] Filter out zero code lengths only during code construction This should prevent crashes described in issue #57. --- SWCompression.xcodeproj/project.pbxproj | 4 ---- Sources/Common/CodingTree/Code.swift | 4 +--- Sources/Deflate/Deflate+Lengths.swift | 18 ------------------ Sources/Deflate/Deflate.swift | 4 ++++ 4 files changed, 5 insertions(+), 25 deletions(-) delete mode 100644 Sources/Deflate/Deflate+Lengths.swift diff --git a/SWCompression.xcodeproj/project.pbxproj b/SWCompression.xcodeproj/project.pbxproj index 41526b3e..a874536f 100644 --- a/SWCompression.xcodeproj/project.pbxproj +++ b/SWCompression.xcodeproj/project.pbxproj @@ -64,7 +64,6 @@ 0686A6451FA4C47300E89C9E /* FileSystemType+Zip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0686A6441FA4C47300E89C9E /* FileSystemType+Zip.swift */; }; 068D070221368242002A6C3B /* DeltaFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068D070121368242002A6C3B /* DeltaFilter.swift */; }; 068D070421368617002A6C3B /* test_delta_filter.7z in Resources */ = {isa = PBXBuildFile; fileRef = 068D070321368617002A6C3B /* test_delta_filter.7z */; }; - 06912BD21F5C8F3D0070BB60 /* Deflate+Lengths.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06912BD11F5C8F3D0070BB60 /* Deflate+Lengths.swift */; }; 0694A74D1F7C0DF00023BC99 /* BurrowsWheeler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0694A74C1F7C0DF00023BC99 /* BurrowsWheeler.swift */; }; 06955E491F65C761004D5D79 /* BZip2+Lengths.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06955E481F65C761004D5D79 /* BZip2+Lengths.swift */; }; 069864CA21403CE700755D9B /* test_sha256.xz in Resources */ = {isa = PBXBuildFile; fileRef = 069864C921403CE700755D9B /* test_sha256.xz */; }; @@ -371,7 +370,6 @@ 0686A6441FA4C47300E89C9E /* FileSystemType+Zip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileSystemType+Zip.swift"; sourceTree = ""; }; 068D070121368242002A6C3B /* DeltaFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeltaFilter.swift; sourceTree = ""; }; 068D070321368617002A6C3B /* test_delta_filter.7z */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_delta_filter.7z; sourceTree = ""; }; - 06912BD11F5C8F3D0070BB60 /* Deflate+Lengths.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deflate+Lengths.swift"; sourceTree = ""; }; 0694A74C1F7C0DF00023BC99 /* BurrowsWheeler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BurrowsWheeler.swift; sourceTree = ""; }; 06955E481F65C761004D5D79 /* BZip2+Lengths.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BZip2+Lengths.swift"; sourceTree = ""; }; 069864C921403CE700755D9B /* test_sha256.xz */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_sha256.xz; sourceTree = ""; }; @@ -648,7 +646,6 @@ children = ( 061FCE251DBCC4BE0052F7BE /* Deflate.swift */, 06CDFC9D1F111D2600292758 /* Deflate+Compress.swift */, - 06912BD11F5C8F3D0070BB60 /* Deflate+Lengths.swift */, 0631CB051F81109E00A2E0AB /* Deflate+Constants.swift */, 06CDFCA71F111D9700292758 /* DeflateError.swift */, ); @@ -1446,7 +1443,6 @@ 0631CB061F81109E00A2E0AB /* Deflate+Constants.swift in Sources */, E648151D26A889B8009261F0 /* TarHeader.swift in Sources */, 06D95AC11F4C54E0006B46AC /* BZip2+Compress.swift in Sources */, - 06912BD21F5C8F3D0070BB60 /* Deflate+Lengths.swift in Sources */, 06A9606A1F1E7E0D0078E6D1 /* 7zSubstreamInfo.swift in Sources */, E6C4150726FE230A00F9D36F /* XxHash32.swift in Sources */, 0694A74D1F7C0DF00023BC99 /* BurrowsWheeler.swift in Sources */, diff --git a/Sources/Common/CodingTree/Code.swift b/Sources/Common/CodingTree/Code.swift index adaad351..278011fc 100644 --- a/Sources/Common/CodingTree/Code.swift +++ b/Sources/Common/CodingTree/Code.swift @@ -12,7 +12,6 @@ struct Code { let code: Int let symbol: Int - /// `lengths` don't have to be sorted, but there must not be any 0 code lengths. static func huffmanCodes(from lengths: [CodeLength]) -> HuffmanCodes { // Sort `lengths` array to calculate canonical Huffman code. let sortedLengths = lengths.sorted() @@ -24,8 +23,7 @@ struct Code { var loopBits = -1 var symbol = -1 - for length in sortedLengths { - precondition(length.codeLength > 0, "Code length must not be 0 during HuffmanTree construction.") + for length in sortedLengths where length.codeLength > 0 { symbol += 1 // We sometimes need to make symbol to have length.bits bit length. let bits = length.codeLength diff --git a/Sources/Deflate/Deflate+Lengths.swift b/Sources/Deflate/Deflate+Lengths.swift deleted file mode 100644 index dd2425f3..00000000 --- a/Sources/Deflate/Deflate+Lengths.swift +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2026 Timofey Solomko -// Licensed under MIT License -// -// See LICENSE for license information - -// Deflate specific functions for generation of HuffmanLength arrays from different inputs. -extension Deflate { - - /// - Note: Skips zero codeLengths. - static func lengths(from orderedCodeLengths: [Int]) -> [CodeLength] { - var lengths = [CodeLength]() - for (i, codeLength) in orderedCodeLengths.enumerated() where codeLength > 0 { - lengths.append(CodeLength(symbol: i, codeLength: codeLength)) - } - return lengths - } - -} diff --git a/Sources/Deflate/Deflate.swift b/Sources/Deflate/Deflate.swift index d1f232c8..68553e33 100644 --- a/Sources/Deflate/Deflate.swift +++ b/Sources/Deflate/Deflate.swift @@ -248,4 +248,8 @@ public class Deflate: DecompressionAlgorithm { return Data(out) } + private static func lengths(from orderedCodeLengths: [Int]) -> [CodeLength] { + return orderedCodeLengths.enumerated().map({ CodeLength(symbol: $0, codeLength: $1) }) + } + }