From e4338d4df51a68a3f8f86c5d9e7b2d58fb8ca870 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sun, 21 Apr 2024 13:31:16 +0300 Subject: [PATCH 01/70] Drop support for Swift 5.3 and 5.4 Mainly because the macOS 11 images, required for these versions of Swift, will soon be deprecated in CI. --- Package.swift | 2 +- README.md | 2 +- azure-pipelines.yml | 23 ----------------------- 3 files changed, 2 insertions(+), 25 deletions(-) diff --git a/Package.swift b/Package.swift index 2e2020db..1839e960 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.3 +// swift-tools-version:5.5 import PackageDescription let package = Package( diff --git a/README.md b/README.md index ce180397..426f40bf 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # SWCompression -[![Swift 5.3+](https://img.shields.io/badge/Swift-5.3+-blue.svg)](https://developer.apple.com/swift/) +[![Swift 5.5+](https://img.shields.io/badge/Swift-5.5+-blue.svg)](https://developer.apple.com/swift/) [![GitHub license](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://raw.githubusercontent.com/tsolomko/SWCompression/master/LICENSE) [![Build Status](https://dev.azure.com/tsolomko/SWCompression/_apis/build/status/tsolomko.SWCompression?branchName=develop)](https://dev.azure.com/tsolomko/SWCompression/_build/latest?definitionId=3&branchName=develop) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3d5dc588..e1dabecf 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -15,18 +15,6 @@ stages: - job: macos strategy: matrix: - macosSwift53: - imageName: 'macOS-11' - DEVELOPER_DIR: '/Applications/Xcode_12.4.app' - IOS_SIMULATOR: 'iPhone 8' - WATCHOS_ACTIONS: 'clean build' - WATCHOS_SIMULATOR: 'Apple Watch Series 4 - 44mm' - macosSwift54: - imageName: 'macOS-11' - DEVELOPER_DIR: '/Applications/Xcode_12.5.1.app' - IOS_SIMULATOR: 'iPhone 8' - WATCHOS_ACTIONS: 'clean test' - WATCHOS_SIMULATOR: 'Apple Watch Series 6 - 44mm' macosSwift55: imageName: 'macOS-12' DEVELOPER_DIR: '/Applications/Xcode_13.2.1.app' @@ -99,12 +87,6 @@ stages: - job: linux strategy: matrix: - linuxSwift53: - imageName: 'ubuntu-20.04' - containerImage: 'swift:5.3.3-focal' - linuxSwift54: - imageName: 'ubuntu-20.04' - containerImage: 'swift:5.4.3-focal' linuxSwift55: imageName: 'ubuntu-20.04' containerImage: 'swift:5.5.3-focal' @@ -136,11 +118,6 @@ stages: - job: windows strategy: matrix: - windowsSwift54: - imageName: 'windows-2019' - SWIFT_VERSION: '5.4.3' - ICU_PATH: 'C:\Library\icu-67\usr\bin' - SWIFT_DEV_PATH: 'C:\Library\Swift-development\bin' windowsSwift55: imageName: 'windows-2019' SWIFT_VERSION: '5.5.3' From ed03f6441d9c0d585cef50befe58c5196eee6d42 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sun, 21 Apr 2024 13:33:03 +0300 Subject: [PATCH 02/70] [CI] Remove a redundant flag from macos builds --- azure-pipelines.yml | 6 ------ utils.py | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e1dabecf..08d4eb5c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -19,38 +19,32 @@ stages: imageName: 'macOS-12' DEVELOPER_DIR: '/Applications/Xcode_13.2.1.app' IOS_SIMULATOR: 'iPhone 8' - WATCHOS_ACTIONS: 'clean test' WATCHOS_SIMULATOR: 'Apple Watch Series 6 - 44mm' macosSwift56: imageName: 'macOS-12' DEVELOPER_DIR: '/Applications/Xcode_13.4.1.app' IOS_SIMULATOR: 'iPhone 8' - WATCHOS_ACTIONS: 'clean test' WATCHOS_SIMULATOR: 'Apple Watch Series 6 - 44mm' macosSwift57: imageName: 'macOS-13' DEVELOPER_DIR: '/Applications/Xcode_14.2.app' IOS_SIMULATOR: 'iPhone 14' - WATCHOS_ACTIONS: 'clean test' WATCHOS_SIMULATOR: 'Apple Watch Series 6 (44mm)' macosSwift58: imageName: 'macOS-13' DEVELOPER_DIR: '/Applications/Xcode_14.3.1.app' IOS_SIMULATOR: 'iPhone 14' - WATCHOS_ACTIONS: 'clean test' WATCHOS_SIMULATOR: 'Apple Watch Series 6 (44mm)' macosSwift59: imageName: 'macOS-13' DEVELOPER_DIR: '/Applications/Xcode_15.2.app' IOS_SIMULATOR: 'iPhone 14' - WATCHOS_ACTIONS: 'clean test' WATCHOS_SIMULATOR: 'Apple Watch Series 6 (44mm)' # TODO: Enable when AZP adds macOS 14 images. # macosSwift510: # imageName: 'macOS-14' # DEVELOPER_DIR: '/Applications/Xcode_15.3.app' # IOS_SIMULATOR: 'iPhone 14' - # WATCHOS_ACTIONS: 'clean test' # WATCHOS_SIMULATOR: 'Apple Watch Series 6 (44mm)' pool: vmImage: $(imageName) diff --git a/utils.py b/utils.py index aef6e96d..70b0fb0f 100755 --- a/utils.py +++ b/utils.py @@ -25,7 +25,7 @@ def _ci_script_macos(): xcodebuild_command_parts = ["xcodebuild", "-quiet", "-project", "SWCompression.xcodeproj", "-scheme", "SWCompression"] destinations_actions = [(["-destination 'platform=OS X'"], ["clean", "test"]), (["-destination 'platform=iOS Simulator,name=" + os.environ["IOS_SIMULATOR"] + "'"], ["clean", "test"]), - (["-destination 'platform=watchOS Simulator,name=" + os.environ["WATCHOS_SIMULATOR"] + "'"], [os.environ["WATCHOS_ACTIONS"]]), + (["-destination 'platform=watchOS Simulator,name=" + os.environ["WATCHOS_SIMULATOR"] + "'"], ["clean", "test"]), (["-destination 'platform=tvOS Simulator,name=Apple TV'"], ["clean", "test"])] for destination, actions in destinations_actions: From 7427a29d6d84a2d72411f39ae491de238937dc46 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sun, 21 Apr 2024 13:45:19 +0300 Subject: [PATCH 03/70] Fix podspec settings for the PrivacyInfo file --- SWCompression.podspec | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/SWCompression.podspec b/SWCompression.podspec index 562de57b..5519d2a3 100644 --- a/SWCompression.podspec +++ b/SWCompression.podspec @@ -25,70 +25,61 @@ Pod::Spec.new do |s| s.dependency "BitByteData", "~> 2.0" + s.resource_bundles = {"SWCompression" => ["Sources/PrivacyInfo.xcprivacy"]} + s.subspec "Deflate" do |sp| sp.source_files = "Sources/{Deflate/*,Common/*,Common/CodingTree/*}.swift" - sp.resource_bundles = {"SWCompression/Deflate" => ["Sources/PrivacyInfo.xcprivacy"]} sp.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS" => "-DSWCOMPRESSION_POD_DEFLATE" } end s.subspec "GZip" do |sp| sp.dependency "SWCompression/Deflate" - sp.resource_bundles = {"SWCompression/GZip" => ["Sources/PrivacyInfo.xcprivacy"]} sp.source_files = "Sources/{GZip/*,Common/*}.swift" end s.subspec "Zlib" do |sp| sp.dependency "SWCompression/Deflate" - sp.resource_bundles = {"SWCompression/Zlib" => ["Sources/PrivacyInfo.xcprivacy"]} sp.source_files = "Sources/{Zlib/*,Common/*}.swift" end s.subspec "BZip2" do |sp| sp.source_files = "Sources/{BZip2/*,Common/*,Common/CodingTree/*}.swift" - sp.resource_bundles = {"SWCompression/BZip2" => ["Sources/PrivacyInfo.xcprivacy"]} sp.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS" => "-DSWCOMPRESSION_POD_BZ2" } end s.subspec "LZMA" do |sp| sp.source_files = "Sources/{LZMA/*,Common/*}.swift" - sp.resource_bundles = {"SWCompression/LZMA" => ["Sources/PrivacyInfo.xcprivacy"]} sp.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS" => "-DSWCOMPRESSION_POD_LZMA" } end s.subspec "LZMA2" do |sp| sp.dependency "SWCompression/LZMA" sp.source_files = "Sources/{LZMA2/*,Common/*}.swift" - sp.resource_bundles = {"SWCompression/LZMA2" => ["Sources/PrivacyInfo.xcprivacy"]} end s.subspec "LZ4" do |sp| sp.source_files = "Sources/{LZ4/*,Common/*}.swift" - sp.resource_bundles = {"SWCompression/LZ4" => ["Sources/PrivacyInfo.xcprivacy"]} sp.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS" => "-DSWCOMPRESSION_POD_LZ4" } end s.subspec "XZ" do |sp| sp.dependency "SWCompression/LZMA2" sp.source_files = "Sources/{XZ/*,Common/*}.swift" - sp.resource_bundles = {"SWCompression/XZ" => ["Sources/PrivacyInfo.xcprivacy"]} end s.subspec "ZIP" do |sp| sp.dependency "SWCompression/Deflate" sp.source_files = "Sources/{Zip/*,Common/*,Common/Container/*}.swift" - sp.resource_bundles = {"SWCompression/ZIP" => ["Sources/PrivacyInfo.xcprivacy"]} sp.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS" => "-DSWCOMPRESSION_POD_ZIP" } end s.subspec "TAR" do |sp| sp.source_files = "Sources/{TAR/*,Common/*,Common/Container/*}.swift" - sp.resource_bundles = {"SWCompression/TAR" => ["Sources/PrivacyInfo.xcprivacy"]} end s.subspec "SevenZip" do |sp| sp.dependency "SWCompression/LZMA2" sp.source_files = "Sources/{7-Zip/*,Common/*,Common/Container/*}.swift" - sp.resource_bundles = {"SWCompression/SevenZip" => ["Sources/PrivacyInfo.xcprivacy"]} sp.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS" => "-DSWCOMPRESSION_POD_SEVENZIP" } end From 891848f9398bf29ca24461b4ce8227f5b5b18020 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sun, 21 Apr 2024 13:45:44 +0300 Subject: [PATCH 04/70] [swcomp] Remove redundant compiler check --- .../swcomp/Extensions/FileHandle+CloseCompat.swift | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Sources/swcomp/Extensions/FileHandle+CloseCompat.swift b/Sources/swcomp/Extensions/FileHandle+CloseCompat.swift index 941930f8..93e95e86 100644 --- a/Sources/swcomp/Extensions/FileHandle+CloseCompat.swift +++ b/Sources/swcomp/Extensions/FileHandle+CloseCompat.swift @@ -8,15 +8,11 @@ import Foundation extension FileHandle { func closeCompat() throws { - #if compiler(<5.2) + if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) { + try self.close() + } else { self.closeFile() - #else - if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) { - try self.close() - } else { - self.closeFile() - } - #endif + } } } From ffa0ba30d686cbc39741547bae426cf31df8e012 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sun, 21 Apr 2024 13:45:58 +0300 Subject: [PATCH 05/70] Upgrade Xcode project format --- SWCompression.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SWCompression.xcodeproj/project.pbxproj b/SWCompression.xcodeproj/project.pbxproj index 3e4b7fc1..f285838a 100644 --- a/SWCompression.xcodeproj/project.pbxproj +++ b/SWCompression.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 55; objects = { /* Begin PBXBuildFile section */ @@ -1179,7 +1179,7 @@ }; }; buildConfigurationList = 06BE1AC21DB410F100EE0F59 /* Build configuration list for PBXProject "SWCompression" */; - compatibilityVersion = "Xcode 11.4"; + compatibilityVersion = "Xcode 13.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( From 2927e6ecf3ce8440b8ca72af432168a87ec5b805 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sun, 21 Apr 2024 17:17:01 +0300 Subject: [PATCH 06/70] Increase minimum deployment targets on Apple platforms --- Package.swift | 8 ++++---- SWCompression.podspec | 8 ++++---- SWCompression.xcodeproj/project.pbxproj | 16 ++++++++-------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Package.swift b/Package.swift index 1839e960..323d637d 100644 --- a/Package.swift +++ b/Package.swift @@ -4,10 +4,10 @@ import PackageDescription let package = Package( name: "SWCompression", platforms: [ - .macOS(.v10_13), - .iOS(.v11), - .tvOS(.v11), - .watchOS(.v4), + .macOS(.v11), + .iOS(.v14), + .tvOS(.v14), + .watchOS(.v7), // TODO: Enable after upgrading to Swift 5.9. // .visionOS(.v1) ], diff --git a/SWCompression.podspec b/SWCompression.podspec index 5519d2a3..2094cd8f 100644 --- a/SWCompression.podspec +++ b/SWCompression.podspec @@ -15,10 +15,10 @@ Pod::Spec.new do |s| s.source = { :git => "https://github.com/tsolomko/SWCompression.git", :tag => "#{s.version}" } - s.ios.deployment_target = "11.0" - s.osx.deployment_target = "10.13" - s.tvos.deployment_target = "11.0" - s.watchos.deployment_target = "4.0" + s.ios.deployment_target = "14.0" + s.osx.deployment_target = "11.0" + s.tvos.deployment_target = "14.0" + s.watchos.deployment_target = "7.0" # s.visionos.deployment_target = "1.0" s.swift_versions = ["5"] diff --git a/SWCompression.xcodeproj/project.pbxproj b/SWCompression.xcodeproj/project.pbxproj index f285838a..ff7666c5 100644 --- a/SWCompression.xcodeproj/project.pbxproj +++ b/SWCompression.xcodeproj/project.pbxproj @@ -1575,8 +1575,8 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - MACOSX_DEPLOYMENT_TARGET = 10.13; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MACOSX_DEPLOYMENT_TARGET = 11.0; ONLY_ACTIVE_ARCH = YES; OTHER_CODE_SIGN_FLAGS = "--deep"; OTHER_LDFLAGS = ( @@ -1587,9 +1587,9 @@ SUPPORTED_PLATFORMS = "macosx watchsimulator iphonesimulator appletvsimulator watchos appletvos iphoneos xrsimulator xros"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TVOS_DEPLOYMENT_TARGET = 11.0; + TVOS_DEPLOYMENT_TARGET = 14.0; VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 4.0; + WATCHOS_DEPLOYMENT_TARGET = 7.0; XROS_DEPLOYMENT_TARGET = 1.0; }; name = Debug; @@ -1643,8 +1643,8 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - MACOSX_DEPLOYMENT_TARGET = 10.13; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MACOSX_DEPLOYMENT_TARGET = 11.0; OTHER_CODE_SIGN_FLAGS = "--deep"; OTHER_LDFLAGS = ( "-framework", @@ -1654,9 +1654,9 @@ SUPPORTED_PLATFORMS = "macosx watchsimulator iphonesimulator appletvsimulator watchos appletvos iphoneos xrsimulator xros"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_VERSION = 5.0; - TVOS_DEPLOYMENT_TARGET = 11.0; + TVOS_DEPLOYMENT_TARGET = 14.0; VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 4.0; + WATCHOS_DEPLOYMENT_TARGET = 7.0; XROS_DEPLOYMENT_TARGET = 1.0; }; name = Release; From fa4af5803a799a6e3df07dbe5732ebb5f5f8cb86 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sun, 21 Apr 2024 17:18:53 +0300 Subject: [PATCH 07/70] [TAR] Remove compatibility functions for FileHandle --- SWCompression.xcodeproj/project.pbxproj | 4 -- Sources/TAR/TarReader.swift | 60 ++++++------------- Sources/TAR/TarWriter.swift | 15 +---- Sources/swcomp/Benchmarks/Benchmarks.swift | 4 +- Sources/swcomp/Containers/TarCommand.swift | 6 +- .../Extensions/FileHandle+CloseCompat.swift | 18 ------ Tests/FileHandle+CloseCompat.swift | 18 ------ Tests/TarReaderTests.swift | 30 +++++----- Tests/TarWriterTests.swift | 2 +- 9 files changed, 40 insertions(+), 117 deletions(-) delete mode 100644 Sources/swcomp/Extensions/FileHandle+CloseCompat.swift delete mode 100644 Tests/FileHandle+CloseCompat.swift diff --git a/SWCompression.xcodeproj/project.pbxproj b/SWCompression.xcodeproj/project.pbxproj index ff7666c5..c9e94da2 100644 --- a/SWCompression.xcodeproj/project.pbxproj +++ b/SWCompression.xcodeproj/project.pbxproj @@ -229,7 +229,6 @@ E64F71BC26AAFD6A008BB308 /* Data+Tar.swift in Sources */ = {isa = PBXBuildFile; fileRef = E64F71BB26AAFD6A008BB308 /* Data+Tar.swift */; }; E6522FB42789AF9600026B56 /* TarReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6522FB32789AF9600026B56 /* TarReaderTests.swift */; }; E6522FB62789AFA600026B56 /* TarWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6522FB52789AFA600026B56 /* TarWriterTests.swift */; }; - E6522FBD2789C4A300026B56 /* FileHandle+CloseCompat.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6522FBC2789C4A300026B56 /* FileHandle+CloseCompat.swift */; }; E652D8F3263D670900FC229B /* test_nonstandard_runlength.bz2 in Resources */ = {isa = PBXBuildFile; fileRef = E652D8F2263D670900FC229B /* test_nonstandard_runlength.bz2 */; }; E652D8F5263D678000FC229B /* test_nonstandard_runlength.answer in Resources */ = {isa = PBXBuildFile; fileRef = E652D8F4263D678000FC229B /* test_nonstandard_runlength.answer */; }; E652FECA27007E79006BC312 /* test3.lz4 in Resources */ = {isa = PBXBuildFile; fileRef = E652FEC127007E78006BC312 /* test3.lz4 */; }; @@ -531,7 +530,6 @@ E64F71BB26AAFD6A008BB308 /* Data+Tar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Tar.swift"; sourceTree = ""; }; E6522FB32789AF9600026B56 /* TarReaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TarReaderTests.swift; sourceTree = ""; }; E6522FB52789AFA600026B56 /* TarWriterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TarWriterTests.swift; sourceTree = ""; }; - E6522FBC2789C4A300026B56 /* FileHandle+CloseCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileHandle+CloseCompat.swift"; sourceTree = ""; }; E652D8F2263D670900FC229B /* test_nonstandard_runlength.bz2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_nonstandard_runlength.bz2; sourceTree = ""; }; E652D8F4263D678000FC229B /* test_nonstandard_runlength.answer */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_nonstandard_runlength.answer; sourceTree = ""; }; E652FEC127007E78006BC312 /* test3.lz4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = test3.lz4; sourceTree = ""; }; @@ -844,7 +842,6 @@ E6522FB52789AFA600026B56 /* TarWriterTests.swift */, 06F065A61FFB763300312A82 /* TarTests.swift */, E6522FB32789AF9600026B56 /* TarReaderTests.swift */, - E6522FBC2789C4A300026B56 /* FileHandle+CloseCompat.swift */, 06F065A91FFB763300312A82 /* XzTests.swift */, 06F065A41FFB763300312A82 /* ZipTests.swift */, 06D42DA62067B71000C1A98B /* TestZipExtraField.swift */, @@ -1506,7 +1503,6 @@ E6D86D2F26FE35C50032CFFA /* XxHash32Tests.swift in Sources */, 06F0661B1FFB763300312A82 /* XzTests.swift in Sources */, 06F0661E1FFB763300312A82 /* BZip2CompressionTests.swift in Sources */, - E6522FBD2789C4A300026B56 /* FileHandle+CloseCompat.swift in Sources */, 06F066131FFB763300312A82 /* Constants.swift in Sources */, 06F0661C1FFB763300312A82 /* SevenZipTests.swift in Sources */, 06F066161FFB763300312A82 /* ZipTests.swift in Sources */, diff --git a/Sources/TAR/TarReader.swift b/Sources/TAR/TarReader.swift index 565ce4b7..e7603a27 100644 --- a/Sources/TAR/TarReader.swift +++ b/Sources/TAR/TarReader.swift @@ -23,12 +23,6 @@ import BitByteData try handle.close() ``` Note that closing the `FileHandle` remains the responsibility of the caller. - - - Important: Due to the API availability limitations of Foundation's `FileHandle`, on certain platforms errors in - `FileHandle` operations may result in unrecoverable runtime failures due to unhandled Objective-C exceptions (which are - impossible to correctly handle in Swift code). As such, it is not recommended to use `TarReader` on those platforms. - The following platforms are _unaffected_ by this issue: macOS 10.15.4+, iOS 13.4+, watchOS 6.2+, tvOS 13.4+, and any - other platforms without Objective-C runtime. */ public struct TarReader { @@ -86,7 +80,7 @@ public struct TarReader { return nil } else if headerData == Data(count: 512) { // EOF marker case. - let offset = try getOffset() + let offset = try handle.offset() if try getData(size: 512) == Data(count: 512) { return nil } else { @@ -94,7 +88,7 @@ public struct TarReader { // match the EOF marker signature. In practice, this indicates a malformed TAR container, since a // zero-filled block is not a valid TAR header (and in fact the end result is an error being thrown in // TarHeader initializer later down the line). - try set(offset: offset) + try handle.seek(toOffset: offset) } } else if headerData.count < 512 { throw DataError.truncated @@ -106,7 +100,7 @@ public struct TarReader { // at most 512 bytes. // Check, just in case, since we use blockStartIndex = -1 when creating TAR containers. assert(header.blockStartIndex >= 0) - let dataStartOffset = try getOffset() + let dataStartOffset = try handle.offset() let entryData = try getData(size: header.size) guard entryData.count == header.size @@ -125,11 +119,11 @@ public struct TarReader { case .longName: longName = LittleEndianByteReader(data: entryData).tarCString(maxLength: header.size) } - try set(offset: dataStartOffset + UInt64(truncatingIfNeeded: header.size.roundTo512())) + try handle.seek(toOffset: dataStartOffset + UInt64(truncatingIfNeeded: header.size.roundTo512())) return try read() } else { let info = TarEntryInfo(header, lastGlobalExtendedHeader, lastLocalExtendedHeader, longName, longLinkName) - try set(offset: dataStartOffset + UInt64(truncatingIfNeeded: header.size.roundTo512())) + try handle.seek(toOffset: dataStartOffset + UInt64(truncatingIfNeeded: header.size.roundTo512())) lastLocalExtendedHeader = nil longName = nil longLinkName = nil @@ -144,40 +138,20 @@ public struct TarReader { } } - private func getOffset() throws -> UInt64 { - if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) { - return try handle.offset() - } else { - return handle.offsetInFile - } - } - - private func set(offset: UInt64) throws { - if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) { - try handle.seek(toOffset: offset) - } else { - handle.seek(toFileOffset: offset) - } - } - + @inline(__always) private func getData(size: Int) throws -> Data { assert(size >= 0, "TarReader.getData(size:): negative size.") - if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) { - // The documentation for FileHandle.read(upToCount:) is a bit misleading. This method does "return the data - // obtained by reading length bytes starting at the current file pointer" even if the requested amount is - // larger than the available data. What is not clear is when the method returns nil. Apparently, there are - // (at least) two cases when it happens: - // - the file pointer is at the EOF regardless of the argument value, - // - the argument is zero. - // It is also unclear what happens when the argument is negative (it seems that it reads everything until - // the EOF), but the assertion above takes care of this. In any case, instead of returning nil we return - // empty data since both of these situations logically seem equivalent for our purposes. This also allows us - // to eliminate additional guard-check for the size parameter. - return try handle.read(upToCount: size) ?? Data() - } else { - // Technically, this can throw NSException, but since it is ObjC exception we cannot handle it in Swift. - return handle.readData(ofLength: size) - } + // The documentation for FileHandle.read(upToCount:) is a bit misleading. This method does "return the data + // obtained by reading length bytes starting at the current file pointer" even if the requested amount is + // larger than the available data. What is not clear is when the method returns nil. Apparently, there are + // (at least) two cases when it happens: + // - the file pointer is at the EOF regardless of the argument value, + // - the argument is zero. + // It is also unclear what happens when the argument is negative (it seems that it reads everything until + // the EOF), but the assertion above takes care of this. In any case, instead of returning nil we return + // empty data since both of these situations logically seem equivalent for our purposes. This also allows us + // to eliminate additional guard-check for the size parameter. + return try handle.read(upToCount: size) ?? Data() } } diff --git a/Sources/TAR/TarWriter.swift b/Sources/TAR/TarWriter.swift index b19d9746..5ffb8416 100644 --- a/Sources/TAR/TarWriter.swift +++ b/Sources/TAR/TarWriter.swift @@ -26,12 +26,6 @@ import Foundation ``` Note that `TarWriter.finalize()` must be called after finishing appending entries to the container. In addition, closing the `FileHandle` remains the responsibility of the caller. - - - Important: Due to the API availability limitations of Foundation's `FileHandle`, on certain platforms errors in - `FileHandle` operations may result in unrecoverable runtime failures due to unhandled Objective-C exceptions (which are - impossible to correctly handle in Swift code). As such, it is not recommended to use `TarWriter` on those platforms. - The following platforms are _unaffected_ by this issue: macOS 10.15.4+, iOS 13.4+, watchOS 6.2+, tvOS 13.4+, and any - other platforms without Objective-C runtime. */ public struct TarWriter { @@ -132,13 +126,8 @@ public struct TarWriter { } private func write(_ data: Data) throws { - if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) { - try handle.write(contentsOf: data) - try handle.synchronize() - } else { - handle.write(data) - handle.synchronizeFile() - } + try handle.write(contentsOf: data) + try handle.synchronize() } } diff --git a/Sources/swcomp/Benchmarks/Benchmarks.swift b/Sources/swcomp/Benchmarks/Benchmarks.swift index 98ab8f51..44f3fc06 100644 --- a/Sources/swcomp/Benchmarks/Benchmarks.swift +++ b/Sources/swcomp/Benchmarks/Benchmarks.swift @@ -594,7 +594,7 @@ struct ReaderTar: Benchmark { } } let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000 - try handle.closeCompat() + try handle.close() return self.size / timeElapsed } catch let error { swcompExit(.benchmarkCannotMeasure(Self.self, error)) @@ -629,7 +629,7 @@ struct WriterTar: Benchmark { } try writer.finalize() let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000 - try handle.closeCompat() + try handle.close() try FileManager.default.removeItem(at: url) return self.size / timeElapsed } catch let error { diff --git a/Sources/swcomp/Containers/TarCommand.swift b/Sources/swcomp/Containers/TarCommand.swift index a01b6645..e58b8f44 100644 --- a/Sources/swcomp/Containers/TarCommand.swift +++ b/Sources/swcomp/Containers/TarCommand.swift @@ -119,7 +119,7 @@ final class TarCommand: Command { return false } } - try handle.closeCompat() + try handle.close() } } else if let outputPath = self.extract { guard try isValidOutputDirectory(outputPath, create: true) @@ -167,7 +167,7 @@ final class TarCommand: Command { return false } } - try handle.closeCompat() + try handle.close() for tuple in directoryAttributes { try fileManager.setAttributes(tuple.attributes, ofItemAtPath: tuple.path) @@ -222,7 +222,7 @@ final class TarCommand: Command { var writer = TarWriter(fileHandle: handle, force: self.useFormat ?? .pax) try TarEntry.generateEntries(&writer, inputPath, verbose) try writer.finalize() - try handle.closeCompat() + try handle.close() } } } diff --git a/Sources/swcomp/Extensions/FileHandle+CloseCompat.swift b/Sources/swcomp/Extensions/FileHandle+CloseCompat.swift deleted file mode 100644 index 93e95e86..00000000 --- a/Sources/swcomp/Extensions/FileHandle+CloseCompat.swift +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2024 Timofey Solomko -// Licensed under MIT License -// -// See LICENSE for license informations - -import Foundation - -extension FileHandle { - - func closeCompat() throws { - if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) { - try self.close() - } else { - self.closeFile() - } - } - -} diff --git a/Tests/FileHandle+CloseCompat.swift b/Tests/FileHandle+CloseCompat.swift deleted file mode 100644 index 93e95e86..00000000 --- a/Tests/FileHandle+CloseCompat.swift +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2024 Timofey Solomko -// Licensed under MIT License -// -// See LICENSE for license informations - -import Foundation - -extension FileHandle { - - func closeCompat() throws { - if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) { - try self.close() - } else { - self.closeFile() - } - } - -} diff --git a/Tests/TarReaderTests.swift b/Tests/TarReaderTests.swift index 2bf525cd..be422acd 100644 --- a/Tests/TarReaderTests.swift +++ b/Tests/TarReaderTests.swift @@ -16,7 +16,7 @@ class TarReaderTests: XCTestCase { let testHandle = try Constants.handle(forTest: "test7", withType: "answer") var reader = TarReader(fileHandle: testHandle) XCTAssertThrowsError(try reader.read()) - try testHandle.closeCompat() + try testHandle.close() } func test() throws { @@ -44,7 +44,7 @@ class TarReaderTests: XCTestCase { } } XCTAssertEqual(entriesCount, 1) - try testHandle.closeCompat() + try testHandle.close() } @@ -77,7 +77,7 @@ class TarReaderTests: XCTestCase { } } XCTAssertEqual(entriesCount, 5) - try testHandle.closeCompat() + try testHandle.close() } func testFormats() throws { @@ -104,7 +104,7 @@ class TarReaderTests: XCTestCase { } XCTAssertEqual(entriesCount, 1) XCTAssertNil(try reader.read()) - try testHandle.closeCompat() + try testHandle.close() } } @@ -126,7 +126,7 @@ class TarReaderTests: XCTestCase { } XCTAssertEqual(entriesCount, 6) XCTAssertNil(try reader.read()) - try testHandle.closeCompat() + try testHandle.close() } } @@ -160,7 +160,7 @@ class TarReaderTests: XCTestCase { XCTAssertEqual(entry!.data, "Hello, Windows!".data(using: .utf8)) } XCTAssertNil(try reader.read()) - try testHandle.closeCompat() + try testHandle.close() } func testEmptyFile() throws { @@ -181,7 +181,7 @@ class TarReaderTests: XCTestCase { } XCTAssertNil(try reader.read()) - try testHandle.closeCompat() + try testHandle.close() } func testEmptyDirectory() throws { @@ -201,7 +201,7 @@ class TarReaderTests: XCTestCase { XCTAssertNil(entry!.data) } XCTAssertNil(try reader.read()) - try testHandle.closeCompat() + try testHandle.close() } func testOnlyDirectoryHeader() throws { @@ -223,21 +223,21 @@ class TarReaderTests: XCTestCase { XCTAssertNil(entry!.data) } XCTAssertNil(try reader.read()) - try testHandle.closeCompat() + try testHandle.close() } func testEmptyContainer() throws { let testHandle = try Constants.handle(forTest: "test_empty_cont", withType: TarReaderTests.testType) var reader = TarReader(fileHandle: testHandle) XCTAssertNil(try reader.read()) - try testHandle.closeCompat() + try testHandle.close() } func testBigContainer() throws { let testHandle = try Constants.handle(forTest: "SWCompressionSourceCode", withType: TarReaderTests.testType) var reader = TarReader(fileHandle: testHandle) while try reader.read() != nil { } - try testHandle.closeCompat() + try testHandle.close() } func testUnicodeUstar() throws { @@ -258,7 +258,7 @@ class TarReaderTests: XCTestCase { } XCTAssertNil(try reader.read()) - try testHandle.closeCompat() + try testHandle.close() } func testUnicodePax() throws { @@ -279,7 +279,7 @@ class TarReaderTests: XCTestCase { } XCTAssertNil(try reader.read()) - try testHandle.closeCompat() + try testHandle.close() } func testGnuIncrementalFormat() throws { @@ -304,7 +304,7 @@ class TarReaderTests: XCTestCase { } XCTAssertEqual(entriesCount, 3) XCTAssertNil(try reader.read()) - try testHandle.closeCompat() + try testHandle.close() } // This test is impossible to implement using TarReader since the test file doesn't contain actual entry data. @@ -328,7 +328,7 @@ class TarReaderTests: XCTestCase { } // Test that reading after reaching EOF returns nil. XCTAssertNil(try reader.read()) - try testHandle.closeCompat() + try testHandle.close() } } diff --git a/Tests/TarWriterTests.swift b/Tests/TarWriterTests.swift index b19de81a..a31f0626 100644 --- a/Tests/TarWriterTests.swift +++ b/Tests/TarWriterTests.swift @@ -36,7 +36,7 @@ class TarWriterTests: XCTestCase { try writer.append(entry) } try writer.finalize() - try handle.closeCompat() + try handle.close() return try Data(contentsOf: tempFileUrl) } From 60b76e64908f26eaa68a88fc7f8f1d2a004c2edd Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sun, 21 Apr 2024 17:24:19 +0300 Subject: [PATCH 08/70] [swcomp] Enable swcomp in the SPM manifest only on macOS Mainly, because SwiftCLI is incompatible with Linux and Windows, and we both don't need swcomp on these platforms (at the moment), and I am too busy to try to make a fork of SwiftCLI which fixes these issues. --- Package.swift | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Package.swift b/Package.swift index 323d637d..87d7d641 100644 --- a/Package.swift +++ b/Package.swift @@ -1,7 +1,7 @@ // swift-tools-version:5.5 import PackageDescription -let package = Package( +var package = Package( name: "SWCompression", platforms: [ .macOS(.v11), @@ -19,8 +19,6 @@ let package = Package( dependencies: [ .package(name: "BitByteData", url: "https://github.com/tsolomko/BitByteData", from: "2.0.0"), - .package(name: "SwiftCLI", url: "https://github.com/jakeheis/SwiftCLI", - from: "6.0.0"), ], targets: [ .target( @@ -30,12 +28,13 @@ let package = Package( exclude: ["swcomp"], sources: ["Common", "7-Zip", "BZip2", "Deflate", "GZip", "LZ4", "LZMA", "LZMA2", "TAR", "XZ", "ZIP", "Zlib"], resources: [.copy("PrivacyInfo.xcprivacy")]), - .target( - name: "swcomp", - dependencies: ["SWCompression", "SwiftCLI"], - path: "Sources", - exclude: ["Common", "7-Zip", "BZip2", "Deflate", "GZip", "LZ4", "LZMA", "LZMA2", "TAR", "XZ", "ZIP", "Zlib", "PrivacyInfo.xcprivacy"], - sources: ["swcomp"]), ], swiftLanguageVersions: [.v5] ) + +#if os(macOS) +package.dependencies.append(.package(name: "SwiftCLI", url: "https://github.com/jakeheis/SwiftCLI", from: "6.0.0")) +package.targets.append(.executableTarget(name: "swcomp", dependencies: ["SWCompression", "SwiftCLI"], path: "Sources", + exclude: ["Common", "7-Zip", "BZip2", "Deflate", "GZip", "LZ4", "LZMA", "LZMA2", "TAR", "XZ", "ZIP", "Zlib", "PrivacyInfo.xcprivacy"], + sources: ["swcomp"])) +#endif From 81cfa82f92eaedd5b28a131409e3713f7d5606ac Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sat, 14 Dec 2024 17:43:57 +0200 Subject: [PATCH 09/70] Drop support for Swift 5.5 and 5.6 This is mainly driven by some CI configurations being deprecated. Additionally add builds for Swift 5.10 and Swift 6.0 in CI. --- Package.swift | 2 +- README.md | 2 +- SWCompression.xcodeproj/project.pbxproj | 6 +- .../xcschemes/SWCompression.xcscheme | 2 +- azure-pipelines.yml | 90 +++++++++---------- 5 files changed, 46 insertions(+), 56 deletions(-) diff --git a/Package.swift b/Package.swift index 87d7d641..af6070d3 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.5 +// swift-tools-version:5.7 import PackageDescription var package = Package( diff --git a/README.md b/README.md index 426f40bf..cf343ba4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # SWCompression -[![Swift 5.5+](https://img.shields.io/badge/Swift-5.5+-blue.svg)](https://developer.apple.com/swift/) +[![Swift 5.7+](https://img.shields.io/badge/Swift-5.7+-blue.svg)](https://developer.apple.com/swift/) [![GitHub license](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://raw.githubusercontent.com/tsolomko/SWCompression/master/LICENSE) [![Build Status](https://dev.azure.com/tsolomko/SWCompression/_apis/build/status/tsolomko.SWCompression?branchName=develop)](https://dev.azure.com/tsolomko/SWCompression/_build/latest?definitionId=3&branchName=develop) diff --git a/SWCompression.xcodeproj/project.pbxproj b/SWCompression.xcodeproj/project.pbxproj index c9e94da2..03dc30da 100644 --- a/SWCompression.xcodeproj/project.pbxproj +++ b/SWCompression.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 55; + objectVersion = 56; objects = { /* Begin PBXBuildFile section */ @@ -1162,7 +1162,7 @@ attributes = { BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1530; + LastUpgradeCheck = 1620; ORGANIZATIONNAME = "Timofey Solomko"; TargetAttributes = { 06BE1AC71DB410F100EE0F59 = { @@ -1176,7 +1176,7 @@ }; }; buildConfigurationList = 06BE1AC21DB410F100EE0F59 /* Build configuration list for PBXProject "SWCompression" */; - compatibilityVersion = "Xcode 13.0"; + compatibilityVersion = "Xcode 14.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( diff --git a/SWCompression.xcodeproj/xcshareddata/xcschemes/SWCompression.xcscheme b/SWCompression.xcodeproj/xcshareddata/xcschemes/SWCompression.xcscheme index 7232fa36..f186312b 100644 --- a/SWCompression.xcodeproj/xcshareddata/xcschemes/SWCompression.xcscheme +++ b/SWCompression.xcodeproj/xcshareddata/xcschemes/SWCompression.xcscheme @@ -1,6 +1,6 @@ Date: Sat, 14 Dec 2024 17:44:51 +0200 Subject: [PATCH 10/70] Remove mention of xcode 12 workaround for testing from README --- README.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index cf343ba4..d9c770ec 100644 --- a/README.md +++ b/README.md @@ -187,14 +187,8 @@ git lfs pull git lfs checkout ``` -The first command will download the BitByteData dependency, which requires having Carthage installed. When using Xcode -12 or later, you should also pass the `--xcf` option, or use the -[xconfig workaround](https://github.com/Carthage/Carthage/blob/master/Documentation/Xcode12Workaround.md). Please note -that when building SWCompression's Xcode project you may see ld warnings about a directory not being found. These are -expected and harmless. Finally, you should keep in mind that support for non-xcframework method of installing -dependencies is likely to be dropped in the future major update. - -The remaining commands will download the files used in tests. These files are stored in a +The first command will download the BitByteData dependency, which requires having Carthage installed. The remaining +commands will download the files used in tests. These files are stored in a [separate repository](https://github.com/tsolomko/SWCompression-Test-Files), using Git LFS. There are two reasons for this complicated setup. Firstly, some of these files can be quite big, and it would be unfortunate if the users of SWCompression had to download them during the installation. Secondly, Swift Package Manager and contemporary versions of From ce5967585656f2b75bed86a7316a8846b8546e77 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sat, 14 Dec 2024 17:45:04 +0200 Subject: [PATCH 11/70] Mention that swcomp is only available on macos --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d9c770ec..93207103 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,7 @@ Every function or type of SWCompression's public API is documented. This documen ### Sophisticated example There is a small command-line program, "swcomp", which is included in this repository in "Sources/swcomp". It can be -built using Swift Package Manager. +built using Swift Package Manager (only available on macOS). __IMPORTANT:__ The "swcomp" command-line tool is NOT intended for general use. From c0b07cfd4cc087f206782a66689f0860f78575e6 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sun, 15 Dec 2024 11:23:04 +0200 Subject: [PATCH 12/70] Use non-deprecated methods in SPM manifest --- Package.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Package.swift b/Package.swift index af6070d3..6fba78b0 100644 --- a/Package.swift +++ b/Package.swift @@ -17,8 +17,7 @@ var package = Package( targets: ["SWCompression"]), ], dependencies: [ - .package(name: "BitByteData", url: "https://github.com/tsolomko/BitByteData", - from: "2.0.0"), + .package(url: "https://github.com/tsolomko/BitByteData", from: "2.0.0"), ], targets: [ .target( @@ -33,7 +32,7 @@ var package = Package( ) #if os(macOS) -package.dependencies.append(.package(name: "SwiftCLI", url: "https://github.com/jakeheis/SwiftCLI", from: "6.0.0")) +package.dependencies.append(.package(url: "https://github.com/jakeheis/SwiftCLI", from: "6.0.0")) package.targets.append(.executableTarget(name: "swcomp", dependencies: ["SWCompression", "SwiftCLI"], path: "Sources", exclude: ["Common", "7-Zip", "BZip2", "Deflate", "GZip", "LZ4", "LZMA", "LZMA2", "TAR", "XZ", "ZIP", "Zlib", "PrivacyInfo.xcprivacy"], sources: ["swcomp"])) From 5ff7317a2f978b0686cf0148623bcfd69dc0e977 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sun, 15 Dec 2024 11:24:55 +0200 Subject: [PATCH 13/70] [Tests] Fix modifying data in memory crashes on Swift 5.9+ The option mappedIfSafe used in loading test data seems to be actually mapping data into memory, which makes modifying data in place cause crashes. --- Tests/Constants.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Constants.swift b/Tests/Constants.swift index 12d89bf7..fcd42794 100644 --- a/Tests/Constants.swift +++ b/Tests/Constants.swift @@ -21,7 +21,7 @@ class Constants { static func data(forTest name: String, withType ext: String) throws -> Data { let url = Constants.url(forTest: name, withType: ext) - return try Data(contentsOf: url, options: .mappedIfSafe) + return try Data(contentsOf: url) } static func handle(forTest name: String, withType ext: String) throws -> FileHandle { @@ -35,7 +35,7 @@ class Constants { static func data(forAnswer name: String) throws -> Data { let url = Constants.url(forAnswer: name) - return try Data(contentsOf: url, options: .mappedIfSafe) + return try Data(contentsOf: url) } private static func url(forAnswer name: String) -> URL { From 17c0a1e52c533edc962d3584aa045eafc79406e4 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sun, 15 Dec 2024 11:25:47 +0200 Subject: [PATCH 14/70] [CI] Update ios simulators for Swift 5.9 and 5.10 --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 73ac5769..2db0880e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -28,12 +28,12 @@ stages: macosSwift59: imageName: 'macOS-14' DEVELOPER_DIR: '/Applications/Xcode_15.2.app' - IOS_SIMULATOR: 'iPhone 14' + IOS_SIMULATOR: 'iPhone 15' WATCHOS_SIMULATOR: 'Apple Watch Series 6 (44mm)' macosSwift510: imageName: 'macOS-15' DEVELOPER_DIR: '/Applications/Xcode_15.4.app' - IOS_SIMULATOR: 'iPhone 14' + IOS_SIMULATOR: 'iPhone 15' WATCHOS_SIMULATOR: 'Apple Watch Series 6 (44mm)' macosSwift60: imageName: 'macOS-15' From e71c7d88f350b90365395c3fd6af61082344ee18 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sun, 15 Dec 2024 19:35:13 +0200 Subject: [PATCH 15/70] [CI] Use docker for broken windows builds --- azure-pipelines.yml | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2db0880e..a075d1f1 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -121,18 +121,6 @@ stages: ICU_PATH: 'C:\Program Files\swift\icu-69.1\usr\bin' SWIFT_DEV_PATH: 'C:\Program Files\swift\runtime-development\usr\bin' XCTEST_LIB_PATH: '\x86_64' - windowsSwift510: - imageName: 'windows-2019' - SWIFT_VERSION: '5.10.1' - ICU_PATH: 'C:\Program Files\swift\icu-69.1\usr\bin' - SWIFT_DEV_PATH: 'C:\Program Files\swift\runtime-development\usr\bin' - XCTEST_LIB_PATH: '\x86_64' - windowsSwift60: - imageName: 'windows-2019' - SWIFT_VERSION: '6.0.3' - ICU_PATH: 'C:\Program Files\swift\icu-69.1\usr\bin' - SWIFT_DEV_PATH: 'C:\Program Files\swift\runtime-development\usr\bin' - XCTEST_LIB_PATH: '\x86_64' pool: vmImage: $(imageName) variables: @@ -151,8 +139,26 @@ stages: - script: | set PATH=C:\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr\bin;%SWIFT_DEV_PATH%;%ICU_PATH%;%LocalAppData%\Programs\Swift;%PATH% swift.exe --version - swift.exe build --target SWCompression %SWIFTFLAGS% - swift.exe build -c release --target SWCompression %SWIFTFLAGS% + swift.exe build %SWIFTFLAGS% + swift.exe build -c release %SWIFTFLAGS% + displayName: 'Build SPM Debug & Release' + - job: windowsDocker + strategy: + matrix: + windowsSwift510: + imageName: 'windows-2022' + containerImage: 'swift:5.10.1-windowsservercore-ltsc2022' + windowsSwift60: + imageName: 'windows-2022' + containerImage: 'swift:6.0.3-windowsservercore-ltsc2022' + pool: + vmImage: $(imageName) + container: $[ variables['containerImage'] ] + steps: + - script: | + swift.exe --version + swift.exe build + swift.exe build -c release displayName: 'Build SPM Debug & Release' - stage: deploy displayName: Deploy From ea9aaccfb9804e6821fd7310fdeab61ba112c546 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sat, 31 Jan 2026 15:30:24 +0800 Subject: [PATCH 16/70] Update copyright year to 2026 --- .jazzy.yaml | 2 +- LICENSE | 2 +- SWCompression.xcodeproj/SWCompression.plist | 2 +- Sources/7-Zip/7zCoder+Equatable.swift | 2 +- Sources/7-Zip/7zCoder.swift | 2 +- Sources/7-Zip/7zCoderInfo.swift | 2 +- Sources/7-Zip/7zContainer.swift | 2 +- Sources/7-Zip/7zEntry.swift | 2 +- Sources/7-Zip/7zEntryInfo.swift | 2 +- Sources/7-Zip/7zError.swift | 2 +- Sources/7-Zip/7zFileInfo.swift | 2 +- Sources/7-Zip/7zFolder.swift | 2 +- Sources/7-Zip/7zHeader.swift | 2 +- Sources/7-Zip/7zPackInfo.swift | 2 +- Sources/7-Zip/7zProperty.swift | 2 +- Sources/7-Zip/7zStreamInfo.swift | 2 +- Sources/7-Zip/7zSubstreamInfo.swift | 2 +- Sources/7-Zip/CompressionMethod+7z.swift | 2 +- Sources/7-Zip/MsbBitReader+7z.swift | 2 +- Sources/BZip2/BZip2+BlockSize.swift | 2 +- Sources/BZip2/BZip2+Compress.swift | 2 +- Sources/BZip2/BZip2+Lengths.swift | 2 +- Sources/BZip2/BZip2.swift | 2 +- Sources/BZip2/BZip2Error.swift | 2 +- Sources/BZip2/BurrowsWheeler.swift | 2 +- Sources/BZip2/SuffixArray.swift | 2 +- Sources/Common/Archive.swift | 2 +- Sources/Common/CheckSums.swift | 2 +- Sources/Common/CodingTree/Code.swift | 2 +- Sources/Common/CodingTree/CodeLength.swift | 2 +- Sources/Common/CodingTree/DecodingTree.swift | 2 +- Sources/Common/CodingTree/EncodingTree.swift | 2 +- Sources/Common/CompressionAlgorithm.swift | 2 +- Sources/Common/CompressionMethod.swift | 2 +- Sources/Common/Container/Container.swift | 2 +- Sources/Common/Container/ContainerEntry.swift | 2 +- Sources/Common/Container/ContainerEntryInfo.swift | 2 +- Sources/Common/Container/ContainerEntryType.swift | 2 +- Sources/Common/Container/DosAttributes.swift | 2 +- Sources/Common/Container/Permissions.swift | 2 +- Sources/Common/DataError.swift | 2 +- Sources/Common/DecompressionAlgorithm.swift | 2 +- Sources/Common/DeltaFilter.swift | 2 +- Sources/Common/Extensions.swift | 2 +- Sources/Common/FileSystemType.swift | 2 +- Sources/Deflate/Deflate+Compress.swift | 2 +- Sources/Deflate/Deflate+Constants.swift | 2 +- Sources/Deflate/Deflate+Lengths.swift | 2 +- Sources/Deflate/Deflate.swift | 2 +- Sources/Deflate/DeflateError.swift | 2 +- Sources/GZip/FileSystemType+Gzip.swift | 2 +- Sources/GZip/GzipArchive.swift | 2 +- Sources/GZip/GzipError.swift | 2 +- Sources/GZip/GzipHeader+ExtraField.swift | 2 +- Sources/GZip/GzipHeader.swift | 2 +- Sources/LZ4/LZ4+Compress.swift | 2 +- Sources/LZ4/LZ4.swift | 2 +- Sources/LZ4/XxHash32.swift | 2 +- Sources/LZMA/LZMA.swift | 2 +- Sources/LZMA/LZMABitTreeDecoder.swift | 2 +- Sources/LZMA/LZMAConstants.swift | 2 +- Sources/LZMA/LZMADecoder.swift | 2 +- Sources/LZMA/LZMAError.swift | 2 +- Sources/LZMA/LZMALenDecoder.swift | 2 +- Sources/LZMA/LZMAProperties.swift | 2 +- Sources/LZMA/LZMARangeDecoder.swift | 2 +- Sources/LZMA2/LZMA2.swift | 2 +- Sources/LZMA2/LZMA2Decoder.swift | 2 +- Sources/LZMA2/LZMA2Error.swift | 2 +- Sources/TAR/ContainerEntryType+Tar.swift | 2 +- Sources/TAR/Data+Tar.swift | 2 +- Sources/TAR/LittleEndianByteReader+Tar.swift | 2 +- Sources/TAR/TarContainer.swift | 2 +- Sources/TAR/TarCreateError.swift | 2 +- Sources/TAR/TarEntry.swift | 2 +- Sources/TAR/TarEntryInfo.swift | 2 +- Sources/TAR/TarError.swift | 2 +- Sources/TAR/TarExtendedHeader.swift | 2 +- Sources/TAR/TarHeader.swift | 2 +- Sources/TAR/TarParser.swift | 2 +- Sources/TAR/TarReader.swift | 2 +- Sources/TAR/TarWriter.swift | 2 +- Sources/XZ/ByteReader+XZ.swift | 2 +- Sources/XZ/Sha256.swift | 2 +- Sources/XZ/XZArchive.swift | 2 +- Sources/XZ/XZBlock.swift | 2 +- Sources/XZ/XZError.swift | 2 +- Sources/XZ/XZStreamHeader.swift | 2 +- Sources/ZIP/BuiltinExtraFields.swift | 2 +- Sources/ZIP/CompressionMethod+Zip.swift | 2 +- Sources/ZIP/FileSystemType+Zip.swift | 2 +- Sources/ZIP/LittleEndianByteReader+Zip.swift | 2 +- Sources/ZIP/ZipCentralDirectoryEntry.swift | 2 +- Sources/ZIP/ZipContainer.swift | 2 +- Sources/ZIP/ZipEndOfCentralDirectory.swift | 2 +- Sources/ZIP/ZipEntry.swift | 2 +- Sources/ZIP/ZipEntryInfo.swift | 2 +- Sources/ZIP/ZipEntryInfoHelper.swift | 2 +- Sources/ZIP/ZipError.swift | 2 +- Sources/ZIP/ZipExtraField.swift | 2 +- Sources/ZIP/ZipLocalHeader.swift | 2 +- Sources/Zlib/ZlibArchive.swift | 2 +- Sources/Zlib/ZlibError.swift | 2 +- Sources/Zlib/ZlibHeader.swift | 2 +- Sources/swcomp/Archives/BZip2Command.swift | 2 +- Sources/swcomp/Archives/GZipCommand.swift | 2 +- Sources/swcomp/Archives/LZ4Command.swift | 2 +- Sources/swcomp/Archives/LZMACommand.swift | 2 +- Sources/swcomp/Archives/XZCommand.swift | 2 +- Sources/swcomp/Benchmarks/Benchmark.swift | 2 +- Sources/swcomp/Benchmarks/BenchmarkGroup.swift | 2 +- Sources/swcomp/Benchmarks/BenchmarkMetadata.swift | 2 +- Sources/swcomp/Benchmarks/BenchmarkResult.swift | 2 +- Sources/swcomp/Benchmarks/Benchmarks.swift | 2 +- Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift | 2 +- Sources/swcomp/Benchmarks/SaveFile.swift | 2 +- Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift | 2 +- Sources/swcomp/Benchmarks/SpeedFormatter.swift | 2 +- Sources/swcomp/Containers/7ZipCommand.swift | 2 +- Sources/swcomp/Containers/CommonFunctions.swift | 2 +- Sources/swcomp/Containers/ContainerCommand.swift | 2 +- Sources/swcomp/Containers/TarCommand.swift | 2 +- Sources/swcomp/Containers/ZipCommand.swift | 2 +- .../Extensions/CompressionMethod+CustomStringConvertible.swift | 2 +- .../Extensions/ContainerEntryInfo+CustomStringConvertible.swift | 2 +- .../Extensions/FileSystemType+CustomStringConvertible.swift | 2 +- .../swcomp/Extensions/GzipHeader+CustomStringConvertible.swift | 2 +- Sources/swcomp/Extensions/TarEntry+Create.swift | 2 +- Sources/swcomp/Extensions/TarFormat+ConvertibleFromString.swift | 2 +- Sources/swcomp/SwcompError.swift | 2 +- Sources/swcomp/main.swift | 2 +- Tests/BZip2CompressionTests.swift | 2 +- Tests/BZip2Tests.swift | 2 +- Tests/Constants.swift | 2 +- Tests/DeflateCompressionTests.swift | 2 +- Tests/GzipTests.swift | 2 +- Tests/LZ4CompressionTests.swift | 2 +- Tests/LZ4Tests.swift | 2 +- Tests/LzmaTests.swift | 2 +- Tests/SevenZipTests.swift | 2 +- Tests/Sha256Tests.swift | 2 +- Tests/TarCreateTests.swift | 2 +- Tests/TarReaderTests.swift | 2 +- Tests/TarTests.swift | 2 +- Tests/TarWriterTests.swift | 2 +- Tests/TestZipExtraField.swift | 2 +- Tests/XxHash32Tests.swift | 2 +- Tests/XzTests.swift | 2 +- Tests/ZipTests.swift | 2 +- Tests/ZlibTests.swift | 2 +- 150 files changed, 150 insertions(+), 150 deletions(-) diff --git a/.jazzy.yaml b/.jazzy.yaml index 23bfd367..d6975103 100644 --- a/.jazzy.yaml +++ b/.jazzy.yaml @@ -4,7 +4,7 @@ clean: false author: Timofey Solomko module: SWCompression module_version: 4.8.6 -copyright: '© 2024 Timofey Solomko' +copyright: '© 2026 Timofey Solomko' readme: README.md github_url: https://github.com/tsolomko/SWCompression github_file_prefix: https://github.com/tsolomko/SWCompression/tree/4.8.6 diff --git a/LICENSE b/LICENSE index 3dc58e8e..af3170d2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Timofey Solomko +Copyright (c) 2026 Timofey Solomko Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/SWCompression.xcodeproj/SWCompression.plist b/SWCompression.xcodeproj/SWCompression.plist index 3d1f3e26..ea15ef36 100644 --- a/SWCompression.xcodeproj/SWCompression.plist +++ b/SWCompression.xcodeproj/SWCompression.plist @@ -19,6 +19,6 @@ CFBundleVersion 91 NSHumanReadableCopyright - Copyright © 2024 Timofey Solomko + Copyright © 2026 Timofey Solomko diff --git a/Sources/7-Zip/7zCoder+Equatable.swift b/Sources/7-Zip/7zCoder+Equatable.swift index 466713f1..96ed9f68 100644 --- a/Sources/7-Zip/7zCoder+Equatable.swift +++ b/Sources/7-Zip/7zCoder+Equatable.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/7-Zip/7zCoder.swift b/Sources/7-Zip/7zCoder.swift index 19b76995..fc41c480 100644 --- a/Sources/7-Zip/7zCoder.swift +++ b/Sources/7-Zip/7zCoder.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/7-Zip/7zCoderInfo.swift b/Sources/7-Zip/7zCoderInfo.swift index 44255eb1..98ad29c9 100644 --- a/Sources/7-Zip/7zCoderInfo.swift +++ b/Sources/7-Zip/7zCoderInfo.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/7-Zip/7zContainer.swift b/Sources/7-Zip/7zContainer.swift index 0f251711..685d58d5 100644 --- a/Sources/7-Zip/7zContainer.swift +++ b/Sources/7-Zip/7zContainer.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/7-Zip/7zEntry.swift b/Sources/7-Zip/7zEntry.swift index f6a5e394..46139a20 100644 --- a/Sources/7-Zip/7zEntry.swift +++ b/Sources/7-Zip/7zEntry.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/7-Zip/7zEntryInfo.swift b/Sources/7-Zip/7zEntryInfo.swift index ddfb6d93..31b63fea 100644 --- a/Sources/7-Zip/7zEntryInfo.swift +++ b/Sources/7-Zip/7zEntryInfo.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/7-Zip/7zError.swift b/Sources/7-Zip/7zError.swift index 03fff69a..4b3492a0 100644 --- a/Sources/7-Zip/7zError.swift +++ b/Sources/7-Zip/7zError.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/7-Zip/7zFileInfo.swift b/Sources/7-Zip/7zFileInfo.swift index 03241362..6f45fc5f 100644 --- a/Sources/7-Zip/7zFileInfo.swift +++ b/Sources/7-Zip/7zFileInfo.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/7-Zip/7zFolder.swift b/Sources/7-Zip/7zFolder.swift index 3cc5ddec..d917a12e 100644 --- a/Sources/7-Zip/7zFolder.swift +++ b/Sources/7-Zip/7zFolder.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/7-Zip/7zHeader.swift b/Sources/7-Zip/7zHeader.swift index 1a50cdba..301e0e78 100644 --- a/Sources/7-Zip/7zHeader.swift +++ b/Sources/7-Zip/7zHeader.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/7-Zip/7zPackInfo.swift b/Sources/7-Zip/7zPackInfo.swift index 9998bcd6..fe8bb595 100644 --- a/Sources/7-Zip/7zPackInfo.swift +++ b/Sources/7-Zip/7zPackInfo.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/7-Zip/7zProperty.swift b/Sources/7-Zip/7zProperty.swift index b817adc4..e5751078 100644 --- a/Sources/7-Zip/7zProperty.swift +++ b/Sources/7-Zip/7zProperty.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/7-Zip/7zStreamInfo.swift b/Sources/7-Zip/7zStreamInfo.swift index f8bca63b..b7e82a19 100644 --- a/Sources/7-Zip/7zStreamInfo.swift +++ b/Sources/7-Zip/7zStreamInfo.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/7-Zip/7zSubstreamInfo.swift b/Sources/7-Zip/7zSubstreamInfo.swift index 1bcc715d..442ea2b0 100644 --- a/Sources/7-Zip/7zSubstreamInfo.swift +++ b/Sources/7-Zip/7zSubstreamInfo.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/7-Zip/CompressionMethod+7z.swift b/Sources/7-Zip/CompressionMethod+7z.swift index 103775ad..f5168bc9 100644 --- a/Sources/7-Zip/CompressionMethod+7z.swift +++ b/Sources/7-Zip/CompressionMethod+7z.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/7-Zip/MsbBitReader+7z.swift b/Sources/7-Zip/MsbBitReader+7z.swift index 55d4bfbf..70044388 100644 --- a/Sources/7-Zip/MsbBitReader+7z.swift +++ b/Sources/7-Zip/MsbBitReader+7z.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/BZip2/BZip2+BlockSize.swift b/Sources/BZip2/BZip2+BlockSize.swift index 63b310aa..215edbc0 100644 --- a/Sources/BZip2/BZip2+BlockSize.swift +++ b/Sources/BZip2/BZip2+BlockSize.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/BZip2/BZip2+Compress.swift b/Sources/BZip2/BZip2+Compress.swift index 676e971e..ed4cf7dc 100644 --- a/Sources/BZip2/BZip2+Compress.swift +++ b/Sources/BZip2/BZip2+Compress.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/BZip2/BZip2+Lengths.swift b/Sources/BZip2/BZip2+Lengths.swift index 824307d6..799507f8 100644 --- a/Sources/BZip2/BZip2+Lengths.swift +++ b/Sources/BZip2/BZip2+Lengths.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/BZip2/BZip2.swift b/Sources/BZip2/BZip2.swift index 2bc26727..efbb844b 100644 --- a/Sources/BZip2/BZip2.swift +++ b/Sources/BZip2/BZip2.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/BZip2/BZip2Error.swift b/Sources/BZip2/BZip2Error.swift index e5ee5579..c58b90c9 100644 --- a/Sources/BZip2/BZip2Error.swift +++ b/Sources/BZip2/BZip2Error.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/BZip2/BurrowsWheeler.swift b/Sources/BZip2/BurrowsWheeler.swift index 59ff64e5..80cae086 100644 --- a/Sources/BZip2/BurrowsWheeler.swift +++ b/Sources/BZip2/BurrowsWheeler.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/BZip2/SuffixArray.swift b/Sources/BZip2/SuffixArray.swift index 5b1c0f26..6c7018d1 100644 --- a/Sources/BZip2/SuffixArray.swift +++ b/Sources/BZip2/SuffixArray.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Common/Archive.swift b/Sources/Common/Archive.swift index 1daca0d5..a0930705 100644 --- a/Sources/Common/Archive.swift +++ b/Sources/Common/Archive.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Common/CheckSums.swift b/Sources/Common/CheckSums.swift index 5b8c7ddc..047cbe7a 100644 --- a/Sources/Common/CheckSums.swift +++ b/Sources/Common/CheckSums.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Common/CodingTree/Code.swift b/Sources/Common/CodingTree/Code.swift index 8992126f..fcbe7999 100644 --- a/Sources/Common/CodingTree/Code.swift +++ b/Sources/Common/CodingTree/Code.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Common/CodingTree/CodeLength.swift b/Sources/Common/CodingTree/CodeLength.swift index eabfa41a..21bf5ff6 100644 --- a/Sources/Common/CodingTree/CodeLength.swift +++ b/Sources/Common/CodingTree/CodeLength.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Common/CodingTree/DecodingTree.swift b/Sources/Common/CodingTree/DecodingTree.swift index a502baa1..8149f3e9 100644 --- a/Sources/Common/CodingTree/DecodingTree.swift +++ b/Sources/Common/CodingTree/DecodingTree.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Common/CodingTree/EncodingTree.swift b/Sources/Common/CodingTree/EncodingTree.swift index 3fb51133..9c5125d4 100644 --- a/Sources/Common/CodingTree/EncodingTree.swift +++ b/Sources/Common/CodingTree/EncodingTree.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Common/CompressionAlgorithm.swift b/Sources/Common/CompressionAlgorithm.swift index 1ae63323..e3dc9a77 100644 --- a/Sources/Common/CompressionAlgorithm.swift +++ b/Sources/Common/CompressionAlgorithm.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Common/CompressionMethod.swift b/Sources/Common/CompressionMethod.swift index 1b796ff4..cede6f79 100644 --- a/Sources/Common/CompressionMethod.swift +++ b/Sources/Common/CompressionMethod.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Common/Container/Container.swift b/Sources/Common/Container/Container.swift index f700655e..a2ef214b 100644 --- a/Sources/Common/Container/Container.swift +++ b/Sources/Common/Container/Container.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Common/Container/ContainerEntry.swift b/Sources/Common/Container/ContainerEntry.swift index def4a7e0..f0e58c25 100644 --- a/Sources/Common/Container/ContainerEntry.swift +++ b/Sources/Common/Container/ContainerEntry.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Common/Container/ContainerEntryInfo.swift b/Sources/Common/Container/ContainerEntryInfo.swift index 14d5a8dd..7b8c653f 100644 --- a/Sources/Common/Container/ContainerEntryInfo.swift +++ b/Sources/Common/Container/ContainerEntryInfo.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Common/Container/ContainerEntryType.swift b/Sources/Common/Container/ContainerEntryType.swift index 09578c22..441de008 100644 --- a/Sources/Common/Container/ContainerEntryType.swift +++ b/Sources/Common/Container/ContainerEntryType.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Common/Container/DosAttributes.swift b/Sources/Common/Container/DosAttributes.swift index 48d7cd7e..fad1f7b1 100644 --- a/Sources/Common/Container/DosAttributes.swift +++ b/Sources/Common/Container/DosAttributes.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Common/Container/Permissions.swift b/Sources/Common/Container/Permissions.swift index 1286a095..d476395f 100644 --- a/Sources/Common/Container/Permissions.swift +++ b/Sources/Common/Container/Permissions.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Common/DataError.swift b/Sources/Common/DataError.swift index f02e111c..bcccd831 100644 --- a/Sources/Common/DataError.swift +++ b/Sources/Common/DataError.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Common/DecompressionAlgorithm.swift b/Sources/Common/DecompressionAlgorithm.swift index 254f03ba..9fa30b5c 100644 --- a/Sources/Common/DecompressionAlgorithm.swift +++ b/Sources/Common/DecompressionAlgorithm.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Common/DeltaFilter.swift b/Sources/Common/DeltaFilter.swift index e75bc14f..795a3fe6 100644 --- a/Sources/Common/DeltaFilter.swift +++ b/Sources/Common/DeltaFilter.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Common/Extensions.swift b/Sources/Common/Extensions.swift index bb602e29..ea8ba358 100644 --- a/Sources/Common/Extensions.swift +++ b/Sources/Common/Extensions.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Common/FileSystemType.swift b/Sources/Common/FileSystemType.swift index 97a2dc55..7763f5ce 100644 --- a/Sources/Common/FileSystemType.swift +++ b/Sources/Common/FileSystemType.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Deflate/Deflate+Compress.swift b/Sources/Deflate/Deflate+Compress.swift index f7842601..1bb39043 100644 --- a/Sources/Deflate/Deflate+Compress.swift +++ b/Sources/Deflate/Deflate+Compress.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Deflate/Deflate+Constants.swift b/Sources/Deflate/Deflate+Constants.swift index 0c4c9ff3..ca7a2cea 100644 --- a/Sources/Deflate/Deflate+Constants.swift +++ b/Sources/Deflate/Deflate+Constants.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Deflate/Deflate+Lengths.swift b/Sources/Deflate/Deflate+Lengths.swift index dd265c6b..815d7f5d 100644 --- a/Sources/Deflate/Deflate+Lengths.swift +++ b/Sources/Deflate/Deflate+Lengths.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Deflate/Deflate.swift b/Sources/Deflate/Deflate.swift index 56b6cf4c..c49a3504 100644 --- a/Sources/Deflate/Deflate.swift +++ b/Sources/Deflate/Deflate.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Deflate/DeflateError.swift b/Sources/Deflate/DeflateError.swift index 802e5c30..439e39aa 100644 --- a/Sources/Deflate/DeflateError.swift +++ b/Sources/Deflate/DeflateError.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/GZip/FileSystemType+Gzip.swift b/Sources/GZip/FileSystemType+Gzip.swift index 6bdbf6b3..d9a98280 100644 --- a/Sources/GZip/FileSystemType+Gzip.swift +++ b/Sources/GZip/FileSystemType+Gzip.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/GZip/GzipArchive.swift b/Sources/GZip/GzipArchive.swift index 32bd9701..8b09dd0a 100644 --- a/Sources/GZip/GzipArchive.swift +++ b/Sources/GZip/GzipArchive.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/GZip/GzipError.swift b/Sources/GZip/GzipError.swift index 6e037c30..2622dc4f 100644 --- a/Sources/GZip/GzipError.swift +++ b/Sources/GZip/GzipError.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/GZip/GzipHeader+ExtraField.swift b/Sources/GZip/GzipHeader+ExtraField.swift index b897c0b1..338c0cb8 100644 --- a/Sources/GZip/GzipHeader+ExtraField.swift +++ b/Sources/GZip/GzipHeader+ExtraField.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/GZip/GzipHeader.swift b/Sources/GZip/GzipHeader.swift index 03b7098b..e3ea37fc 100644 --- a/Sources/GZip/GzipHeader.swift +++ b/Sources/GZip/GzipHeader.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/LZ4/LZ4+Compress.swift b/Sources/LZ4/LZ4+Compress.swift index dfb57743..b0edcb4e 100644 --- a/Sources/LZ4/LZ4+Compress.swift +++ b/Sources/LZ4/LZ4+Compress.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/LZ4/LZ4.swift b/Sources/LZ4/LZ4.swift index aeb1c7c1..6d4aef2c 100644 --- a/Sources/LZ4/LZ4.swift +++ b/Sources/LZ4/LZ4.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/LZ4/XxHash32.swift b/Sources/LZ4/XxHash32.swift index 768c9b11..260b68ff 100644 --- a/Sources/LZ4/XxHash32.swift +++ b/Sources/LZ4/XxHash32.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/LZMA/LZMA.swift b/Sources/LZMA/LZMA.swift index 6d7bbe7a..a75a6444 100644 --- a/Sources/LZMA/LZMA.swift +++ b/Sources/LZMA/LZMA.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/LZMA/LZMABitTreeDecoder.swift b/Sources/LZMA/LZMABitTreeDecoder.swift index 8bd30f99..65ac172a 100644 --- a/Sources/LZMA/LZMABitTreeDecoder.swift +++ b/Sources/LZMA/LZMABitTreeDecoder.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/LZMA/LZMAConstants.swift b/Sources/LZMA/LZMAConstants.swift index ebea1b77..e686c5a4 100644 --- a/Sources/LZMA/LZMAConstants.swift +++ b/Sources/LZMA/LZMAConstants.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/LZMA/LZMADecoder.swift b/Sources/LZMA/LZMADecoder.swift index 2564fd5f..095c1e14 100644 --- a/Sources/LZMA/LZMADecoder.swift +++ b/Sources/LZMA/LZMADecoder.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/LZMA/LZMAError.swift b/Sources/LZMA/LZMAError.swift index 95f151ca..10175b84 100644 --- a/Sources/LZMA/LZMAError.swift +++ b/Sources/LZMA/LZMAError.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/LZMA/LZMALenDecoder.swift b/Sources/LZMA/LZMALenDecoder.swift index 271d9c68..6c79a6ae 100644 --- a/Sources/LZMA/LZMALenDecoder.swift +++ b/Sources/LZMA/LZMALenDecoder.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/LZMA/LZMAProperties.swift b/Sources/LZMA/LZMAProperties.swift index 0a81083b..340a586f 100644 --- a/Sources/LZMA/LZMAProperties.swift +++ b/Sources/LZMA/LZMAProperties.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/LZMA/LZMARangeDecoder.swift b/Sources/LZMA/LZMARangeDecoder.swift index e43e21a5..91b896b8 100644 --- a/Sources/LZMA/LZMARangeDecoder.swift +++ b/Sources/LZMA/LZMARangeDecoder.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/LZMA2/LZMA2.swift b/Sources/LZMA2/LZMA2.swift index 46cbe674..501396d9 100644 --- a/Sources/LZMA2/LZMA2.swift +++ b/Sources/LZMA2/LZMA2.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/LZMA2/LZMA2Decoder.swift b/Sources/LZMA2/LZMA2Decoder.swift index d467d3f8..416271c8 100644 --- a/Sources/LZMA2/LZMA2Decoder.swift +++ b/Sources/LZMA2/LZMA2Decoder.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/LZMA2/LZMA2Error.swift b/Sources/LZMA2/LZMA2Error.swift index 636b513c..af0c3472 100644 --- a/Sources/LZMA2/LZMA2Error.swift +++ b/Sources/LZMA2/LZMA2Error.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/TAR/ContainerEntryType+Tar.swift b/Sources/TAR/ContainerEntryType+Tar.swift index 1602d3be..3d1e5b56 100644 --- a/Sources/TAR/ContainerEntryType+Tar.swift +++ b/Sources/TAR/ContainerEntryType+Tar.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/TAR/Data+Tar.swift b/Sources/TAR/Data+Tar.swift index 984c6324..8dbfdd3e 100644 --- a/Sources/TAR/Data+Tar.swift +++ b/Sources/TAR/Data+Tar.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/TAR/LittleEndianByteReader+Tar.swift b/Sources/TAR/LittleEndianByteReader+Tar.swift index 5f22a051..4ef2a45a 100644 --- a/Sources/TAR/LittleEndianByteReader+Tar.swift +++ b/Sources/TAR/LittleEndianByteReader+Tar.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/TAR/TarContainer.swift b/Sources/TAR/TarContainer.swift index 8c2db113..dc9ddaeb 100644 --- a/Sources/TAR/TarContainer.swift +++ b/Sources/TAR/TarContainer.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/TAR/TarCreateError.swift b/Sources/TAR/TarCreateError.swift index 61ead2bc..072f7acf 100644 --- a/Sources/TAR/TarCreateError.swift +++ b/Sources/TAR/TarCreateError.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/TAR/TarEntry.swift b/Sources/TAR/TarEntry.swift index 06625758..ea685eb6 100644 --- a/Sources/TAR/TarEntry.swift +++ b/Sources/TAR/TarEntry.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/TAR/TarEntryInfo.swift b/Sources/TAR/TarEntryInfo.swift index 18b3ed24..0f4842e6 100644 --- a/Sources/TAR/TarEntryInfo.swift +++ b/Sources/TAR/TarEntryInfo.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/TAR/TarError.swift b/Sources/TAR/TarError.swift index 2b9fe91e..0dcce194 100644 --- a/Sources/TAR/TarError.swift +++ b/Sources/TAR/TarError.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/TAR/TarExtendedHeader.swift b/Sources/TAR/TarExtendedHeader.swift index aa052c10..1ff9becb 100644 --- a/Sources/TAR/TarExtendedHeader.swift +++ b/Sources/TAR/TarExtendedHeader.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/TAR/TarHeader.swift b/Sources/TAR/TarHeader.swift index fb4b660c..95f1f950 100644 --- a/Sources/TAR/TarHeader.swift +++ b/Sources/TAR/TarHeader.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/TAR/TarParser.swift b/Sources/TAR/TarParser.swift index 327d3db0..a7bc1b23 100644 --- a/Sources/TAR/TarParser.swift +++ b/Sources/TAR/TarParser.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/TAR/TarReader.swift b/Sources/TAR/TarReader.swift index e7603a27..f961e458 100644 --- a/Sources/TAR/TarReader.swift +++ b/Sources/TAR/TarReader.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/TAR/TarWriter.swift b/Sources/TAR/TarWriter.swift index 5ffb8416..8003a83f 100644 --- a/Sources/TAR/TarWriter.swift +++ b/Sources/TAR/TarWriter.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/XZ/ByteReader+XZ.swift b/Sources/XZ/ByteReader+XZ.swift index b8de7d96..94bd8890 100644 --- a/Sources/XZ/ByteReader+XZ.swift +++ b/Sources/XZ/ByteReader+XZ.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/XZ/Sha256.swift b/Sources/XZ/Sha256.swift index cca5894a..dc15f39d 100644 --- a/Sources/XZ/Sha256.swift +++ b/Sources/XZ/Sha256.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/XZ/XZArchive.swift b/Sources/XZ/XZArchive.swift index bfc275ac..48ccbd97 100644 --- a/Sources/XZ/XZArchive.swift +++ b/Sources/XZ/XZArchive.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/XZ/XZBlock.swift b/Sources/XZ/XZBlock.swift index 0c984df7..3fe2f3ce 100644 --- a/Sources/XZ/XZBlock.swift +++ b/Sources/XZ/XZBlock.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/XZ/XZError.swift b/Sources/XZ/XZError.swift index 5a6b52a2..8a301a6b 100644 --- a/Sources/XZ/XZError.swift +++ b/Sources/XZ/XZError.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/XZ/XZStreamHeader.swift b/Sources/XZ/XZStreamHeader.swift index be5b581a..333f78f7 100644 --- a/Sources/XZ/XZStreamHeader.swift +++ b/Sources/XZ/XZStreamHeader.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/ZIP/BuiltinExtraFields.swift b/Sources/ZIP/BuiltinExtraFields.swift index 345af697..70293b27 100644 --- a/Sources/ZIP/BuiltinExtraFields.swift +++ b/Sources/ZIP/BuiltinExtraFields.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/ZIP/CompressionMethod+Zip.swift b/Sources/ZIP/CompressionMethod+Zip.swift index bf91ba89..c467859e 100644 --- a/Sources/ZIP/CompressionMethod+Zip.swift +++ b/Sources/ZIP/CompressionMethod+Zip.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/ZIP/FileSystemType+Zip.swift b/Sources/ZIP/FileSystemType+Zip.swift index a0e93e83..3ecb572f 100644 --- a/Sources/ZIP/FileSystemType+Zip.swift +++ b/Sources/ZIP/FileSystemType+Zip.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/ZIP/LittleEndianByteReader+Zip.swift b/Sources/ZIP/LittleEndianByteReader+Zip.swift index b70fb66a..e5f8b1da 100644 --- a/Sources/ZIP/LittleEndianByteReader+Zip.swift +++ b/Sources/ZIP/LittleEndianByteReader+Zip.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/ZIP/ZipCentralDirectoryEntry.swift b/Sources/ZIP/ZipCentralDirectoryEntry.swift index 0a996ed2..b6ff0bd8 100644 --- a/Sources/ZIP/ZipCentralDirectoryEntry.swift +++ b/Sources/ZIP/ZipCentralDirectoryEntry.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/ZIP/ZipContainer.swift b/Sources/ZIP/ZipContainer.swift index 8d555b30..97c14ffd 100644 --- a/Sources/ZIP/ZipContainer.swift +++ b/Sources/ZIP/ZipContainer.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/ZIP/ZipEndOfCentralDirectory.swift b/Sources/ZIP/ZipEndOfCentralDirectory.swift index 4a7c135b..36da217e 100644 --- a/Sources/ZIP/ZipEndOfCentralDirectory.swift +++ b/Sources/ZIP/ZipEndOfCentralDirectory.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/ZIP/ZipEntry.swift b/Sources/ZIP/ZipEntry.swift index b29cb215..05cb9953 100644 --- a/Sources/ZIP/ZipEntry.swift +++ b/Sources/ZIP/ZipEntry.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/ZIP/ZipEntryInfo.swift b/Sources/ZIP/ZipEntryInfo.swift index 7499f1b3..927db60b 100644 --- a/Sources/ZIP/ZipEntryInfo.swift +++ b/Sources/ZIP/ZipEntryInfo.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/ZIP/ZipEntryInfoHelper.swift b/Sources/ZIP/ZipEntryInfoHelper.swift index 9d61185d..4ebf00ea 100644 --- a/Sources/ZIP/ZipEntryInfoHelper.swift +++ b/Sources/ZIP/ZipEntryInfoHelper.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/ZIP/ZipError.swift b/Sources/ZIP/ZipError.swift index be19107f..b6610b2b 100644 --- a/Sources/ZIP/ZipError.swift +++ b/Sources/ZIP/ZipError.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/ZIP/ZipExtraField.swift b/Sources/ZIP/ZipExtraField.swift index b9b4b5cc..ad04bc99 100644 --- a/Sources/ZIP/ZipExtraField.swift +++ b/Sources/ZIP/ZipExtraField.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/ZIP/ZipLocalHeader.swift b/Sources/ZIP/ZipLocalHeader.swift index fe93f56e..37366f7d 100644 --- a/Sources/ZIP/ZipLocalHeader.swift +++ b/Sources/ZIP/ZipLocalHeader.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Zlib/ZlibArchive.swift b/Sources/Zlib/ZlibArchive.swift index 043629be..f4b6822e 100644 --- a/Sources/Zlib/ZlibArchive.swift +++ b/Sources/Zlib/ZlibArchive.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Zlib/ZlibError.swift b/Sources/Zlib/ZlibError.swift index 1f0f5fad..55681a30 100644 --- a/Sources/Zlib/ZlibError.swift +++ b/Sources/Zlib/ZlibError.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/Zlib/ZlibHeader.swift b/Sources/Zlib/ZlibHeader.swift index 000a03f8..4de8fe35 100644 --- a/Sources/Zlib/ZlibHeader.swift +++ b/Sources/Zlib/ZlibHeader.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Archives/BZip2Command.swift b/Sources/swcomp/Archives/BZip2Command.swift index ab69d86f..dd17dbd3 100644 --- a/Sources/swcomp/Archives/BZip2Command.swift +++ b/Sources/swcomp/Archives/BZip2Command.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Archives/GZipCommand.swift b/Sources/swcomp/Archives/GZipCommand.swift index 0fbbdf03..ad8d0a25 100644 --- a/Sources/swcomp/Archives/GZipCommand.swift +++ b/Sources/swcomp/Archives/GZipCommand.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Archives/LZ4Command.swift b/Sources/swcomp/Archives/LZ4Command.swift index 6f0c6abb..e9c1f272 100644 --- a/Sources/swcomp/Archives/LZ4Command.swift +++ b/Sources/swcomp/Archives/LZ4Command.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Archives/LZMACommand.swift b/Sources/swcomp/Archives/LZMACommand.swift index 08d7821a..22c00170 100644 --- a/Sources/swcomp/Archives/LZMACommand.swift +++ b/Sources/swcomp/Archives/LZMACommand.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Archives/XZCommand.swift b/Sources/swcomp/Archives/XZCommand.swift index e0cd6a15..b330cf5c 100644 --- a/Sources/swcomp/Archives/XZCommand.swift +++ b/Sources/swcomp/Archives/XZCommand.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Benchmarks/Benchmark.swift b/Sources/swcomp/Benchmarks/Benchmark.swift index 6b4c2c3a..3f353e97 100644 --- a/Sources/swcomp/Benchmarks/Benchmark.swift +++ b/Sources/swcomp/Benchmarks/Benchmark.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Benchmarks/BenchmarkGroup.swift b/Sources/swcomp/Benchmarks/BenchmarkGroup.swift index 4e9707fd..2852c053 100644 --- a/Sources/swcomp/Benchmarks/BenchmarkGroup.swift +++ b/Sources/swcomp/Benchmarks/BenchmarkGroup.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Benchmarks/BenchmarkMetadata.swift b/Sources/swcomp/Benchmarks/BenchmarkMetadata.swift index c035a361..4f42f057 100644 --- a/Sources/swcomp/Benchmarks/BenchmarkMetadata.swift +++ b/Sources/swcomp/Benchmarks/BenchmarkMetadata.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Benchmarks/BenchmarkResult.swift b/Sources/swcomp/Benchmarks/BenchmarkResult.swift index 99d347e0..e32563dc 100644 --- a/Sources/swcomp/Benchmarks/BenchmarkResult.swift +++ b/Sources/swcomp/Benchmarks/BenchmarkResult.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Benchmarks/Benchmarks.swift b/Sources/swcomp/Benchmarks/Benchmarks.swift index 44f3fc06..448ab359 100644 --- a/Sources/swcomp/Benchmarks/Benchmarks.swift +++ b/Sources/swcomp/Benchmarks/Benchmarks.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift b/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift index ae9c95f5..8b6bed42 100644 --- a/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift +++ b/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Benchmarks/SaveFile.swift b/Sources/swcomp/Benchmarks/SaveFile.swift index 6dd7a434..ac3b859f 100644 --- a/Sources/swcomp/Benchmarks/SaveFile.swift +++ b/Sources/swcomp/Benchmarks/SaveFile.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift b/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift index bf023699..6072869b 100644 --- a/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift +++ b/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Benchmarks/SpeedFormatter.swift b/Sources/swcomp/Benchmarks/SpeedFormatter.swift index 8a2e9ad7..3f496601 100644 --- a/Sources/swcomp/Benchmarks/SpeedFormatter.swift +++ b/Sources/swcomp/Benchmarks/SpeedFormatter.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Containers/7ZipCommand.swift b/Sources/swcomp/Containers/7ZipCommand.swift index 3fdb8875..b4015615 100644 --- a/Sources/swcomp/Containers/7ZipCommand.swift +++ b/Sources/swcomp/Containers/7ZipCommand.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Containers/CommonFunctions.swift b/Sources/swcomp/Containers/CommonFunctions.swift index a5b9bd8c..b41c8216 100644 --- a/Sources/swcomp/Containers/CommonFunctions.swift +++ b/Sources/swcomp/Containers/CommonFunctions.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Containers/ContainerCommand.swift b/Sources/swcomp/Containers/ContainerCommand.swift index d2a074a7..3fc09788 100644 --- a/Sources/swcomp/Containers/ContainerCommand.swift +++ b/Sources/swcomp/Containers/ContainerCommand.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Containers/TarCommand.swift b/Sources/swcomp/Containers/TarCommand.swift index e58b8f44..b706c83c 100644 --- a/Sources/swcomp/Containers/TarCommand.swift +++ b/Sources/swcomp/Containers/TarCommand.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Containers/ZipCommand.swift b/Sources/swcomp/Containers/ZipCommand.swift index ddf19124..7d685ca6 100644 --- a/Sources/swcomp/Containers/ZipCommand.swift +++ b/Sources/swcomp/Containers/ZipCommand.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Extensions/CompressionMethod+CustomStringConvertible.swift b/Sources/swcomp/Extensions/CompressionMethod+CustomStringConvertible.swift index d0c01536..67a49b00 100644 --- a/Sources/swcomp/Extensions/CompressionMethod+CustomStringConvertible.swift +++ b/Sources/swcomp/Extensions/CompressionMethod+CustomStringConvertible.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Extensions/ContainerEntryInfo+CustomStringConvertible.swift b/Sources/swcomp/Extensions/ContainerEntryInfo+CustomStringConvertible.swift index 345f27b0..891c5c3f 100644 --- a/Sources/swcomp/Extensions/ContainerEntryInfo+CustomStringConvertible.swift +++ b/Sources/swcomp/Extensions/ContainerEntryInfo+CustomStringConvertible.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Extensions/FileSystemType+CustomStringConvertible.swift b/Sources/swcomp/Extensions/FileSystemType+CustomStringConvertible.swift index 4a2cb0d0..98503229 100644 --- a/Sources/swcomp/Extensions/FileSystemType+CustomStringConvertible.swift +++ b/Sources/swcomp/Extensions/FileSystemType+CustomStringConvertible.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Extensions/GzipHeader+CustomStringConvertible.swift b/Sources/swcomp/Extensions/GzipHeader+CustomStringConvertible.swift index a4631594..25011661 100644 --- a/Sources/swcomp/Extensions/GzipHeader+CustomStringConvertible.swift +++ b/Sources/swcomp/Extensions/GzipHeader+CustomStringConvertible.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Extensions/TarEntry+Create.swift b/Sources/swcomp/Extensions/TarEntry+Create.swift index 664725d7..5d0effa2 100644 --- a/Sources/swcomp/Extensions/TarEntry+Create.swift +++ b/Sources/swcomp/Extensions/TarEntry+Create.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/Extensions/TarFormat+ConvertibleFromString.swift b/Sources/swcomp/Extensions/TarFormat+ConvertibleFromString.swift index 436361f2..7d0231a1 100644 --- a/Sources/swcomp/Extensions/TarFormat+ConvertibleFromString.swift +++ b/Sources/swcomp/Extensions/TarFormat+ConvertibleFromString.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/SwcompError.swift b/Sources/swcomp/SwcompError.swift index 5a88f023..50196d20 100644 --- a/Sources/swcomp/SwcompError.swift +++ b/Sources/swcomp/SwcompError.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Sources/swcomp/main.swift b/Sources/swcomp/main.swift index 4adf98ae..11a11150 100644 --- a/Sources/swcomp/main.swift +++ b/Sources/swcomp/main.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Tests/BZip2CompressionTests.swift b/Tests/BZip2CompressionTests.swift index 4b127a01..41a8fe4d 100644 --- a/Tests/BZip2CompressionTests.swift +++ b/Tests/BZip2CompressionTests.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Tests/BZip2Tests.swift b/Tests/BZip2Tests.swift index c03fdd2c..68bed62e 100644 --- a/Tests/BZip2Tests.swift +++ b/Tests/BZip2Tests.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Tests/Constants.swift b/Tests/Constants.swift index fcd42794..e05702bb 100644 --- a/Tests/Constants.swift +++ b/Tests/Constants.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Tests/DeflateCompressionTests.swift b/Tests/DeflateCompressionTests.swift index de9e1016..1b143d33 100644 --- a/Tests/DeflateCompressionTests.swift +++ b/Tests/DeflateCompressionTests.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Tests/GzipTests.swift b/Tests/GzipTests.swift index 5dd5e40e..daa49ee7 100644 --- a/Tests/GzipTests.swift +++ b/Tests/GzipTests.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Tests/LZ4CompressionTests.swift b/Tests/LZ4CompressionTests.swift index 9e381bfe..544b6c06 100644 --- a/Tests/LZ4CompressionTests.swift +++ b/Tests/LZ4CompressionTests.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Tests/LZ4Tests.swift b/Tests/LZ4Tests.swift index 16ab52f2..4a591f14 100644 --- a/Tests/LZ4Tests.swift +++ b/Tests/LZ4Tests.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Tests/LzmaTests.swift b/Tests/LzmaTests.swift index c3cbaab9..2f17a8b4 100644 --- a/Tests/LzmaTests.swift +++ b/Tests/LzmaTests.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Tests/SevenZipTests.swift b/Tests/SevenZipTests.swift index d9aed7c6..41fa5e89 100644 --- a/Tests/SevenZipTests.swift +++ b/Tests/SevenZipTests.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Tests/Sha256Tests.swift b/Tests/Sha256Tests.swift index cb78202b..83468bc8 100644 --- a/Tests/Sha256Tests.swift +++ b/Tests/Sha256Tests.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Tests/TarCreateTests.swift b/Tests/TarCreateTests.swift index ae0c4cd0..76e55514 100644 --- a/Tests/TarCreateTests.swift +++ b/Tests/TarCreateTests.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Tests/TarReaderTests.swift b/Tests/TarReaderTests.swift index be422acd..4ad4a866 100644 --- a/Tests/TarReaderTests.swift +++ b/Tests/TarReaderTests.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Tests/TarTests.swift b/Tests/TarTests.swift index b31aedd5..015397f1 100644 --- a/Tests/TarTests.swift +++ b/Tests/TarTests.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Tests/TarWriterTests.swift b/Tests/TarWriterTests.swift index a31f0626..261ff50d 100644 --- a/Tests/TarWriterTests.swift +++ b/Tests/TarWriterTests.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Tests/TestZipExtraField.swift b/Tests/TestZipExtraField.swift index 45676511..084a1501 100644 --- a/Tests/TestZipExtraField.swift +++ b/Tests/TestZipExtraField.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Tests/XxHash32Tests.swift b/Tests/XxHash32Tests.swift index d773af4e..87e15276 100644 --- a/Tests/XxHash32Tests.swift +++ b/Tests/XxHash32Tests.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Tests/XzTests.swift b/Tests/XzTests.swift index d243bdf9..708bf111 100644 --- a/Tests/XzTests.swift +++ b/Tests/XzTests.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Tests/ZipTests.swift b/Tests/ZipTests.swift index 5aea5218..d2bfa497 100644 --- a/Tests/ZipTests.swift +++ b/Tests/ZipTests.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information diff --git a/Tests/ZlibTests.swift b/Tests/ZlibTests.swift index d6aa7c55..2f9d81d1 100644 --- a/Tests/ZlibTests.swift +++ b/Tests/ZlibTests.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Timofey Solomko +// Copyright (c) 2026 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information From ec180c3d063480a879cf148a59777887ea842dba Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sat, 31 Jan 2026 20:01:03 +0800 Subject: [PATCH 17/70] Add a note to README about CocoaPods deprecation --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 93207103..ce28cf45 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,10 @@ Also, SWCompression is _written with Swift only._ SWCompression can be integrated into your project using Swift Package Manager, CocoaPods, or Carthage. +__Note:__ Due to [upcoming](https://blog.cocoapods.org/CocoaPods-Specs-Repo/) deprecation of CocoaPods no new releases +of SWCompression will be published using CocoaPods. The last release of SWCompression made available via CocoaPods was +4.8.7. Consider switching to Swift Package Manager instead. + ### Swift Package Manager To install using SPM, add SWCompression to you package dependencies and specify it as a dependency for your target, e.g.: From 0a4edd58966b743d1f37dbcfc680962090d12288 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sat, 31 Jan 2026 20:04:47 +0800 Subject: [PATCH 18/70] Update xcode project --- SWCompression.xcodeproj/project.pbxproj | 4 +++- .../xcshareddata/xcschemes/SWCompression.xcscheme | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/SWCompression.xcodeproj/project.pbxproj b/SWCompression.xcodeproj/project.pbxproj index 03dc30da..05c3c142 100644 --- a/SWCompression.xcodeproj/project.pbxproj +++ b/SWCompression.xcodeproj/project.pbxproj @@ -1162,7 +1162,7 @@ attributes = { BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1620; + LastUpgradeCheck = 2620; ORGANIZATIONNAME = "Timofey Solomko"; TargetAttributes = { 06BE1AC71DB410F100EE0F59 = { @@ -1580,6 +1580,7 @@ BitByteData, ); SDKROOT = macosx; + STRING_CATALOG_GENERATE_SYMBOLS = YES; SUPPORTED_PLATFORMS = "macosx watchsimulator iphonesimulator appletvsimulator watchos appletvos iphoneos xrsimulator xros"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -1647,6 +1648,7 @@ BitByteData, ); SDKROOT = macosx; + STRING_CATALOG_GENERATE_SYMBOLS = YES; SUPPORTED_PLATFORMS = "macosx watchsimulator iphonesimulator appletvsimulator watchos appletvos iphoneos xrsimulator xros"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_VERSION = 5.0; diff --git a/SWCompression.xcodeproj/xcshareddata/xcschemes/SWCompression.xcscheme b/SWCompression.xcodeproj/xcshareddata/xcschemes/SWCompression.xcscheme index f186312b..1b98aff8 100644 --- a/SWCompression.xcodeproj/xcshareddata/xcschemes/SWCompression.xcscheme +++ b/SWCompression.xcodeproj/xcshareddata/xcschemes/SWCompression.xcscheme @@ -1,6 +1,6 @@ Date: Sat, 31 Jan 2026 20:06:42 +0800 Subject: [PATCH 19/70] Drop support for Swift 5.7 and 5.8 --- Package.swift | 5 ++--- README.md | 2 +- SWCompression.xcodeproj/project.pbxproj | 4 ++-- azure-pipelines.yml | 28 ------------------------- 4 files changed, 5 insertions(+), 34 deletions(-) diff --git a/Package.swift b/Package.swift index 6fba78b0..0bf40d03 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.7 +// swift-tools-version:5.9 import PackageDescription var package = Package( @@ -8,8 +8,7 @@ var package = Package( .iOS(.v14), .tvOS(.v14), .watchOS(.v7), - // TODO: Enable after upgrading to Swift 5.9. - // .visionOS(.v1) + .visionOS(.v1) ], products: [ .library( diff --git a/README.md b/README.md index ce28cf45..10fc6be2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # SWCompression -[![Swift 5.7+](https://img.shields.io/badge/Swift-5.7+-blue.svg)](https://developer.apple.com/swift/) +[![Swift 5.9+](https://img.shields.io/badge/Swift-5.9+-blue.svg)](https://developer.apple.com/swift/) [![GitHub license](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://raw.githubusercontent.com/tsolomko/SWCompression/master/LICENSE) [![Build Status](https://dev.azure.com/tsolomko/SWCompression/_apis/build/status/tsolomko.SWCompression?branchName=develop)](https://dev.azure.com/tsolomko/SWCompression/_build/latest?definitionId=3&branchName=develop) diff --git a/SWCompression.xcodeproj/project.pbxproj b/SWCompression.xcodeproj/project.pbxproj index 05c3c142..e881802f 100644 --- a/SWCompression.xcodeproj/project.pbxproj +++ b/SWCompression.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 56; + objectVersion = 60; objects = { /* Begin PBXBuildFile section */ @@ -1176,7 +1176,7 @@ }; }; buildConfigurationList = 06BE1AC21DB410F100EE0F59 /* Build configuration list for PBXProject "SWCompression" */; - compatibilityVersion = "Xcode 14.0"; + compatibilityVersion = "Xcode 15.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a075d1f1..cb2369b0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -15,16 +15,6 @@ stages: - job: macos strategy: matrix: - macosSwift57: - imageName: 'macOS-13' - DEVELOPER_DIR: '/Applications/Xcode_14.2.app' - IOS_SIMULATOR: 'iPhone 14' - WATCHOS_SIMULATOR: 'Apple Watch Series 6 (44mm)' - macosSwift58: - imageName: 'macOS-13' - DEVELOPER_DIR: '/Applications/Xcode_14.3.1.app' - IOS_SIMULATOR: 'iPhone 14' - WATCHOS_SIMULATOR: 'Apple Watch Series 6 (44mm)' macosSwift59: imageName: 'macOS-14' DEVELOPER_DIR: '/Applications/Xcode_15.2.app' @@ -75,12 +65,6 @@ stages: - job: linux strategy: matrix: - linuxSwift57: - imageName: 'ubuntu-22.04' - containerImage: 'swift:5.7.3-jammy' - linuxSwift58: - imageName: 'ubuntu-22.04' - containerImage: 'swift:5.8.1-jammy' linuxSwift59: imageName: 'ubuntu-22.04' containerImage: 'swift:5.9.2-jammy' @@ -103,18 +87,6 @@ stages: - job: windows strategy: matrix: - windowsSwift57: - imageName: 'windows-2019' - SWIFT_VERSION: '5.7.3' - ICU_PATH: 'C:\Program Files\swift\icu-69.1\usr\bin' - SWIFT_DEV_PATH: 'C:\Program Files\swift\runtime-development\usr\bin' - XCTEST_LIB_PATH: '\x86_64' - windowsSwift58: - imageName: 'windows-2019' - SWIFT_VERSION: '5.8.1' - ICU_PATH: 'C:\Program Files\swift\icu-69.1\usr\bin' - SWIFT_DEV_PATH: 'C:\Program Files\swift\runtime-development\usr\bin' - XCTEST_LIB_PATH: '\x86_64' windowsSwift59: imageName: 'windows-2019' SWIFT_VERSION: '5.9.2' From 33e5716c3bf660fe22fb5afe66df782937c2269a Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sat, 31 Jan 2026 20:09:19 +0800 Subject: [PATCH 20/70] Increase minimum deployment targets on Apple platforms --- Package.swift | 8 ++++---- SWCompression.podspec | 10 +++++----- SWCompression.xcodeproj/project.pbxproj | 20 ++++++++++---------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Package.swift b/Package.swift index 0bf40d03..27e2c996 100644 --- a/Package.swift +++ b/Package.swift @@ -4,10 +4,10 @@ import PackageDescription var package = Package( name: "SWCompression", platforms: [ - .macOS(.v11), - .iOS(.v14), - .tvOS(.v14), - .watchOS(.v7), + .macOS(.v14), + .iOS(.v17), + .tvOS(.v17), + .watchOS(.v10), .visionOS(.v1) ], products: [ diff --git a/SWCompression.podspec b/SWCompression.podspec index 2094cd8f..e6216032 100644 --- a/SWCompression.podspec +++ b/SWCompression.podspec @@ -15,11 +15,11 @@ Pod::Spec.new do |s| s.source = { :git => "https://github.com/tsolomko/SWCompression.git", :tag => "#{s.version}" } - s.ios.deployment_target = "14.0" - s.osx.deployment_target = "11.0" - s.tvos.deployment_target = "14.0" - s.watchos.deployment_target = "7.0" - # s.visionos.deployment_target = "1.0" + s.ios.deployment_target = "17.0" + s.osx.deployment_target = "14.0" + s.tvos.deployment_target = "17.0" + s.watchos.deployment_target = "10.0" + s.visionos.deployment_target = "1.0" s.swift_versions = ["5"] diff --git a/SWCompression.xcodeproj/project.pbxproj b/SWCompression.xcodeproj/project.pbxproj index e881802f..50117dfe 100644 --- a/SWCompression.xcodeproj/project.pbxproj +++ b/SWCompression.xcodeproj/project.pbxproj @@ -1571,8 +1571,8 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; - MACOSX_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; + MACOSX_DEPLOYMENT_TARGET = 14.6; ONLY_ACTIVE_ARCH = YES; OTHER_CODE_SIGN_FLAGS = "--deep"; OTHER_LDFLAGS = ( @@ -1584,10 +1584,10 @@ SUPPORTED_PLATFORMS = "macosx watchsimulator iphonesimulator appletvsimulator watchos appletvos iphoneos xrsimulator xros"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TVOS_DEPLOYMENT_TARGET = 14.0; + TVOS_DEPLOYMENT_TARGET = 17.6; VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 7.0; - XROS_DEPLOYMENT_TARGET = 1.0; + WATCHOS_DEPLOYMENT_TARGET = 10.6; + XROS_DEPLOYMENT_TARGET = 1.3; }; name = Debug; }; @@ -1640,8 +1640,8 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; - MACOSX_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; + MACOSX_DEPLOYMENT_TARGET = 14.6; OTHER_CODE_SIGN_FLAGS = "--deep"; OTHER_LDFLAGS = ( "-framework", @@ -1652,10 +1652,10 @@ SUPPORTED_PLATFORMS = "macosx watchsimulator iphonesimulator appletvsimulator watchos appletvos iphoneos xrsimulator xros"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_VERSION = 5.0; - TVOS_DEPLOYMENT_TARGET = 14.0; + TVOS_DEPLOYMENT_TARGET = 17.6; VERSIONING_SYSTEM = "apple-generic"; - WATCHOS_DEPLOYMENT_TARGET = 7.0; - XROS_DEPLOYMENT_TARGET = 1.0; + WATCHOS_DEPLOYMENT_TARGET = 10.6; + XROS_DEPLOYMENT_TARGET = 1.3; }; name = Release; }; From 9c22e4353e2370e5686e28ea7d6771c905752a08 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sat, 31 Jan 2026 20:10:36 +0800 Subject: [PATCH 21/70] [CI] Add builds on Swift 6.1 and 6.2 --- azure-pipelines.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index cb2369b0..42ce83ed 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -30,6 +30,16 @@ stages: DEVELOPER_DIR: '/Applications/Xcode_16.2.app' IOS_SIMULATOR: 'iPhone 16' WATCHOS_SIMULATOR: 'Apple Watch Series 10 (42mm)' + macosSwift61: + imageName: 'macOS-15' + DEVELOPER_DIR: '/Applications/Xcode_16.4.app' + IOS_SIMULATOR: 'iPhone 16' + WATCHOS_SIMULATOR: 'Apple Watch Series 10 (42mm)' + macosSwift62: + imageName: 'macOS-15' + DEVELOPER_DIR: '/Applications/Xcode_26.2.app' + IOS_SIMULATOR: 'iPhone 16' + WATCHOS_SIMULATOR: 'Apple Watch Series 10 (42mm)' pool: vmImage: $(imageName) variables: @@ -74,6 +84,12 @@ stages: linuxSwift60: imageName: 'ubuntu-24.04' containerImage: 'swift:6.0.3-noble' + linuxSwift61: + imageName: 'ubuntu-24.04' + containerImage: 'swift:6.1.3-noble' + linuxSwift62: + imageName: 'ubuntu-24.04' + containerImage: 'swift:6.2.3-noble' pool: vmImage: $(imageName) container: $[ variables['containerImage'] ] @@ -123,6 +139,12 @@ stages: windowsSwift60: imageName: 'windows-2022' containerImage: 'swift:6.0.3-windowsservercore-ltsc2022' + windowsSwift61: + imageName: 'windows-2022' + containerImage: 'swift:6.1.3-windowsservercore-ltsc2022' + windowsSwift62: + imageName: 'windows-2022' + containerImage: 'swift:6.2.3-windowsservercore-ltsc2022' pool: vmImage: $(imageName) container: $[ variables['containerImage'] ] From f498c08da991e2f36aa92ea23323a85e1b37a0ad Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sat, 31 Jan 2026 20:11:54 +0800 Subject: [PATCH 22/70] [CI] Change Swift 5.9 build on Windows to use Docker image --- azure-pipelines.yml | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 42ce83ed..efac028e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -104,35 +104,8 @@ stages: strategy: matrix: windowsSwift59: - imageName: 'windows-2019' - SWIFT_VERSION: '5.9.2' - ICU_PATH: 'C:\Program Files\swift\icu-69.1\usr\bin' - SWIFT_DEV_PATH: 'C:\Program Files\swift\runtime-development\usr\bin' - XCTEST_LIB_PATH: '\x86_64' - pool: - vmImage: $(imageName) - variables: - DEVELOPER_DIR: 'C:\Library\Developer' - SDKROOT: 'C:\Library\Developer\Platforms\Windows.platform\Developer\SDKs\Windows.sdk' - SWIFTFLAGS: '--sdk $(SDKROOT) -Xswiftc -sdk -Xswiftc $(SDKROOT) -Xswiftc -resource-dir -Xswiftc $(SDKROOT)/usr/lib/swift -Xswiftc -I -Xswiftc $(SDKROOT)/usr/lib/swift -Xswiftc -L -Xswiftc $(SDKROOT)/usr/lib/swift/windows' - steps: - - pwsh: Invoke-WebRequest -Uri https://swift.org/builds/swift-$(SWIFT_VERSION)-release/windows10/swift-$(SWIFT_VERSION)-RELEASE/swift-$(SWIFT_VERSION)-RELEASE-windows10.exe -OutFile swift-install.exe - displayName: 'Download Swift' - - task: BatchScript@1 - inputs: - filename: .\swift-install.exe - arguments: /install /quiet - modifyEnvironment: true - displayName: 'Install Swift' - - script: | - set PATH=C:\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr\bin;%SWIFT_DEV_PATH%;%ICU_PATH%;%LocalAppData%\Programs\Swift;%PATH% - swift.exe --version - swift.exe build %SWIFTFLAGS% - swift.exe build -c release %SWIFTFLAGS% - displayName: 'Build SPM Debug & Release' - - job: windowsDocker - strategy: - matrix: + imageName: 'windows-2022' + containerImage: 'swift:5.9.2-windowsservercore-ltsc2022' windowsSwift510: imageName: 'windows-2022' containerImage: 'swift:5.10.1-windowsservercore-ltsc2022' From 38ac3e63758f99b0baf5887dc352e70a48d1f77c Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sat, 31 Jan 2026 20:12:17 +0800 Subject: [PATCH 23/70] [CI] Fix macOS version for Swift 5.10 build --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index efac028e..81d59d72 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -21,7 +21,7 @@ stages: IOS_SIMULATOR: 'iPhone 15' WATCHOS_SIMULATOR: 'Apple Watch Series 6 (44mm)' macosSwift510: - imageName: 'macOS-15' + imageName: 'macOS-14' DEVELOPER_DIR: '/Applications/Xcode_15.4.app' IOS_SIMULATOR: 'iPhone 15' WATCHOS_SIMULATOR: 'Apple Watch Series 6 (44mm)' From 44476e46b40c128bb42e1681455ebffaf28579c6 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sat, 31 Jan 2026 20:20:30 +0800 Subject: [PATCH 24/70] [CI] Disable Swift 5.10 build on Windows Swift 5.10 Windows Docker container causes ridiculous errors like "unknown or missing subcommand 'swift-build.exe'". --- azure-pipelines.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 81d59d72..837a90bc 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -106,9 +106,9 @@ stages: windowsSwift59: imageName: 'windows-2022' containerImage: 'swift:5.9.2-windowsservercore-ltsc2022' - windowsSwift510: - imageName: 'windows-2022' - containerImage: 'swift:5.10.1-windowsservercore-ltsc2022' + # windowsSwift510: + # imageName: 'windows-2022' + # containerImage: 'swift:5.10.1-windowsservercore-ltsc2022' windowsSwift60: imageName: 'windows-2022' containerImage: 'swift:6.0.3-windowsservercore-ltsc2022' From 713ae2c630eddbe2d562f72436f8df4c99b69012 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sat, 31 Jan 2026 20:34:57 +0800 Subject: [PATCH 25/70] Remove mention of generate-xcodeproj command in README This command was deprecated by SPM a long time ago. --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 10fc6be2..dc28b024 100644 --- a/README.md +++ b/README.md @@ -175,9 +175,6 @@ In the case of a bug, it will be especially helpful if you attach a file (archiv If you'd like to contribute, please [create a pull request](https://github.com/tsolomko/SWCompression/pulls) on GitHub. -__Note:__ If you are considering working on SWCompression, please note that Xcode project (SWCompression.xcodeproj) -was created manually and you shouldn't use `swift package generate-xcodeproj` command. - ### Executing tests locally If you want to run tests on your computer, you need to do a couple of additional steps after cloning the repository: From 718bdfa493e01b5078558fe32a6e9d4138bd2254 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sat, 31 Jan 2026 20:35:40 +0800 Subject: [PATCH 26/70] utils.py: add an option to build BBD only for macOS This option is mutually exclusive with xros. --- utils.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/utils.py b/utils.py index 70b0fb0f..83264d04 100755 --- a/utils.py +++ b/utils.py @@ -57,7 +57,9 @@ def action_dbm(args): script = ["carthage", "bootstrap", "--no-use-binaries", "--use-xcframeworks"] if args.debug: script += ["--configuration", "Debug"] - if args.xros: + if args.macos_only: + script += ["--platform", "macOS"] + elif args.xros: script += ["--platform", "macOS,iOS,watchOS,tvOS,visionOS"] else: script += ["--platform", "macOS,iOS,watchOS,tvOS"] @@ -119,8 +121,11 @@ def action_pr(args): description="downloads BitByteData dependency using Carthage (macOS only)") parser_dbm.add_argument("--debug", "-d", action="store_true", dest="debug", help="build BitByteData in Debug configuration") -parser_dbm.add_argument("--xros", action="store_true", dest="xros", - help="build BitByteData for visionOS as well (requires Apple Silicon)") +platform_group = parser_dbm.add_mutually_exclusive_group() +platform_group.add_argument("--macos-only", action="store_true", dest="macos_only", + help="build BitByteData for macOS only") +platform_group.add_argument("--xros", action="store_true", dest="xros", + help="build BitByteData for visionOS as well (requires Apple Silicon)") parser_dbm.set_defaults(func=action_dbm) # Parser for 'prepare-release' command. From cd116a92ddd53a8239c12ec2a7b103e946b0b5ef Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sat, 31 Jan 2026 21:48:04 +0800 Subject: [PATCH 27/70] [CI] Add a step for installing Darwin platforms This needs to be done before downloading BBD. AZP now has an extremely limited set of preinstalled simulators. --- azure-pipelines.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 837a90bc..c85ac8f3 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -66,6 +66,13 @@ stages: git lfs pull git lfs checkout displayName: 'Download or update test files' + - script: | + set -e -o xtrace + xcrun simctl list > /dev/null + xcodebuild -downloadPlatform iOS + xcodebuild -downloadPlatform watchOS + xcodebuild -downloadPlatform tvOS + displayName: 'Install Darwin platforms' - script: ./utils.py download-bbd-macos displayName: 'Download BitByteData' - script: ./utils.py ci script-macos From eb83e098af5ec040b3d037fa10fb1e966e9701eb Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Mon, 2 Feb 2026 17:17:45 +0800 Subject: [PATCH 28/70] Fix framework search paths in Xcode project This should fix installation with Carthage on the currently supported versions of Xcode. --- SWCompression.xcodeproj/project.pbxproj | 36 +++++++++++-------------- azure-pipelines.yml | 1 + 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/SWCompression.xcodeproj/project.pbxproj b/SWCompression.xcodeproj/project.pbxproj index 50117dfe..12d16981 100644 --- a/SWCompression.xcodeproj/project.pbxproj +++ b/SWCompression.xcodeproj/project.pbxproj @@ -1369,7 +1369,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/bash; - shellScript = "if [[ ${SDK_NAME} == iphone* ]]; then\n if [[ ! -d ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/ ]]; then\n mkdir -p ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n fi\n if [[ ${SDK_NAME} == iphonesimulator* ]]; then\n cp -a ${PROJECT_DIR}/Carthage/Build/BitByteData.xcframework/ios-*-simulator/BitByteData.framework ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n else\n cp -a ${PROJECT_DIR}/Carthage/Build/BitByteData.xcframework/ios-arm64*/BitByteData.framework ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n fi\nelif [[ ${SDK_NAME} == appletv* ]]; then\n if [[ ! -d ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/ ]]; then\n mkdir -p ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n fi\n if [[ ${SDK_NAME} == appletvsimulator* ]]; then\n cp -a ${PROJECT_DIR}/Carthage/Build/BitByteData.xcframework/tvos-*-simulator/BitByteData.framework ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n else\n cp -a ${PROJECT_DIR}/Carthage/Build/BitByteData.xcframework/tvos-arm64/BitByteData.framework ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n fi\nelif [[ ${SDK_NAME} == watch* ]]; then\n if [[ ! -d ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/ ]]; then\n mkdir -p ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n fi\n if [[ ${SDK_NAME} == watchsimulator* ]]; then\n cp -a ${PROJECT_DIR}/Carthage/Build/BitByteData.xcframework/watchos-*-simulator/BitByteData.framework ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n else\n cp -a ${PROJECT_DIR}/Carthage/Build/BitByteData.xcframework/watchos-arm64_32_armv7k/BitByteData.framework ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n fi\nelif [[ ${SDK_NAME} == macos* ]]; then\n if [[ ! -d ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Contents/Frameworks/ ]]; then\n mkdir -p ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Contents/Frameworks/\n fi\n cp -a ${PROJECT_DIR}/Carthage/Build/BitByteData.xcframework/macos-*/BitByteData.framework ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Contents/Frameworks/\nfi\n\nxattr -rc ${BUILT_PRODUCTS_DIR}\n"; + shellScript = "if [[ ${SDK_NAME} == iphone* ]]; then\n if [[ ! -d ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/ ]]; then\n mkdir -p ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n fi\n if [[ ${SDK_NAME} == iphonesimulator* ]]; then\n cp -a ${PROJECT_DIR}/Carthage/Build/BitByteData.xcframework/ios-*-simulator/BitByteData.framework ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n else\n cp -a ${PROJECT_DIR}/Carthage/Build/BitByteData.xcframework/ios-arm64*/BitByteData.framework ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n fi\nelif [[ ${SDK_NAME} == appletv* ]]; then\n if [[ ! -d ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/ ]]; then\n mkdir -p ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n fi\n if [[ ${SDK_NAME} == appletvsimulator* ]]; then\n cp -a ${PROJECT_DIR}/Carthage/Build/BitByteData.xcframework/tvos-*-simulator/BitByteData.framework ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n else\n cp -a ${PROJECT_DIR}/Carthage/Build/BitByteData.xcframework/tvos-arm64/BitByteData.framework ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n fi\nelif [[ ${SDK_NAME} == watch* ]]; then\n if [[ ! -d ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/ ]]; then\n mkdir -p ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n fi\n if [[ ${SDK_NAME} == watchsimulator* ]]; then\n cp -a ${PROJECT_DIR}/Carthage/Build/BitByteData.xcframework/watchos-*-simulator/BitByteData.framework ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n else\n cp -a ${PROJECT_DIR}/Carthage/Build/BitByteData.xcframework/watchos-*_armv7k/BitByteData.framework ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n fi\nelif [[ ${SDK_NAME} == macos* ]]; then\n if [[ ! -d ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Contents/Frameworks/ ]]; then\n mkdir -p ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Contents/Frameworks/\n fi\n cp -a ${PROJECT_DIR}/Carthage/Build/BitByteData.xcframework/macos-*/BitByteData.framework ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Contents/Frameworks/\nfi\n\nxattr -rc ${BUILT_PRODUCTS_DIR}\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -1552,17 +1552,14 @@ ENABLE_TESTABILITY = YES; "FRAMEWORK_SEARCH_PATHS[sdk=appletvos*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/tvos-arm64\""; "FRAMEWORK_SEARCH_PATHS[sdk=appletvsimulator*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/tvos-arm64_x86_64-simulator\""; - "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]" = ( - "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/ios-arm64_armv7\"", - "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/ios-arm64\"", - ); - "FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]" = ( - "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/ios-arm64_i386_x86_64-simulator\"", - "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/ios-arm64_x86_64-simulator\"", - ); + "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/ios-arm64\""; + "FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/ios-arm64_x86_64-simulator\""; "FRAMEWORK_SEARCH_PATHS[sdk=macosx*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/macos-arm64_x86_64\""; - "FRAMEWORK_SEARCH_PATHS[sdk=watchos*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/watchos-arm64_32_armv7k\""; - "FRAMEWORK_SEARCH_PATHS[sdk=watchsimulator*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/watchos-arm64_i386_x86_64-simulator\""; + "FRAMEWORK_SEARCH_PATHS[sdk=watchos*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/watchos-arm64_arm64_32_armv7k\""; + "FRAMEWORK_SEARCH_PATHS[sdk=watchsimulator*]" = ( + "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/watchos-arm64_i386_x86_64-simulator\"", + "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/watchos-arm64_x86_64-simulator\"", + ); GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -1622,17 +1619,14 @@ ENABLE_STRICT_OBJC_MSGSEND = YES; "FRAMEWORK_SEARCH_PATHS[sdk=appletvos*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/tvos-arm64\""; "FRAMEWORK_SEARCH_PATHS[sdk=appletvsimulator*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/tvos-arm64_x86_64-simulator\""; - "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]" = ( - "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/ios-arm64_armv7\"", - "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/ios-arm64\"", - ); - "FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]" = ( - "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/ios-arm64_i386_x86_64-simulator\"", - "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/ios-arm64_x86_64-simulator\"", - ); + "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/ios-arm64\""; + "FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/ios-arm64_x86_64-simulator\""; "FRAMEWORK_SEARCH_PATHS[sdk=macosx*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/macos-arm64_x86_64\""; - "FRAMEWORK_SEARCH_PATHS[sdk=watchos*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/watchos-arm64_32_armv7k\""; - "FRAMEWORK_SEARCH_PATHS[sdk=watchsimulator*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/watchos-arm64_i386_x86_64-simulator\""; + "FRAMEWORK_SEARCH_PATHS[sdk=watchos*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/watchos-arm64_arm64_32_armv7k \""; + "FRAMEWORK_SEARCH_PATHS[sdk=watchsimulator*]" = ( + "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/watchos-arm64_i386_x86_64-simulator\"", + "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/watchos-arm64_x86_64-simulator\"", + ); GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c85ac8f3..e0556730 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -30,6 +30,7 @@ stages: DEVELOPER_DIR: '/Applications/Xcode_16.2.app' IOS_SIMULATOR: 'iPhone 16' WATCHOS_SIMULATOR: 'Apple Watch Series 10 (42mm)' + # TODO: When Swift 6.0 support is dropped we can remove i386 search path for the watchOS simulator in Xcode. macosSwift61: imageName: 'macOS-15' DEVELOPER_DIR: '/Applications/Xcode_16.4.app' From f1b635056d8efe45fecee6d2fa5df3dc5f94a10a Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Tue, 3 Feb 2026 01:07:34 +0800 Subject: [PATCH 29/70] Add BitByteData as a package dependency in Xcode project --- SWCompression.xcodeproj/project.pbxproj | 42 +++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/SWCompression.xcodeproj/project.pbxproj b/SWCompression.xcodeproj/project.pbxproj index 12d16981..a326164d 100644 --- a/SWCompression.xcodeproj/project.pbxproj +++ b/SWCompression.xcodeproj/project.pbxproj @@ -275,6 +275,7 @@ E6C057D627498F5A007C83DF /* TarWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6C057D527498F5A007C83DF /* TarWriter.swift */; }; E6C4150726FE230A00F9D36F /* XxHash32.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6C4150626FE230A00F9D36F /* XxHash32.swift */; }; E6D86D2F26FE35C50032CFFA /* XxHash32Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6D86D2E26FE35C50032CFFA /* XxHash32Tests.swift */; }; + E6DCC9D82F30DA080042AE2E /* BitByteData in Frameworks */ = {isa = PBXBuildFile; productRef = E6DCC9D72F30DA080042AE2E /* BitByteData */; }; E6DF9ECE2704BF14003BD91E /* test_multi_frame.lz4 in Resources */ = {isa = PBXBuildFile; fileRef = E6DF9ECD2704BF14003BD91E /* test_multi_frame.lz4 */; }; E6E49D522BD5081D00BFC756 /* PrivacyInfo.xcprivacy in CopyFiles */ = {isa = PBXBuildFile; fileRef = E6E49D502BD507D700BFC756 /* PrivacyInfo.xcprivacy */; }; E6EDD6A826F7767F00884532 /* test_minor_version_3.7z in Resources */ = {isa = PBXBuildFile; fileRef = E6EDD6A626F7767E00884532 /* test_minor_version_3.7z */; }; @@ -592,6 +593,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + E6DCC9D52F30DA020042AE2E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E6DCC9D82F30DA080042AE2E /* BitByteData in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -1125,10 +1134,12 @@ buildPhases = ( 06BE1AC31DB410F100EE0F59 /* Sources */, E6E49D512BD5081700BFC756 /* CopyFiles */, + E6DCC9D52F30DA020042AE2E /* Frameworks */, ); buildRules = ( ); dependencies = ( + E6DCC9DA2F30DB4E0042AE2E /* PBXTargetDependency */, ); name = SWCompression; productName = SWCompression; @@ -1184,6 +1195,9 @@ Base, ); mainGroup = 06BE1ABE1DB410F100EE0F59; + packageReferences = ( + E6DCC9D12F30D7610042AE2E /* XCRemoteSwiftPackageReference "BitByteData" */, + ); productRefGroup = 06BE1AC91DB410F100EE0F59 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -1517,6 +1531,10 @@ target = 06BE1AC71DB410F100EE0F59 /* SWCompression */; targetProxy = 06F0659B1FFB761600312A82 /* PBXContainerItemProxy */; }; + E6DCC9DA2F30DB4E0042AE2E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = E6DCC9D92F30DB4E0042AE2E /* BitByteData */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -1778,6 +1796,30 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + E6DCC9D12F30D7610042AE2E /* XCRemoteSwiftPackageReference "BitByteData" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/tsolomko/BitByteData"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.0.4; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + E6DCC9D72F30DA080042AE2E /* BitByteData */ = { + isa = XCSwiftPackageProductDependency; + package = E6DCC9D12F30D7610042AE2E /* XCRemoteSwiftPackageReference "BitByteData" */; + productName = BitByteData; + }; + E6DCC9D92F30DB4E0042AE2E /* BitByteData */ = { + isa = XCSwiftPackageProductDependency; + package = E6DCC9D12F30D7610042AE2E /* XCRemoteSwiftPackageReference "BitByteData" */; + productName = BitByteData; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 06BE1ABF1DB410F100EE0F59 /* Project object */; } From 29697ca7ceb4abfed9526224d344297da75498f3 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Tue, 3 Feb 2026 01:08:25 +0800 Subject: [PATCH 30/70] Remove from Xcode project settings for embedding BBD built with Carthage --- SWCompression.xcodeproj/project.pbxproj | 88 +------------------------ 1 file changed, 2 insertions(+), 86 deletions(-) diff --git a/SWCompression.xcodeproj/project.pbxproj b/SWCompression.xcodeproj/project.pbxproj index a326164d..cccc0158 100644 --- a/SWCompression.xcodeproj/project.pbxproj +++ b/SWCompression.xcodeproj/project.pbxproj @@ -1153,7 +1153,6 @@ 06F065911FFB761600312A82 /* Sources */, 06F065921FFB761600312A82 /* Frameworks */, 06F065931FFB761600312A82 /* Resources */, - 062BE32F1FFBADB300343CAD /* Copy BitByteData.framework into test bundle */, ); buildRules = ( ); @@ -1369,25 +1368,6 @@ }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - 062BE32F1FFBADB300343CAD /* Copy BitByteData.framework into test bundle */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Copy BitByteData.framework into test bundle"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/bash; - shellScript = "if [[ ${SDK_NAME} == iphone* ]]; then\n if [[ ! -d ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/ ]]; then\n mkdir -p ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n fi\n if [[ ${SDK_NAME} == iphonesimulator* ]]; then\n cp -a ${PROJECT_DIR}/Carthage/Build/BitByteData.xcframework/ios-*-simulator/BitByteData.framework ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n else\n cp -a ${PROJECT_DIR}/Carthage/Build/BitByteData.xcframework/ios-arm64*/BitByteData.framework ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n fi\nelif [[ ${SDK_NAME} == appletv* ]]; then\n if [[ ! -d ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/ ]]; then\n mkdir -p ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n fi\n if [[ ${SDK_NAME} == appletvsimulator* ]]; then\n cp -a ${PROJECT_DIR}/Carthage/Build/BitByteData.xcframework/tvos-*-simulator/BitByteData.framework ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n else\n cp -a ${PROJECT_DIR}/Carthage/Build/BitByteData.xcframework/tvos-arm64/BitByteData.framework ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n fi\nelif [[ ${SDK_NAME} == watch* ]]; then\n if [[ ! -d ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/ ]]; then\n mkdir -p ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n fi\n if [[ ${SDK_NAME} == watchsimulator* ]]; then\n cp -a ${PROJECT_DIR}/Carthage/Build/BitByteData.xcframework/watchos-*-simulator/BitByteData.framework ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n else\n cp -a ${PROJECT_DIR}/Carthage/Build/BitByteData.xcframework/watchos-*_armv7k/BitByteData.framework ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Frameworks/\n fi\nelif [[ ${SDK_NAME} == macos* ]]; then\n if [[ ! -d ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Contents/Frameworks/ ]]; then\n mkdir -p ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Contents/Frameworks/\n fi\n cp -a ${PROJECT_DIR}/Carthage/Build/BitByteData.xcframework/macos-*/BitByteData.framework ${BUILT_PRODUCTS_DIR}/TestSWCompression.xctest/Contents/Frameworks/\nfi\n\nxattr -rc ${BUILT_PRODUCTS_DIR}\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ 06BE1AC31DB410F100EE0F59 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -1568,16 +1548,7 @@ EAGER_LINKING = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; - "FRAMEWORK_SEARCH_PATHS[sdk=appletvos*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/tvos-arm64\""; - "FRAMEWORK_SEARCH_PATHS[sdk=appletvsimulator*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/tvos-arm64_x86_64-simulator\""; - "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/ios-arm64\""; - "FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/ios-arm64_x86_64-simulator\""; - "FRAMEWORK_SEARCH_PATHS[sdk=macosx*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/macos-arm64_x86_64\""; - "FRAMEWORK_SEARCH_PATHS[sdk=watchos*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/watchos-arm64_arm64_32_armv7k\""; - "FRAMEWORK_SEARCH_PATHS[sdk=watchsimulator*]" = ( - "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/watchos-arm64_i386_x86_64-simulator\"", - "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/watchos-arm64_x86_64-simulator\"", - ); + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -1589,11 +1560,6 @@ IPHONEOS_DEPLOYMENT_TARGET = 17.6; MACOSX_DEPLOYMENT_TARGET = 14.6; ONLY_ACTIVE_ARCH = YES; - OTHER_CODE_SIGN_FLAGS = "--deep"; - OTHER_LDFLAGS = ( - "-framework", - BitByteData, - ); SDKROOT = macosx; STRING_CATALOG_GENERATE_SYMBOLS = YES; SUPPORTED_PLATFORMS = "macosx watchsimulator iphonesimulator appletvsimulator watchos appletvos iphoneos xrsimulator xros"; @@ -1635,16 +1601,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; EAGER_LINKING = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; - "FRAMEWORK_SEARCH_PATHS[sdk=appletvos*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/tvos-arm64\""; - "FRAMEWORK_SEARCH_PATHS[sdk=appletvsimulator*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/tvos-arm64_x86_64-simulator\""; - "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/ios-arm64\""; - "FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/ios-arm64_x86_64-simulator\""; - "FRAMEWORK_SEARCH_PATHS[sdk=macosx*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/macos-arm64_x86_64\""; - "FRAMEWORK_SEARCH_PATHS[sdk=watchos*]" = "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/watchos-arm64_arm64_32_armv7k \""; - "FRAMEWORK_SEARCH_PATHS[sdk=watchsimulator*]" = ( - "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/watchos-arm64_i386_x86_64-simulator\"", - "\"$(SRCROOT)/Carthage/Build/BitByteData.xcframework/watchos-arm64_x86_64-simulator\"", - ); + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; @@ -1654,11 +1611,6 @@ GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.6; MACOSX_DEPLOYMENT_TARGET = 14.6; - OTHER_CODE_SIGN_FLAGS = "--deep"; - OTHER_LDFLAGS = ( - "-framework", - BitByteData, - ); SDKROOT = macosx; STRING_CATALOG_GENERATE_SYMBOLS = YES; SUPPORTED_PLATFORMS = "macosx watchsimulator iphonesimulator appletvsimulator watchos appletvos iphoneos xrsimulator xros"; @@ -1682,19 +1634,8 @@ ENABLE_MODULE_VERIFIER = YES; INFOPLIST_FILE = SWCompression.xcodeproj/SWCompression.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@loader_path/Frameworks", - "@executable_path/Frameworks", - "@loader_path/../Frameworks", - "@executable_path/../Frameworks", - ); MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++14"; - OTHER_LDFLAGS = ( - "-framework", - BitByteData, - ); PRODUCT_BUNDLE_IDENTIFIER = me.tsolomko.SWCompression; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1712,19 +1653,8 @@ ENABLE_MODULE_VERIFIER = YES; INFOPLIST_FILE = SWCompression.xcodeproj/SWCompression.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@loader_path/Frameworks", - "@executable_path/Frameworks", - "@loader_path/../Frameworks", - "@executable_path/../Frameworks", - ); MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++14"; - OTHER_LDFLAGS = ( - "-framework", - BitByteData, - ); PRODUCT_BUNDLE_IDENTIFIER = me.tsolomko.SWCompression; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1736,13 +1666,6 @@ buildSettings = { COPY_PHASE_STRIP = NO; INFOPLIST_FILE = SWCompression.xcodeproj/TestSWCompression.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@loader_path/Frameworks", - "@executable_path/Frameworks", - "@loader_path/../Frameworks", - "@executable_path/../Frameworks", - ); PRODUCT_BUNDLE_IDENTIFIER = me.tsolomko.TestSWCompression; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -1753,13 +1676,6 @@ buildSettings = { COPY_PHASE_STRIP = NO; INFOPLIST_FILE = SWCompression.xcodeproj/TestSWCompression.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@loader_path/Frameworks", - "@executable_path/Frameworks", - "@loader_path/../Frameworks", - "@executable_path/../Frameworks", - ); PRODUCT_BUNDLE_IDENTIFIER = me.tsolomko.TestSWCompression; PRODUCT_NAME = "$(TARGET_NAME)"; }; From 5b3840f6132629863bcd012f8e01018867580d4c Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Tue, 3 Feb 2026 01:09:16 +0800 Subject: [PATCH 31/70] utils.py: remove download-bbd-macos command --- azure-pipelines.yml | 2 -- utils.py | 25 ------------------------- 2 files changed, 27 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e0556730..2d42e3d0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -74,8 +74,6 @@ stages: xcodebuild -downloadPlatform watchOS xcodebuild -downloadPlatform tvOS displayName: 'Install Darwin platforms' - - script: ./utils.py download-bbd-macos - displayName: 'Download BitByteData' - script: ./utils.py ci script-macos displayName: 'Build & Test' - script: swift build -c release diff --git a/utils.py b/utils.py index 83264d04..4201234d 100755 --- a/utils.py +++ b/utils.py @@ -52,19 +52,6 @@ def action_cw(args): _sprun(["rm", "-f", "Package.resolved"]) _sprun(["rm", "-f", "SWCompression.framework.zip"]) -def action_dbm(args): - print("=> Downloading BitByteData dependency using Carthage") - script = ["carthage", "bootstrap", "--no-use-binaries", "--use-xcframeworks"] - if args.debug: - script += ["--configuration", "Debug"] - if args.macos_only: - script += ["--platform", "macOS"] - elif args.xros: - script += ["--platform", "macOS,iOS,watchOS,tvOS,visionOS"] - else: - script += ["--platform", "macOS,iOS,watchOS,tvOS"] - _sprun(script) - def action_pr(args): _sprun(["agvtool", "next-version", "-all"]) _sprun(["agvtool", "new-marketing-version", args.version]) @@ -116,18 +103,6 @@ def action_pr(args): description="cleans workspace from files produced by various build systems") parser_cw.set_defaults(func=action_cw) -# Parser for 'download-bbd-macos' command. -parser_dbm = subparsers.add_parser("download-bbd-macos", help="download BitByteData", - description="downloads BitByteData dependency using Carthage (macOS only)") -parser_dbm.add_argument("--debug", "-d", action="store_true", dest="debug", - help="build BitByteData in Debug configuration") -platform_group = parser_dbm.add_mutually_exclusive_group() -platform_group.add_argument("--macos-only", action="store_true", dest="macos_only", - help="build BitByteData for macOS only") -platform_group.add_argument("--xros", action="store_true", dest="xros", - help="build BitByteData for visionOS as well (requires Apple Silicon)") -parser_dbm.set_defaults(func=action_dbm) - # Parser for 'prepare-release' command. parser_pr = subparsers.add_parser("prepare-release", help="prepare next release", description="prepare next release of SWCompression") From 8a90134aa59ae89a4dbedb428e7c2df0faa96361 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Tue, 3 Feb 2026 01:09:59 +0800 Subject: [PATCH 32/70] Remove Cartfile --- Cartfile | 1 - utils.py | 2 -- 2 files changed, 3 deletions(-) delete mode 100644 Cartfile diff --git a/Cartfile b/Cartfile deleted file mode 100644 index 656bf838..00000000 --- a/Cartfile +++ /dev/null @@ -1 +0,0 @@ -github "tsolomko/BitByteData" ~> 2.0.0 diff --git a/utils.py b/utils.py index 4201234d..eb5b11c1 100755 --- a/utils.py +++ b/utils.py @@ -43,11 +43,9 @@ def action_ci(args): def action_cw(args): _sprun(["rm", "-rf", "build/"]) - _sprun(["rm", "-rf", "Carthage/"]) _sprun(["rm", "-rf", "docs/"]) _sprun(["rm", "-rf", "Pods/"]) _sprun(["rm", "-rf", ".build/"]) - _sprun(["rm", "-f", "Cartfile.resolved"]) _sprun(["rm", "-f", "docs.json"]) _sprun(["rm", "-f", "Package.resolved"]) _sprun(["rm", "-f", "SWCompression.framework.zip"]) From ad4a9574f79437deadb28c84bb9379d67053add1 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Tue, 3 Feb 2026 01:10:15 +0800 Subject: [PATCH 33/70] Remove podspec --- SWCompression.podspec | 86 ------------------------------------------- utils.py | 11 ------ 2 files changed, 97 deletions(-) delete mode 100644 SWCompression.podspec diff --git a/SWCompression.podspec b/SWCompression.podspec deleted file mode 100644 index e6216032..00000000 --- a/SWCompression.podspec +++ /dev/null @@ -1,86 +0,0 @@ -Pod::Spec.new do |s| - - s.name = "SWCompression" - s.version = "4.8.6" - s.summary = "A framework with functions for working with compression, archives and containers." - - s.description = "A framework with (de)compression algorithms and functions for processing various archives and containers." - - s.homepage = "https://github.com/tsolomko/SWCompression" - s.documentation_url = "http://tsolomko.github.io/SWCompression" - - s.license = { :type => "MIT", :file => "LICENSE" } - - s.author = { "Timofey Solomko" => "tsolomko@gmail.com" } - - s.source = { :git => "https://github.com/tsolomko/SWCompression.git", :tag => "#{s.version}" } - - s.ios.deployment_target = "17.0" - s.osx.deployment_target = "14.0" - s.tvos.deployment_target = "17.0" - s.watchos.deployment_target = "10.0" - s.visionos.deployment_target = "1.0" - - s.swift_versions = ["5"] - - s.dependency "BitByteData", "~> 2.0" - - s.resource_bundles = {"SWCompression" => ["Sources/PrivacyInfo.xcprivacy"]} - - s.subspec "Deflate" do |sp| - sp.source_files = "Sources/{Deflate/*,Common/*,Common/CodingTree/*}.swift" - sp.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS" => "-DSWCOMPRESSION_POD_DEFLATE" } - end - - s.subspec "GZip" do |sp| - sp.dependency "SWCompression/Deflate" - sp.source_files = "Sources/{GZip/*,Common/*}.swift" - end - - s.subspec "Zlib" do |sp| - sp.dependency "SWCompression/Deflate" - sp.source_files = "Sources/{Zlib/*,Common/*}.swift" - end - - s.subspec "BZip2" do |sp| - sp.source_files = "Sources/{BZip2/*,Common/*,Common/CodingTree/*}.swift" - sp.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS" => "-DSWCOMPRESSION_POD_BZ2" } - end - - s.subspec "LZMA" do |sp| - sp.source_files = "Sources/{LZMA/*,Common/*}.swift" - sp.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS" => "-DSWCOMPRESSION_POD_LZMA" } - end - - s.subspec "LZMA2" do |sp| - sp.dependency "SWCompression/LZMA" - sp.source_files = "Sources/{LZMA2/*,Common/*}.swift" - end - - s.subspec "LZ4" do |sp| - sp.source_files = "Sources/{LZ4/*,Common/*}.swift" - sp.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS" => "-DSWCOMPRESSION_POD_LZ4" } - end - - s.subspec "XZ" do |sp| - sp.dependency "SWCompression/LZMA2" - sp.source_files = "Sources/{XZ/*,Common/*}.swift" - end - - s.subspec "ZIP" do |sp| - sp.dependency "SWCompression/Deflate" - sp.source_files = "Sources/{Zip/*,Common/*,Common/Container/*}.swift" - sp.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS" => "-DSWCOMPRESSION_POD_ZIP" } - end - - s.subspec "TAR" do |sp| - sp.source_files = "Sources/{TAR/*,Common/*,Common/Container/*}.swift" - end - - s.subspec "SevenZip" do |sp| - sp.dependency "SWCompression/LZMA2" - sp.source_files = "Sources/{7-Zip/*,Common/*,Common/Container/*}.swift" - sp.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS" => "-DSWCOMPRESSION_POD_SEVENZIP" } - end - -end diff --git a/utils.py b/utils.py index eb5b11c1..392a8fb9 100755 --- a/utils.py +++ b/utils.py @@ -44,7 +44,6 @@ def action_ci(args): def action_cw(args): _sprun(["rm", "-rf", "build/"]) _sprun(["rm", "-rf", "docs/"]) - _sprun(["rm", "-rf", "Pods/"]) _sprun(["rm", "-rf", ".build/"]) _sprun(["rm", "-f", "docs.json"]) _sprun(["rm", "-f", "Package.resolved"]) @@ -54,16 +53,6 @@ def action_pr(args): _sprun(["agvtool", "next-version", "-all"]) _sprun(["agvtool", "new-marketing-version", args.version]) - f = open("SWCompression.podspec", "r", encoding="utf-8") - lines = f.readlines() - f.close() - f = open("SWCompression.podspec", "w", encoding="utf-8") - for line in lines: - if line.startswith(" s.version = "): - line = " s.version = \"" + args.version + "\"\n" - f.write(line) - f.close() - f = open(".jazzy.yaml", "r", encoding="utf-8") lines = f.readlines() f.close() From 1566b49ce3a5937ea3e16387257f56c2366f1e0a Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Tue, 3 Feb 2026 01:15:33 +0800 Subject: [PATCH 34/70] [7-Zip][ZIP] Remove support for CocoaPods optional dependencies --- Sources/7-Zip/7zFolder.swift | 18 +++--------------- Sources/ZIP/ZipContainer.swift | 26 +++++++++----------------- 2 files changed, 12 insertions(+), 32 deletions(-) diff --git a/Sources/7-Zip/7zFolder.swift b/Sources/7-Zip/7zFolder.swift index d917a12e..edcfdd7c 100644 --- a/Sources/7-Zip/7zFolder.swift +++ b/Sources/7-Zip/7zFolder.swift @@ -147,17 +147,9 @@ class SevenZipFolder { case .copy: continue case .deflate: - #if (!SWCOMPRESSION_POD_SEVENZIP) || (SWCOMPRESSION_POD_SEVENZIP && SWCOMPRESSION_POD_DEFLATE) - decodedData = try Deflate.decompress(data: decodedData) - #else - throw SevenZipError.compressionNotSupported - #endif + decodedData = try Deflate.decompress(data: decodedData) case .bzip2: - #if (!SWCOMPRESSION_POD_SEVENZIP) || (SWCOMPRESSION_POD_SEVENZIP && SWCOMPRESSION_POD_BZ2) - decodedData = try BZip2.decompress(data: decodedData) - #else - throw SevenZipError.compressionNotSupported - #endif + decodedData = try BZip2.decompress(data: decodedData) case .lzma2: // Dictionary size is stored in coder's properties. guard let properties = coder.properties, @@ -187,11 +179,7 @@ class SevenZipFolder { decodedData = DeltaFilter.decode(LittleEndianByteReader(data: decodedData), (properties[0] &+ 1).toInt()) } else if coder.id == [0x04, 0xF7, 0x11, 0x04] { - #if (!SWCOMPRESSION_POD_SEVENZIP) || (SWCOMPRESSION_POD_SEVENZIP && SWCOMPRESSION_POD_LZ4) - decodedData = try LZ4.decompress(data: decodedData) - #else - throw SevenZipError.compressionNotSupported - #endif + decodedData = try LZ4.decompress(data: decodedData) } else if coder.isEncryptionMethod { throw SevenZipError.encryptionNotSupported } else { diff --git a/Sources/ZIP/ZipContainer.swift b/Sources/ZIP/ZipContainer.swift index 97c14ffd..92300590 100644 --- a/Sources/ZIP/ZipContainer.swift +++ b/Sources/ZIP/ZipContainer.swift @@ -75,24 +75,16 @@ public class ZipContainer: Container { bitReader.align() byteReader.offset = bitReader.offset case .bzip2: - #if (!SWCOMPRESSION_POD_ZIP) || (SWCOMPRESSION_POD_ZIP && SWCOMPRESSION_POD_BZ2) - // BZip2 algorithm uses different bit numbering scheme. - let bitReader = MsbBitReader(byteReader) - fileData = try BZip2.decompress(bitReader) - // Sometimes bitReader is misaligned after BZip2 decompression, so we need to align before getting end - // index back. - bitReader.align() - byteReader.offset = bitReader.offset - #else - throw ZipError.compressionNotSupported - #endif + // BZip2 algorithm uses different bit numbering scheme. + let bitReader = MsbBitReader(byteReader) + fileData = try BZip2.decompress(bitReader) + // Sometimes bitReader is misaligned after BZip2 decompression, so we need to align before getting end + // index back. + bitReader.align() + byteReader.offset = bitReader.offset case .lzma: - #if (!SWCOMPRESSION_POD_ZIP) || (SWCOMPRESSION_POD_ZIP && SWCOMPRESSION_POD_LZMA) - byteReader.offset += 4 // Skipping LZMA SDK version and size of properties. - fileData = try LZMA.decompress(byteReader, LZMAProperties(byteReader), uncompSize.toInt()) - #else - throw ZipError.compressionNotSupported - #endif + byteReader.offset += 4 // Skipping LZMA SDK version and size of properties. + fileData = try LZMA.decompress(byteReader, LZMAProperties(byteReader), uncompSize.toInt()) default: throw ZipError.compressionNotSupported } From b5a0a02b54da42957dbe7a8f2cd42ff852600d0f Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Tue, 3 Feb 2026 01:16:33 +0800 Subject: [PATCH 35/70] Remove installation instructions with CocoaPods and Carthage from README --- README.md | 64 ++++--------------------------------------------------- 1 file changed, 4 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index dc28b024..fdcf456c 100644 --- a/README.md +++ b/README.md @@ -32,15 +32,12 @@ Also, SWCompression is _written with Swift only._ ## Installation -SWCompression can be integrated into your project using Swift Package Manager, CocoaPods, or Carthage. +SWCompression can be integrated into your project using Swift Package Manager. -__Note:__ Due to [upcoming](https://blog.cocoapods.org/CocoaPods-Specs-Repo/) deprecation of CocoaPods no new releases -of SWCompression will be published using CocoaPods. The last release of SWCompression made available via CocoaPods was -4.8.7. Consider switching to Swift Package Manager instead. +__Note:__ SWCompression versions 4.8.6 and earlier were also made available via CocoaPods or Carthage. -### Swift Package Manager - -To install using SPM, add SWCompression to you package dependencies and specify it as a dependency for your target, e.g.: +To install with Swift Package manager, add SWCompression to you package dependencies and specify it as a dependency for +your target, e.g.: ```swift import PackageDescription @@ -62,59 +59,6 @@ let package = Package( More details you can find in [Swift Package Manager's Documentation](https://github.com/apple/swift-package-manager/tree/main/Documentation). -### CocoaPods - -Add `pod 'SWCompression', '~> 4.8'` and `use_frameworks!` lines to your Podfile. - -To complete installation, run `pod install`. - -If you need only some parts of framework, you can install only them using sub-podspecs. Available subspecs: - -- SWCompression/BZip2 -- SWCompression/Deflate -- SWCompression/Gzip -- SWCompression/LZMA -- SWCompression/LZMA2 -- SWCompression/LZ4 -- SWCompression/SevenZip -- SWCompression/TAR -- SWCompression/XZ -- SWCompression/Zlib -- SWCompression/ZIP - -#### "Optional Dependencies" - -For both ZIP and 7-Zip there is the most commonly used compression method: Deflate and LZMA/LZMA2 correspondingly. Thus, -SWCompression/ZIP subspec has SWCompression/Deflate subspec as a dependency and SWCompression/LZMA subspec is a -dependency for SWCompression/SevenZip. - -But both of these formats also support other compression methods, and some of them are implemented in SWCompression. -For CocoaPods configurations there are some sort of 'optional dependencies' for such compression methods. - -"Optional dependency" in this context means that SWCompression/ZIP or SWCompression/7-Zip will support a compression -method only if a corresponding subspec is expicitly specified in your Podfile and installed. - -List of "optional dependecies": - -- For SWCompression/ZIP: - - SWCompression/BZip2 - - SWCompression/LZMA -- For SWCompression/SevenZip: - - SWCompression/BZip2 - - SWCompression/Deflate - - SWCompression/LZ4 - -__Note:__ If you use Swift Package Manager or Carthage you always have everything (ZIP and 7-Zip are built with Deflate, -BZip2, LZMA/LZMA2 and LZ4 support). - -### Carthage - -Add to your Cartfile `github "tsolomko/SWCompression" ~> 4.8`. - -Then you should run `carthage update --use-xcframeworks`. After that drag and drop both `SWCompression.xcframework` and -`BitByteData.xcframework` files from from the `Carthage/Build/` directory into the "Frameworks, Libraries, and Embedded -Content" section of your target's "General" tab in Xcode. - ## Usage ### Basic Example From 9916541aa834f0178625456272849c78f4f2ac26 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Tue, 3 Feb 2026 01:16:51 +0800 Subject: [PATCH 36/70] utils.py: change OS X to macOS --- utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils.py b/utils.py index 392a8fb9..d582adeb 100755 --- a/utils.py +++ b/utils.py @@ -23,7 +23,7 @@ def _ci_script_macos(): _sprun_shell("xcodebuild -version") _sprun(["swift", "--version"]) xcodebuild_command_parts = ["xcodebuild", "-quiet", "-project", "SWCompression.xcodeproj", "-scheme", "SWCompression"] - destinations_actions = [(["-destination 'platform=OS X'"], ["clean", "test"]), + destinations_actions = [(["-destination 'platform=macOS'"], ["clean", "test"]), (["-destination 'platform=iOS Simulator,name=" + os.environ["IOS_SIMULATOR"] + "'"], ["clean", "test"]), (["-destination 'platform=watchOS Simulator,name=" + os.environ["WATCHOS_SIMULATOR"] + "'"], ["clean", "test"]), (["-destination 'platform=tvOS Simulator,name=Apple TV'"], ["clean", "test"])] From 33341b690b6cf1c4ffecc620292a274726d8477d Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Tue, 3 Feb 2026 01:17:21 +0800 Subject: [PATCH 37/70] [LZ4] Code comments grammar and wording improvements --- Sources/LZ4/LZ4.swift | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/Sources/LZ4/LZ4.swift b/Sources/LZ4/LZ4.swift index 6d4aef2c..43c082b3 100644 --- a/Sources/LZ4/LZ4.swift +++ b/Sources/LZ4/LZ4.swift @@ -155,7 +155,7 @@ public enum LZ4: DecompressionAlgorithm { } // The functions below return uncompressed data and the offset to the next byte after the processed frame (even if - // the end of the input data was reached). The offset is needed to make multiDecompress function work. + // the end of the input data was reached). The offset is required to make multiDecompress function work. private static func process(legacyFrame data: Data) throws -> (Data, Data.Index) { let reader = LittleEndianByteReader(data: data) @@ -229,11 +229,11 @@ public enum LZ4: DecompressionAlgorithm { let contentSize: Int? if contentSizePresent { - // At this point valid LZ4 frame must have at least 13 bytes remaining for: content size (8 bytes), header + // At this point a valid LZ4 frame must have at least 13 bytes remaining for content size (8 bytes), header // checksum (1 byte), and EndMark (4 bytes), assuming zero data blocks. guard reader.bytesLeft >= 13 else { throw DataError.truncated } - // Since Data is indexed by the Int type, the maximum size of the uncompressed data that we can decode is + // Since Data is indexed by the Int type, the maximum size of uncompressed data that we can decode is // Int.max. However, LZ4 supports uncompressed data sizes up to UInt64.max, which is larger, so we check // for this possibility. let rawContentSize = reader.uint64() @@ -249,7 +249,7 @@ public enum LZ4: DecompressionAlgorithm { // If dictionary ID is present in the frame, then we must have a dictionary to successfully decode it. guard dictionary != nil else { throw DataError.corrupted } - // At this point valid LZ4 frame must have at least 9 bytes remaining for: dictionary ID (4 bytes), header + // At this point a valid LZ4 frame must have at least 9 bytes remaining for dictionary ID (4 bytes), header // checksum (1 byte), and EndMark (4 bytes), assuming zero data blocks. guard reader.bytesLeft >= 9 else { throw DataError.truncated } @@ -264,7 +264,7 @@ public enum LZ4: DecompressionAlgorithm { } if let extDictId = extDictId, let dictId = dictId { - // If dictionary ID is present in the frame, and passed as an argument, then they must be equal. + // If dictionary ID is present in the frame and passed as an argument, then they must be equal. guard extDictId == dictId else { throw DataError.corrupted } } @@ -278,7 +278,7 @@ public enum LZ4: DecompressionAlgorithm { while true { guard reader.bytesLeft >= 4 else { throw DataError.truncated } - // Either the size of the block, or the EndMark. + // Either the size of a block, or the EndMark. let blockMark = reader.uint32() // Check for the EndMark. if blockMark == 0 { @@ -287,8 +287,8 @@ public enum LZ4: DecompressionAlgorithm { // The highest bit indicates if the block is compressed. let compressed = blockMark & 0x80000000 == 0 let blockSize = (blockMark & 0x7FFFFFFF).toInt() - // Since we don't do manual memory allocation we don't have to constraint the block size. Nevertheless, we - // follow the reference implementation here and reject blocks with sizes greater than maximum block size. + // Since we don't do manual memory allocation we don't have to constrain the block size. Nevertheless, we + // follow the reference implementation here and reject blocks with sizes greater than the maximum block size. guard blockSize <= maxBlockSize else { throw DataError.corrupted } @@ -350,7 +350,7 @@ public enum LZ4: DecompressionAlgorithm { guard data.endIndex - reader.offset >= 1 else { throw DataError.truncated } let byte = reader.byte() - // There is no size limit on the literal count, so we need to check that it remains within Int range + // There is no size limit on the literal count, so we have to check that it remains within Int range // (similar to content size considerations). let (newLiteralCount, overflow) = literalCount.addingReportingOverflow(byte.toInt()) guard !overflow @@ -388,7 +388,7 @@ public enum LZ4: DecompressionAlgorithm { guard data.endIndex - reader.offset >= 1 else { throw DataError.truncated } let byte = reader.byte() - // Again, there is no size limit on the match length, so we need to check that it remains within Int + // Again, there is no size limit on the match length, so we have to check that it remains within Int // range. let (newMatchLength, overflow) = matchLength.addingReportingOverflow(byte.toInt()) guard !overflow @@ -401,8 +401,7 @@ public enum LZ4: DecompressionAlgorithm { } // We record the start index of the last encountered match to verify it against end-of-block restrictions. - // Note, that this refers to the bytes that we have found a match for, and not to the bytes that we're - // matching to. + // This refers to the bytes that we have found a match for, and not to the bytes that we're matching to. lastMatchStartIndex = out.endIndex let matchStartIndex = out.endIndex - offset for i in 0.. Date: Tue, 3 Feb 2026 12:27:27 +0800 Subject: [PATCH 38/70] [BZip2] Code comments clarification --- Sources/BZip2/BZip2+Compress.swift | 3 ++- Sources/BZip2/BZip2.swift | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Sources/BZip2/BZip2+Compress.swift b/Sources/BZip2/BZip2+Compress.swift index ed4cf7dc..e09ab1d6 100644 --- a/Sources/BZip2/BZip2+Compress.swift +++ b/Sources/BZip2/BZip2+Compress.swift @@ -41,7 +41,8 @@ extension BZip2: CompressionAlgorithm { public static func compress(data: Data, blockSize: BlockSize) -> Data { let bitWriter = MsbBitWriter() // We intentionally use smaller block size for compression to account for potential data size expansion - // after intial RLE, which seems to be not being expected by original BZip2 implementation. + // after intial RLE, which seems to be not being expected by original BZip2 implementation. + // In the worst case initial RLE causes expansion by a factor of 1.25, so 1000 / 1.25 = 800. let rawBlockSize = blockSize.sizeInKilobytes * 800 // BZip2 Header. bitWriter.write(number: 0x425a, bitsCount: 16) // Magic number = 'BZ'. diff --git a/Sources/BZip2/BZip2.swift b/Sources/BZip2/BZip2.swift index efbb844b..cf921553 100644 --- a/Sources/BZip2/BZip2.swift +++ b/Sources/BZip2/BZip2.swift @@ -26,7 +26,8 @@ public class BZip2: DecompressionAlgorithm { } static func decompress(_ bitReader: MsbBitReader) throws -> Data { - // Valid BZip2 "archive" must contain at least 14 bytes of data. + // Valid BZip2 "archive" must contain at least 14 bytes of data: magic number (2 bytes), method (1 byte), block + // size (1 byte), block type (6 bytes), block CRC (4 bytes). guard bitReader.bitsLeft >= 14 * 8 else { throw BZip2Error.wrongMagic } @@ -44,7 +45,7 @@ public class BZip2: DecompressionAlgorithm { var totalCRC: UInt32 = 0 while true { - // Using `Int64` because 48 bits may not fit into `Int` on some platforms. + // Using `UInt64` because 48 bits may not fit into `Int` on some platforms. let blockType = bitReader.uint64(fromBits: 48) let blockCRC32 = bitReader.uint32(fromBits: 32) From 60283d4a54106f9acc8997b8a6bb3588d02a1c3d Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Tue, 3 Feb 2026 12:27:41 +0800 Subject: [PATCH 39/70] Reorder PrivacyInfo file in Xcode project --- SWCompression.xcodeproj/project.pbxproj | 2 +- azure-pipelines.yml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/SWCompression.xcodeproj/project.pbxproj b/SWCompression.xcodeproj/project.pbxproj index cccc0158..4c450a1a 100644 --- a/SWCompression.xcodeproj/project.pbxproj +++ b/SWCompression.xcodeproj/project.pbxproj @@ -702,9 +702,9 @@ 06BE1ABE1DB410F100EE0F59 = { isa = PBXGroup; children = ( - E6E49D502BD507D700BFC756 /* PrivacyInfo.xcprivacy */, 06BE1ACA1DB410F100EE0F59 /* Sources */, 06F065A01FFB763300312A82 /* Tests */, + E6E49D502BD507D700BFC756 /* PrivacyInfo.xcprivacy */, E631055A27086132006EACC3 /* SWCompression.xctestplan */, 06BE1AC91DB410F100EE0F59 /* Products */, ); diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2d42e3d0..eb7f3d00 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -30,7 +30,6 @@ stages: DEVELOPER_DIR: '/Applications/Xcode_16.2.app' IOS_SIMULATOR: 'iPhone 16' WATCHOS_SIMULATOR: 'Apple Watch Series 10 (42mm)' - # TODO: When Swift 6.0 support is dropped we can remove i386 search path for the watchOS simulator in Xcode. macosSwift61: imageName: 'macOS-15' DEVELOPER_DIR: '/Applications/Xcode_16.4.app' From d8c4527c89abeb97e7574964485d09d7e438509c Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Wed, 4 Feb 2026 17:27:56 +0800 Subject: [PATCH 40/70] [BZip2] Slightly optimize MTF transform of selectors during compression It can be statically proven that the output of MTF transform lies within a certain range, so a guard check can be replaced with an assertion. --- Sources/BZip2/BZip2+Compress.swift | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Sources/BZip2/BZip2+Compress.swift b/Sources/BZip2/BZip2+Compress.swift index e09ab1d6..20d117bf 100644 --- a/Sources/BZip2/BZip2+Compress.swift +++ b/Sources/BZip2/BZip2+Compress.swift @@ -175,10 +175,14 @@ extension BZip2: CompressionAlgorithm { bitWriter.write(number: tables.count, bitsCount: 3) bitWriter.write(number: selectors.count, bitsCount: 15) - let mtfSelectors = mtf(selectors, characters: Array(0.. [Int] { + /// Assumes that the characters are given by a list of integers from 0 up to and including `maxValue`. + private static func mtf(_ array: [Int], maxValue: Int) -> [Int] { var out = [Int]() - /// Mutable copy of `characters`. - var dictionary = characters + var dictionary = Array(0...maxValue) for i in 0.. Date: Thu, 5 Feb 2026 15:02:44 +0800 Subject: [PATCH 41/70] [swcomp] Print benchmark metadatas in normal order --- Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift | 4 ++-- Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift b/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift index 8b6bed42..cce435e4 100644 --- a/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift +++ b/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift @@ -54,7 +54,7 @@ final class RunBenchmarkCommand: Command { if baseMetadatas.count == 1 { baseMetadatas[baseMetadatas.first!.key] = "" } - for (metadataUUID, index) in baseMetadatas.sorted(by: { $0.value < $1.value }) { + for (metadataUUID, index) in baseMetadatas.sorted(by: { Int($0.value.dropFirst().dropLast())! < Int($1.value.dropFirst().dropLast())! }) { print("BASE\(index) Metadata") print("----------------") baseSaveFile.metadatas[metadataUUID]!.print() @@ -110,7 +110,7 @@ final class RunBenchmarkCommand: Command { if let baseResults = baseResults[result.id] { print("\nNEW: average = \(benchmark.format(avg)), standard deviation = \(benchmark.format(std))") - for (other, baseUUID) in baseResults { + for (other, baseUUID) in baseResults.sorted(by: { Int(baseMetadatas[$0.1]!.dropFirst().dropLast())! < Int(baseMetadatas[$1.1]!.dropFirst().dropLast())! }) { print("BASE\(baseMetadatas[baseUUID]!): average = \(benchmark.format(other.avg)), standard deviation = \(benchmark.format(other.std))") result.printComparison(with: other) } diff --git a/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift b/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift index 6072869b..de451704 100644 --- a/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift +++ b/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift @@ -26,7 +26,7 @@ final class ShowBenchmarkCommand: Command { if newMetadatas.count == 1 { newMetadatas[newMetadatas.first!.key] = "" } - for (metadataUUID, index) in newMetadatas.sorted(by: { $0.value < $1.value }) { + for (metadataUUID, index) in newMetadatas.sorted(by: { Int($0.value.dropFirst().dropLast())! < Int($1.value.dropFirst().dropLast())! }) { print("NEW\(index) Metadata") print("---------------") newSaveFile.metadatas[metadataUUID]!.print() @@ -47,7 +47,7 @@ final class ShowBenchmarkCommand: Command { if baseMetadatas.count == 1 { baseMetadatas[baseMetadatas.first!.key] = "" } - for (metadataUUID, index) in baseMetadatas.sorted(by: { $0.value < $1.value }) { + for (metadataUUID, index) in baseMetadatas.sorted(by: { Int($0.value.dropFirst().dropLast())! < Int($1.value.dropFirst().dropLast())! }) { print("BASE\(index) Metadata") print("----------------") baseSaveFile.metadatas[metadataUUID]!.print() @@ -61,14 +61,14 @@ final class ShowBenchmarkCommand: Command { for resultId in newResults.keys.sorted() { let results = newResults[resultId]! - for (result, metadataUUID) in results { + for (result, metadataUUID) in results.sorted(by: { Int(newMetadatas[$0.1]!.dropFirst().dropLast())! < Int(newMetadatas[$1.1]!.dropFirst().dropLast())! }) { let benchmark = Benchmarks(rawValue: result.name)?.initialized(result.input) print("\(result.name) => \(result.input), iterations = \(result.iterCount)") print("NEW\(newMetadatas[metadataUUID]!): average = \(benchmark.format(result.avg)), standard deviation = \(benchmark.format(result.std))") if let baseResults = baseResults[resultId] { - for (other, baseUUID) in baseResults { + for (other, baseUUID) in baseResults.sorted(by: { Int(baseMetadatas[$0.1]!.dropFirst().dropLast())! < Int(baseMetadatas[$1.1]!.dropFirst().dropLast())! }) { print("BASE\(baseMetadatas[baseUUID]!): average = \(benchmark.format(other.avg)), standard deviation = \(benchmark.format(other.std))") result.printComparison(with: other) } From 5a0eb8187abe943bdd8fc3b6775fcf81750928f1 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sat, 7 Feb 2026 23:10:11 +0800 Subject: [PATCH 42/70] [XZ] Rename a file for consistency --- SWCompression.xcodeproj/project.pbxproj | 8 ++++---- ...yteReader+XZ.swift => LittleEndianByteReader+XZ.swift} | 0 2 files changed, 4 insertions(+), 4 deletions(-) rename Sources/XZ/{ByteReader+XZ.swift => LittleEndianByteReader+XZ.swift} (100%) diff --git a/SWCompression.xcodeproj/project.pbxproj b/SWCompression.xcodeproj/project.pbxproj index 4c450a1a..7315276f 100644 --- a/SWCompression.xcodeproj/project.pbxproj +++ b/SWCompression.xcodeproj/project.pbxproj @@ -96,7 +96,7 @@ 06BAC5941F8CFF28002443F4 /* ZipEntryInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BAC5931F8CFF28002443F4 /* ZipEntryInfo.swift */; }; 06BB8F301E0C2C720079FB9E /* LZMA2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BB8F2F1E0C2C720079FB9E /* LZMA2.swift */; }; 06CC3FDD1F8AAE8B00BD576D /* MsbBitReader+7z.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06CC3FDC1F8AAE8B00BD576D /* MsbBitReader+7z.swift */; }; - 06CC3FE21F8AAF3100BD576D /* ByteReader+XZ.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06CC3FE11F8AAF3100BD576D /* ByteReader+XZ.swift */; }; + 06CC3FE21F8AAF3100BD576D /* LittleEndianByteReader+XZ.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06CC3FE11F8AAF3100BD576D /* LittleEndianByteReader+XZ.swift */; }; 06CDFC9E1F111D2600292758 /* Deflate+Compress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06CDFC9D1F111D2600292758 /* Deflate+Compress.swift */; }; 06CDFCA31F111D6C00292758 /* BZip2Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06CDFCA21F111D6C00292758 /* BZip2Error.swift */; }; 06CDFCA81F111D9700292758 /* DeflateError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06CDFCA71F111D9700292758 /* DeflateError.swift */; }; @@ -396,7 +396,7 @@ 06BB8F2F1E0C2C720079FB9E /* LZMA2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LZMA2.swift; sourceTree = ""; }; 06BE1AC81DB410F100EE0F59 /* SWCompression.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SWCompression.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 06CC3FDC1F8AAE8B00BD576D /* MsbBitReader+7z.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MsbBitReader+7z.swift"; sourceTree = ""; }; - 06CC3FE11F8AAF3100BD576D /* ByteReader+XZ.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ByteReader+XZ.swift"; sourceTree = ""; }; + 06CC3FE11F8AAF3100BD576D /* LittleEndianByteReader+XZ.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LittleEndianByteReader+XZ.swift"; sourceTree = ""; }; 06CDFC9D1F111D2600292758 /* Deflate+Compress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deflate+Compress.swift"; sourceTree = ""; }; 06CDFCA21F111D6C00292758 /* BZip2Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BZip2Error.swift; sourceTree = ""; }; 06CDFCA71F111D9700292758 /* DeflateError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeflateError.swift; sourceTree = ""; }; @@ -683,7 +683,7 @@ 061C06201F0E89D900832F0C /* XZError.swift */, 065146221FA8E20C007F83F4 /* XZBlock.swift */, 0651461D1FA8E004007F83F4 /* XZStreamHeader.swift */, - 06CC3FE11F8AAF3100BD576D /* ByteReader+XZ.swift */, + 06CC3FE11F8AAF3100BD576D /* LittleEndianByteReader+XZ.swift */, 06516B75213938B000C9823B /* Sha256.swift */, ); path = XZ; @@ -1446,7 +1446,7 @@ 06CDFCA31F111D6C00292758 /* BZip2Error.swift in Sources */, 06D3802E21E36190008B50C6 /* Code.swift in Sources */, 06CFF6B22078C5A5003B8375 /* TarParser.swift in Sources */, - 06CC3FE21F8AAF3100BD576D /* ByteReader+XZ.swift in Sources */, + 06CC3FE21F8AAF3100BD576D /* LittleEndianByteReader+XZ.swift in Sources */, 065D0ADC1F2B9D9100CE2DA8 /* 7zEntryInfo.swift in Sources */, 06F276DF1F2BAB4A00E67335 /* 7zEntry.swift in Sources */, 0651461E1FA8E004007F83F4 /* XZStreamHeader.swift in Sources */, diff --git a/Sources/XZ/ByteReader+XZ.swift b/Sources/XZ/LittleEndianByteReader+XZ.swift similarity index 100% rename from Sources/XZ/ByteReader+XZ.swift rename to Sources/XZ/LittleEndianByteReader+XZ.swift From 5e8f568f6486b0a27a71c0dc77b5ef7624f5e502 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Wed, 11 Feb 2026 17:20:16 +0800 Subject: [PATCH 43/70] [swcomp] Save and show benchmark warmup iteration --- Sources/swcomp/Benchmarks/Benchmark.swift | 6 +++--- Sources/swcomp/Benchmarks/BenchmarkResult.swift | 1 + .../swcomp/Benchmarks/RunBenchmarkCommand.swift | 16 ++++++++++++---- .../swcomp/Benchmarks/ShowBenchmarkCommand.swift | 13 +++++++++++-- Sources/swcomp/Containers/CommonFunctions.swift | 2 +- 5 files changed, 28 insertions(+), 10 deletions(-) diff --git a/Sources/swcomp/Benchmarks/Benchmark.swift b/Sources/swcomp/Benchmarks/Benchmark.swift index 3f353e97..695b9d3c 100644 --- a/Sources/swcomp/Benchmarks/Benchmark.swift +++ b/Sources/swcomp/Benchmarks/Benchmark.swift @@ -9,7 +9,7 @@ protocol Benchmark { init(_ input: String) - func warmupIteration() + func warmupIteration() -> Double func measure() -> Double @@ -23,8 +23,8 @@ extension Benchmark { return 10 } - func warmupIteration() { - _ = measure() + func warmupIteration() -> Double { + return measure() } func format(_ value: Double) -> String { diff --git a/Sources/swcomp/Benchmarks/BenchmarkResult.swift b/Sources/swcomp/Benchmarks/BenchmarkResult.swift index e32563dc..2b1fd105 100644 --- a/Sources/swcomp/Benchmarks/BenchmarkResult.swift +++ b/Sources/swcomp/Benchmarks/BenchmarkResult.swift @@ -12,6 +12,7 @@ struct BenchmarkResult: Codable { var iterCount: Int var avg: Double var std: Double + var warmup: Double? var id: String { return [self.name, self.input, String(self.iterCount)].joined(separator: "<#>") diff --git a/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift b/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift index cce435e4..434257ae 100644 --- a/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift +++ b/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift @@ -77,10 +77,14 @@ final class RunBenchmarkCommand: Command { let benchmark = self.selectedBenchmark.initialized(input) let iterationCount = self.iterationCount ?? benchmark.defaultIterationCount + let warmup: Double? if !self.noWarmup { - print("Warmup iteration...") + print("Warmup iteration...", terminator: " ") // Zeroth (excluded) iteration. - benchmark.warmupIteration() + warmup = benchmark.warmupIteration() + print(benchmark.format(warmup!)) + } else { + warmup = nil } var sum = 0.0 @@ -106,12 +110,16 @@ final class RunBenchmarkCommand: Command { let avg = sum / Double(iterationCount) let std = sqrt(squareSum / Double(iterationCount) - sum * sum / Double(iterationCount * iterationCount)) let result = BenchmarkResult(name: self.selectedBenchmark.rawValue, input: input, iterCount: iterationCount, - avg: avg, std: std) + avg: avg, std: std, warmup: warmup) if let baseResults = baseResults[result.id] { print("\nNEW: average = \(benchmark.format(avg)), standard deviation = \(benchmark.format(std))") for (other, baseUUID) in baseResults.sorted(by: { Int(baseMetadatas[$0.1]!.dropFirst().dropLast())! < Int(baseMetadatas[$1.1]!.dropFirst().dropLast())! }) { - print("BASE\(baseMetadatas[baseUUID]!): average = \(benchmark.format(other.avg)), standard deviation = \(benchmark.format(other.std))") + if let otherWarmup = other.warmup { + print("BASE\(baseMetadatas[baseUUID]!): average = \(benchmark.format(other.avg)), standard deviation = \(benchmark.format(other.std)), warmup = \(benchmark.format(otherWarmup))") + } else { + print("BASE\(baseMetadatas[baseUUID]!): average = \(benchmark.format(other.avg)), standard deviation = \(benchmark.format(other.std))") + } result.printComparison(with: other) } } else { diff --git a/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift b/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift index de451704..c234c8a7 100644 --- a/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift +++ b/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift @@ -66,10 +66,19 @@ final class ShowBenchmarkCommand: Command { print("\(result.name) => \(result.input), iterations = \(result.iterCount)") - print("NEW\(newMetadatas[metadataUUID]!): average = \(benchmark.format(result.avg)), standard deviation = \(benchmark.format(result.std))") + + if let warmup = result.warmup { + print("NEW\(newMetadatas[metadataUUID]!): average = \(benchmark.format(result.avg)), standard deviation = \(benchmark.format(result.std)), warmup = \(benchmark.format(warmup))") + } else { + print("NEW\(newMetadatas[metadataUUID]!): average = \(benchmark.format(result.avg)), standard deviation = \(benchmark.format(result.std))") + } if let baseResults = baseResults[resultId] { for (other, baseUUID) in baseResults.sorted(by: { Int(baseMetadatas[$0.1]!.dropFirst().dropLast())! < Int(baseMetadatas[$1.1]!.dropFirst().dropLast())! }) { - print("BASE\(baseMetadatas[baseUUID]!): average = \(benchmark.format(other.avg)), standard deviation = \(benchmark.format(other.std))") + if let otherWarmup = other.warmup { + print("BASE\(baseMetadatas[baseUUID]!): average = \(benchmark.format(other.avg)), standard deviation = \(benchmark.format(other.std)), warmup = \(benchmark.format(otherWarmup))") + } else { + print("BASE\(baseMetadatas[baseUUID]!): average = \(benchmark.format(other.avg)), standard deviation = \(benchmark.format(other.std))") + } result.printComparison(with: other) } } diff --git a/Sources/swcomp/Containers/CommonFunctions.swift b/Sources/swcomp/Containers/CommonFunctions.swift index b41c8216..89db6a9d 100644 --- a/Sources/swcomp/Containers/CommonFunctions.swift +++ b/Sources/swcomp/Containers/CommonFunctions.swift @@ -147,4 +147,4 @@ func writeFile(_ entry: T, _ outputURL: URL, _ verbose: Bool) } try fileManager.setAttributes(attributes, ofItemAtPath: entryFullURL.path) -} \ No newline at end of file +} From 3537e0ada1b8632cc1fa004db6316950b63b2506 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Wed, 11 Feb 2026 17:27:02 +0800 Subject: [PATCH 44/70] [swcomp] Save raw benchmark data for posterity --- Sources/swcomp/Benchmarks/BenchmarkResult.swift | 1 + Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Sources/swcomp/Benchmarks/BenchmarkResult.swift b/Sources/swcomp/Benchmarks/BenchmarkResult.swift index 2b1fd105..d26c2902 100644 --- a/Sources/swcomp/Benchmarks/BenchmarkResult.swift +++ b/Sources/swcomp/Benchmarks/BenchmarkResult.swift @@ -13,6 +13,7 @@ struct BenchmarkResult: Codable { var avg: Double var std: Double var warmup: Double? + var iters: [Double]? var id: String { return [self.name, self.input, String(self.iterCount)].joined(separator: "<#>") diff --git a/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift b/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift index 434257ae..a0ad523c 100644 --- a/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift +++ b/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift @@ -94,6 +94,7 @@ final class RunBenchmarkCommand: Command { #if !os(Linux) fflush(__stdoutp) #endif + var iterations = [Double]() for i in 1...iterationCount { if i > 1 { print(", ", terminator: "") @@ -105,12 +106,13 @@ final class RunBenchmarkCommand: Command { #endif sum += speed squareSum += speed * speed + iterations.append(speed) } let avg = sum / Double(iterationCount) let std = sqrt(squareSum / Double(iterationCount) - sum * sum / Double(iterationCount * iterationCount)) let result = BenchmarkResult(name: self.selectedBenchmark.rawValue, input: input, iterCount: iterationCount, - avg: avg, std: std, warmup: warmup) + avg: avg, std: std, warmup: warmup, iters: iterations) if let baseResults = baseResults[result.id] { print("\nNEW: average = \(benchmark.format(avg)), standard deviation = \(benchmark.format(std))") From e236b502b713c04eb6283a62eece40e55a97e927 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Wed, 11 Feb 2026 17:54:06 +0800 Subject: [PATCH 45/70] [swcomp] Improve benchmark show output for multi-way comparsions --- Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift b/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift index c234c8a7..403783fd 100644 --- a/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift +++ b/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift @@ -61,12 +61,13 @@ final class ShowBenchmarkCommand: Command { for resultId in newResults.keys.sorted() { let results = newResults[resultId]! + print() + print("----------------") + print() + print("\(results[0].0.name) => \(results[0].0.input), iterations = \(results[0].0.iterCount)") + print() for (result, metadataUUID) in results.sorted(by: { Int(newMetadatas[$0.1]!.dropFirst().dropLast())! < Int(newMetadatas[$1.1]!.dropFirst().dropLast())! }) { let benchmark = Benchmarks(rawValue: result.name)?.initialized(result.input) - - print("\(result.name) => \(result.input), iterations = \(result.iterCount)") - - if let warmup = result.warmup { print("NEW\(newMetadatas[metadataUUID]!): average = \(benchmark.format(result.avg)), standard deviation = \(benchmark.format(result.std)), warmup = \(benchmark.format(warmup))") } else { @@ -82,8 +83,6 @@ final class ShowBenchmarkCommand: Command { result.printComparison(with: other) } } - - print() } } } From 6b701e0b2fd8fa96ffeba41045b6d740d41358d0 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Wed, 11 Feb 2026 19:14:26 +0800 Subject: [PATCH 46/70] [swcomp] Add --print-uuid option to benchmark show command This option enables printing of internal UUIDs of benchmark runs saved in the file. --- Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift b/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift index 403783fd..67e1ea98 100644 --- a/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift +++ b/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift @@ -18,6 +18,9 @@ final class ShowBenchmarkCommand: Command { @Key("-c", "--compare", description: "Compare with other saved benchmarks results") var comparePath: String? + @Flag("--print-uuid", description: "Prints internal UUIDs of saved benchmark runs") + var printUuid: Bool + @Param var path: String func execute() throws { @@ -29,6 +32,9 @@ final class ShowBenchmarkCommand: Command { for (metadataUUID, index) in newMetadatas.sorted(by: { Int($0.value.dropFirst().dropLast())! < Int($1.value.dropFirst().dropLast())! }) { print("NEW\(index) Metadata") print("---------------") + if self.printUuid { + print("UUID: \(metadataUUID)") + } newSaveFile.metadatas[metadataUUID]!.print() } @@ -50,6 +56,9 @@ final class ShowBenchmarkCommand: Command { for (metadataUUID, index) in baseMetadatas.sorted(by: { Int($0.value.dropFirst().dropLast())! < Int($1.value.dropFirst().dropLast())! }) { print("BASE\(index) Metadata") print("----------------") + if self.printUuid { + print("UUID: \(metadataUUID)") + } baseSaveFile.metadatas[metadataUUID]!.print() } From c05b37d3245f176f4bbceec9a1cf812b2e5d744a Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Wed, 11 Feb 2026 19:15:20 +0800 Subject: [PATCH 47/70] [swcomp] Add --metadata-only option to benchmark show command With this option only metadata is printed for the benchmark runs saved in the file. --- Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift b/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift index 67e1ea98..649ebb48 100644 --- a/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift +++ b/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift @@ -21,6 +21,9 @@ final class ShowBenchmarkCommand: Command { @Flag("--print-uuid", description: "Prints internal UUIDs of saved benchmark runs") var printUuid: Bool + @Flag("--metadata-only", description: "Prints only metadata of saved benchmark runs") + var metadataOnly: Bool + @Param var path: String func execute() throws { @@ -68,6 +71,10 @@ final class ShowBenchmarkCommand: Command { } } + if self.metadataOnly { + return + } + for resultId in newResults.keys.sorted() { let results = newResults[resultId]! print() From 5618fea2ee78353a419fa60ba4c0f0e796c0074d Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Wed, 11 Feb 2026 21:20:36 +0800 Subject: [PATCH 48/70] [swcomp] Add --self-compare option to benchmark show command This option allows to compare a run with other runs in the same file. --- .../Benchmarks/RunBenchmarkCommand.swift | 4 +- .../Benchmarks/ShowBenchmarkCommand.swift | 47 +++++++++++++++++-- Sources/swcomp/SwcompError.swift | 10 ++++ 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift b/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift index a0ad523c..7b8c81fb 100644 --- a/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift +++ b/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift @@ -32,7 +32,7 @@ final class RunBenchmarkCommand: Command { @Key("-d", "--description", description: "Adds a custom description when saving results") var description: String? - @Flag("-t", "--preserve-timestamp", description: "Adds a timestamp when saving a result") + @Flag("-t", "--preserve-timestamp", description: "Adds a timestamp when saving results") var preserveTimestamp: Bool @Flag("-W", "--no-warmup", description: "Disables warmup iteration") @@ -139,7 +139,7 @@ final class RunBenchmarkCommand: Command { var isDir = ObjCBool(false) let saveFileExists = FileManager.default.fileExists(atPath: savePath, isDirectory: &isDir) - if self.append && saveFileExists { + if self.append && saveFileExists { if isDir.boolValue { swcompExit(.benchmarkCannotAppendToDirectory) } diff --git a/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift b/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift index 649ebb48..905c442b 100644 --- a/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift +++ b/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift @@ -18,6 +18,9 @@ final class ShowBenchmarkCommand: Command { @Key("-c", "--compare", description: "Compare with other saved benchmarks results") var comparePath: String? + @Key("--self-compare", description: "Compare runs within the same file with new results identified by run UUID") + var selfCompare: String? + @Flag("--print-uuid", description: "Prints internal UUIDs of saved benchmark runs") var printUuid: Bool @@ -26,12 +29,26 @@ final class ShowBenchmarkCommand: Command { @Param var path: String + var optionGroups: [OptionGroup] { + return [.atMostOne($comparePath, $selfCompare)] + } + func execute() throws { let newSaveFile = try SaveFile.load(from: self.path) - var newMetadatas = Dictionary(uniqueKeysWithValues: zip(newSaveFile.metadatas.keys, (1...newSaveFile.metadatas.count).map { "(\($0))" })) - if newMetadatas.count == 1 { - newMetadatas[newMetadatas.first!.key] = "" + var newMetadatas: [UUID: String] + if let newUUIDString = self.selfCompare { + guard let newUUID = UUID(uuidString: newUUIDString) + else { swcompExit(.benchmarkBadUUID) } + guard newSaveFile.metadatas.contains(where: { $0.key == newUUID} ) + else { swcompExit(.benchmarkNoUUID) } + newMetadatas = [newUUID: ""] + } else { + newMetadatas = Dictionary(uniqueKeysWithValues: zip(newSaveFile.metadatas.keys, (1...newSaveFile.metadatas.count).map { "(\($0))" })) + if newMetadatas.count == 1 { + newMetadatas[newMetadatas.first!.key] = "" + } } + for (metadataUUID, index) in newMetadatas.sorted(by: { Int($0.value.dropFirst().dropLast())! < Int($1.value.dropFirst().dropLast())! }) { print("NEW\(index) Metadata") print("---------------") @@ -42,7 +59,7 @@ final class ShowBenchmarkCommand: Command { } var newResults = [String: [(BenchmarkResult, UUID)]]() - for newRun in newSaveFile.runs { + for newRun in newSaveFile.runs.filter ( { (run: SaveFile.Run) in newMetadatas.keys.contains(where: { $0 == run.metadataUUID }) }) { newResults.merge(Dictionary(grouping: newRun.results.map { ($0, newRun.metadataUUID) }, by: { $0.0.id }), uniquingKeysWith: { $0 + $1 }) } @@ -69,6 +86,28 @@ final class ShowBenchmarkCommand: Command { baseResults.merge(Dictionary(grouping: baseRun.results.map { ($0, baseRun.metadataUUID) }, by: { $0.0.id }), uniquingKeysWith: { $0 + $1 }) } + } else if let newUUIDString = self.selfCompare { + guard let newUUID = UUID(uuidString: newUUIDString) + else { swcompExit(.benchmarkBadUUID) } + guard newSaveFile.metadatas.contains(where: { $0.key == newUUID} ) + else { swcompExit(.benchmarkNoUUID) } + baseMetadatas = Dictionary(uniqueKeysWithValues: zip(newSaveFile.metadatas.keys.filter({ $0 != newUUID }), (1...(newSaveFile.metadatas.count - 1)).map { "(\($0))" })) + if baseMetadatas.count == 1 { + baseMetadatas[baseMetadatas.first!.key] = "" + } + for (metadataUUID, index) in baseMetadatas.sorted(by: { Int($0.value.dropFirst().dropLast())! < Int($1.value.dropFirst().dropLast())! }) { + print("BASE\(index) Metadata") + print("----------------") + if self.printUuid { + print("UUID: \(metadataUUID)") + } + newSaveFile.metadatas[metadataUUID]!.print() + } + + for baseRun in newSaveFile.runs.filter ( { (run: SaveFile.Run) in !newMetadatas.keys.contains(where: { $0 == run.metadataUUID }) }) { + baseResults.merge(Dictionary(grouping: baseRun.results.map { ($0, baseRun.metadataUUID) }, by: { $0.0.id }), + uniquingKeysWith: { $0 + $1 }) + } } if self.metadataOnly { diff --git a/Sources/swcomp/SwcompError.swift b/Sources/swcomp/SwcompError.swift index 50196d20..4fe3312c 100644 --- a/Sources/swcomp/SwcompError.swift +++ b/Sources/swcomp/SwcompError.swift @@ -19,6 +19,8 @@ enum SwcompError { case benchmarkReaderTarNoInputSize(String) case benchmarkCannotGetSubcommandPathWindows case benchmarkCannotAppendToDirectory + case benchmarkBadUUID + case benchmarkNoUUID case containerSymLinkDestPath(String) case containerHardLinkDestPath(String) case containerNoEntryData(String) @@ -54,6 +56,10 @@ enum SwcompError { return 206 case .benchmarkCannotAppendToDirectory: return 207 + case .benchmarkBadUUID: + return 208 + case .benchmarkNoUUID: + return 218 case .containerSymLinkDestPath: return 301 case .containerHardLinkDestPath: @@ -99,6 +105,10 @@ enum SwcompError { return "Cannot get subcommand path on Windows. (This error should never be shown!)" case .benchmarkCannotAppendToDirectory: return "Cannot append results to the save path since it is a directory." + case .benchmarkBadUUID: + return "Specified run UUID is not well-formed." + case .benchmarkNoUUID: + return "Specified run UUID is not found in the file." case .containerSymLinkDestPath(let entryName): return "Unable to get destination path for symbolic link \(entryName)." case .containerHardLinkDestPath(let entryName): From 871b5dbe03c57a416ab38798f21462529e6a7767 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Wed, 11 Feb 2026 21:53:00 +0800 Subject: [PATCH 49/70] [swcomp] Add benchmark remove-run subcommand --- .../swcomp/Benchmarks/BenchmarkGroup.swift | 3 +- .../swcomp/Benchmarks/RemoveRunCommand.swift | 37 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 Sources/swcomp/Benchmarks/RemoveRunCommand.swift diff --git a/Sources/swcomp/Benchmarks/BenchmarkGroup.swift b/Sources/swcomp/Benchmarks/BenchmarkGroup.swift index 2852c053..b7042baf 100644 --- a/Sources/swcomp/Benchmarks/BenchmarkGroup.swift +++ b/Sources/swcomp/Benchmarks/BenchmarkGroup.swift @@ -14,7 +14,8 @@ final class BenchmarkGroup: CommandGroup { let children: [Routable] = [ RunBenchmarkCommand(), - ShowBenchmarkCommand() + ShowBenchmarkCommand(), + RemoveRunCommand() ] } diff --git a/Sources/swcomp/Benchmarks/RemoveRunCommand.swift b/Sources/swcomp/Benchmarks/RemoveRunCommand.swift new file mode 100644 index 00000000..a40de786 --- /dev/null +++ b/Sources/swcomp/Benchmarks/RemoveRunCommand.swift @@ -0,0 +1,37 @@ +// Copyright (c) 2026 Timofey Solomko +// Licensed under MIT License +// +// See LICENSE for license information + +#if os(Linux) + import CoreFoundation +#endif + +import Foundation +import SwiftCLI + +final class RemoveRunCommand: Command { + + let name = "remove-run" + let shortDescription = "Removes run from the file" + let longDescription = "Removes a run identified by UUID and any associated results from the specified file" + + @Param var runUUID: String + @Param var path: String + + func execute() throws { + var saveFile = try SaveFile.load(from: self.path) + guard let uuid = UUID(uuidString: self.runUUID) + else { swcompExit(.benchmarkBadUUID) } + guard saveFile.metadatas.contains(where: { $0.key == uuid} ) + else { swcompExit(.benchmarkNoUUID) } + + saveFile.metadatas.removeValue(forKey: uuid) + saveFile.runs.removeAll(where: { $0.metadataUUID == uuid }) + + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + let data = try encoder.encode(saveFile) + try data.write(to: URL(fileURLWithPath: path)) + } +} From b2b4e18a581f2d38c731138f17b10b84ce8db209 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Wed, 11 Feb 2026 21:33:27 +0800 Subject: [PATCH 50/70] [swcomp] Remove redundant import statements --- Sources/swcomp/Benchmarks/RemoveRunCommand.swift | 4 ---- Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift | 1 - Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift | 4 ---- .../CompressionMethod+CustomStringConvertible.swift | 1 - .../ContainerEntryInfo+CustomStringConvertible.swift | 1 - .../Extensions/FileSystemType+CustomStringConvertible.swift | 1 - .../Extensions/GzipHeader+CustomStringConvertible.swift | 1 - .../swcomp/Extensions/TarFormat+ConvertibleFromString.swift | 1 - 8 files changed, 14 deletions(-) diff --git a/Sources/swcomp/Benchmarks/RemoveRunCommand.swift b/Sources/swcomp/Benchmarks/RemoveRunCommand.swift index a40de786..a9e1072c 100644 --- a/Sources/swcomp/Benchmarks/RemoveRunCommand.swift +++ b/Sources/swcomp/Benchmarks/RemoveRunCommand.swift @@ -3,10 +3,6 @@ // // See LICENSE for license information -#if os(Linux) - import CoreFoundation -#endif - import Foundation import SwiftCLI diff --git a/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift b/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift index 7b8c81fb..f9c58b76 100644 --- a/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift +++ b/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift @@ -8,7 +8,6 @@ #endif import Foundation -import SWCompression import SwiftCLI final class RunBenchmarkCommand: Command { diff --git a/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift b/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift index 905c442b..4fb08502 100644 --- a/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift +++ b/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift @@ -3,10 +3,6 @@ // // See LICENSE for license information -#if os(Linux) - import CoreFoundation -#endif - import Foundation import SwiftCLI diff --git a/Sources/swcomp/Extensions/CompressionMethod+CustomStringConvertible.swift b/Sources/swcomp/Extensions/CompressionMethod+CustomStringConvertible.swift index 67a49b00..5a3f66d3 100644 --- a/Sources/swcomp/Extensions/CompressionMethod+CustomStringConvertible.swift +++ b/Sources/swcomp/Extensions/CompressionMethod+CustomStringConvertible.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import SWCompression extension CompressionMethod: CustomStringConvertible { diff --git a/Sources/swcomp/Extensions/ContainerEntryInfo+CustomStringConvertible.swift b/Sources/swcomp/Extensions/ContainerEntryInfo+CustomStringConvertible.swift index 891c5c3f..e885adad 100644 --- a/Sources/swcomp/Extensions/ContainerEntryInfo+CustomStringConvertible.swift +++ b/Sources/swcomp/Extensions/ContainerEntryInfo+CustomStringConvertible.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import SWCompression extension ContainerEntryInfo where Self: CustomStringConvertible { diff --git a/Sources/swcomp/Extensions/FileSystemType+CustomStringConvertible.swift b/Sources/swcomp/Extensions/FileSystemType+CustomStringConvertible.swift index 98503229..6c0f40c8 100644 --- a/Sources/swcomp/Extensions/FileSystemType+CustomStringConvertible.swift +++ b/Sources/swcomp/Extensions/FileSystemType+CustomStringConvertible.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import SWCompression extension FileSystemType: CustomStringConvertible { diff --git a/Sources/swcomp/Extensions/GzipHeader+CustomStringConvertible.swift b/Sources/swcomp/Extensions/GzipHeader+CustomStringConvertible.swift index 25011661..a0cc5fa2 100644 --- a/Sources/swcomp/Extensions/GzipHeader+CustomStringConvertible.swift +++ b/Sources/swcomp/Extensions/GzipHeader+CustomStringConvertible.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import SWCompression extension GzipHeader: CustomStringConvertible { diff --git a/Sources/swcomp/Extensions/TarFormat+ConvertibleFromString.swift b/Sources/swcomp/Extensions/TarFormat+ConvertibleFromString.swift index 7d0231a1..5d1e9427 100644 --- a/Sources/swcomp/Extensions/TarFormat+ConvertibleFromString.swift +++ b/Sources/swcomp/Extensions/TarFormat+ConvertibleFromString.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import SwiftCLI import SWCompression From 3f5694aeee60ebbd9751b93500def43133d6b4de Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Thu, 12 Feb 2026 01:34:05 +0800 Subject: [PATCH 51/70] [swcomp] Rename SaveFile to OldSaveFile --- .../Benchmarks/{SaveFile.swift => OldSaveFile.swift} | 6 +++--- Sources/swcomp/Benchmarks/RemoveRunCommand.swift | 2 +- Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift | 10 +++++----- Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) rename Sources/swcomp/Benchmarks/{SaveFile.swift => OldSaveFile.swift} (72%) diff --git a/Sources/swcomp/Benchmarks/SaveFile.swift b/Sources/swcomp/Benchmarks/OldSaveFile.swift similarity index 72% rename from Sources/swcomp/Benchmarks/SaveFile.swift rename to Sources/swcomp/Benchmarks/OldSaveFile.swift index ac3b859f..61610f01 100644 --- a/Sources/swcomp/Benchmarks/SaveFile.swift +++ b/Sources/swcomp/Benchmarks/OldSaveFile.swift @@ -5,7 +5,7 @@ import Foundation -struct SaveFile: Codable { +struct OldSaveFile: Codable { struct Run: Codable { @@ -18,10 +18,10 @@ struct SaveFile: Codable { var runs: [Run] - static func load(from path: String) throws -> SaveFile { + static func load(from path: String) throws -> OldSaveFile { let decoder = JSONDecoder() let data = try Data(contentsOf: URL(fileURLWithPath: path)) - return try decoder.decode(SaveFile.self, from: data) + return try decoder.decode(OldSaveFile.self, from: data) } } diff --git a/Sources/swcomp/Benchmarks/RemoveRunCommand.swift b/Sources/swcomp/Benchmarks/RemoveRunCommand.swift index a9e1072c..8c8384b6 100644 --- a/Sources/swcomp/Benchmarks/RemoveRunCommand.swift +++ b/Sources/swcomp/Benchmarks/RemoveRunCommand.swift @@ -16,7 +16,7 @@ final class RemoveRunCommand: Command { @Param var path: String func execute() throws { - var saveFile = try SaveFile.load(from: self.path) + var saveFile = try OldSaveFile.load(from: self.path) guard let uuid = UUID(uuidString: self.runUUID) else { swcompExit(.benchmarkBadUUID) } guard saveFile.metadatas.contains(where: { $0.key == uuid} ) diff --git a/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift b/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift index f9c58b76..4a6edeab 100644 --- a/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift +++ b/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift @@ -47,7 +47,7 @@ final class RunBenchmarkCommand: Command { var baseResults = [String: [(BenchmarkResult, UUID)]]() var baseMetadatas = [UUID: String]() if let comparePath = comparePath { - let baseSaveFile = try SaveFile.load(from: comparePath) + let baseSaveFile = try OldSaveFile.load(from: comparePath) baseMetadatas = Dictionary(uniqueKeysWithValues: zip(baseSaveFile.metadatas.keys, (1...baseSaveFile.metadatas.count).map { "(\($0))" })) if baseMetadatas.count == 1 { @@ -133,7 +133,7 @@ final class RunBenchmarkCommand: Command { if let savePath = self.savePath { let metadata = try BenchmarkMetadata(self.description, self.preserveTimestamp) - var saveFile: SaveFile + var saveFile: OldSaveFile var isDir = ObjCBool(false) let saveFileExists = FileManager.default.fileExists(atPath: savePath, isDirectory: &isDir) @@ -142,7 +142,7 @@ final class RunBenchmarkCommand: Command { if isDir.boolValue { swcompExit(.benchmarkCannotAppendToDirectory) } - saveFile = try SaveFile.load(from: savePath) + saveFile = try OldSaveFile.load(from: savePath) var uuid: UUID if let foundUUID = saveFile.metadatas.first(where: { $0.value == metadata })?.key { uuid = foundUUID @@ -152,10 +152,10 @@ final class RunBenchmarkCommand: Command { } while saveFile.metadatas[uuid] != nil saveFile.metadatas[uuid] = metadata } - saveFile.runs.append(SaveFile.Run(metadataUUID: uuid, results: newResults)) + saveFile.runs.append(OldSaveFile.Run(metadataUUID: uuid, results: newResults)) } else { let uuid = UUID() - saveFile = SaveFile(metadatas: [uuid: metadata], runs: [SaveFile.Run(metadataUUID: uuid, results: newResults)]) + saveFile = OldSaveFile(metadatas: [uuid: metadata], runs: [OldSaveFile.Run(metadataUUID: uuid, results: newResults)]) } let encoder = JSONEncoder() diff --git a/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift b/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift index 4fb08502..ac78413c 100644 --- a/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift +++ b/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift @@ -30,7 +30,7 @@ final class ShowBenchmarkCommand: Command { } func execute() throws { - let newSaveFile = try SaveFile.load(from: self.path) + let newSaveFile = try OldSaveFile.load(from: self.path) var newMetadatas: [UUID: String] if let newUUIDString = self.selfCompare { guard let newUUID = UUID(uuidString: newUUIDString) @@ -55,7 +55,7 @@ final class ShowBenchmarkCommand: Command { } var newResults = [String: [(BenchmarkResult, UUID)]]() - for newRun in newSaveFile.runs.filter ( { (run: SaveFile.Run) in newMetadatas.keys.contains(where: { $0 == run.metadataUUID }) }) { + for newRun in newSaveFile.runs.filter ( { (run: OldSaveFile.Run) in newMetadatas.keys.contains(where: { $0 == run.metadataUUID }) }) { newResults.merge(Dictionary(grouping: newRun.results.map { ($0, newRun.metadataUUID) }, by: { $0.0.id }), uniquingKeysWith: { $0 + $1 }) } @@ -63,7 +63,7 @@ final class ShowBenchmarkCommand: Command { var baseResults = [String: [(BenchmarkResult, UUID)]]() var baseMetadatas = [UUID: String]() if let comparePath = comparePath { - let baseSaveFile = try SaveFile.load(from: comparePath) + let baseSaveFile = try OldSaveFile.load(from: comparePath) baseMetadatas = Dictionary(uniqueKeysWithValues: zip(baseSaveFile.metadatas.keys, (1...baseSaveFile.metadatas.count).map { "(\($0))" })) if baseMetadatas.count == 1 { @@ -100,7 +100,7 @@ final class ShowBenchmarkCommand: Command { newSaveFile.metadatas[metadataUUID]!.print() } - for baseRun in newSaveFile.runs.filter ( { (run: SaveFile.Run) in !newMetadatas.keys.contains(where: { $0 == run.metadataUUID }) }) { + for baseRun in newSaveFile.runs.filter ( { (run: OldSaveFile.Run) in !newMetadatas.keys.contains(where: { $0 == run.metadataUUID }) }) { baseResults.merge(Dictionary(grouping: baseRun.results.map { ($0, baseRun.metadataUUID) }, by: { $0.0.id }), uniquingKeysWith: { $0 + $1 }) } From 98b5ee3610f155231deff9c9ead5fb26a1d3c179 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Thu, 12 Feb 2026 11:45:53 +0800 Subject: [PATCH 52/70] [swcomp] Save results file JSON with keys sorted This ensures a more stable layout of JSON fields between updates to the same file. --- Sources/swcomp/Benchmarks/RemoveRunCommand.swift | 3 ++- Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/swcomp/Benchmarks/RemoveRunCommand.swift b/Sources/swcomp/Benchmarks/RemoveRunCommand.swift index 8c8384b6..779614e7 100644 --- a/Sources/swcomp/Benchmarks/RemoveRunCommand.swift +++ b/Sources/swcomp/Benchmarks/RemoveRunCommand.swift @@ -26,8 +26,9 @@ final class RemoveRunCommand: Command { saveFile.runs.removeAll(where: { $0.metadataUUID == uuid }) let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted + encoder.outputFormatting = [.prettyPrinted, .sortedKeys] let data = try encoder.encode(saveFile) try data.write(to: URL(fileURLWithPath: path)) } + } diff --git a/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift b/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift index 4a6edeab..8b9844fd 100644 --- a/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift +++ b/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift @@ -159,8 +159,7 @@ final class RunBenchmarkCommand: Command { } let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - + encoder.outputFormatting = [.prettyPrinted, .sortedKeys] let data = try encoder.encode(saveFile) try data.write(to: URL(fileURLWithPath: savePath)) } From 20d33b0966505ab50bbdc026626c8038b9c5034c Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Thu, 12 Feb 2026 11:49:10 +0800 Subject: [PATCH 53/70] [swcomp] Add a new benchmark save file format Ultimately, it turned out that the approach of the old save file format of separating runs metadatas and results only leads to super complicated code. Unfortunately, it didn't help with append-updates to save files (they are still loaded fully, their content is modified in memory, and the actual file is then overwritten). --- Sources/swcomp/Benchmarks/SaveFile.swift | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Sources/swcomp/Benchmarks/SaveFile.swift diff --git a/Sources/swcomp/Benchmarks/SaveFile.swift b/Sources/swcomp/Benchmarks/SaveFile.swift new file mode 100644 index 00000000..9ff6f0bf --- /dev/null +++ b/Sources/swcomp/Benchmarks/SaveFile.swift @@ -0,0 +1,27 @@ +// Copyright (c) 2026 Timofey Solomko +// Licensed under MIT License +// +// See LICENSE for license information + +import Foundation + +struct SaveFile: Codable { + + struct Run: Codable { + + var uuid: UUID + var metadata: BenchmarkMetadata + var results: [BenchmarkResult] + + } + + var formatVersion = 2 + var runs: [Run] + + static func load(from path: String) throws -> SaveFile { + let decoder = JSONDecoder() + let data = try Data(contentsOf: URL(fileURLWithPath: path)) + return try decoder.decode(SaveFile.self, from: data) + } + +} From 4ec437e9120192f6e793718a76f7bf057f433bb5 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Thu, 12 Feb 2026 13:02:58 +0800 Subject: [PATCH 54/70] [swcomp] Add benchmark convert command This command converts save file from old format to the new one. --- .../swcomp/Benchmarks/BenchmarkGroup.swift | 3 ++- .../swcomp/Benchmarks/ConvertCommand.swift | 25 +++++++++++++++++++ Sources/swcomp/Benchmarks/SaveFile.swift | 14 +++++++++++ Sources/swcomp/SwcompError.swift | 5 ++++ 4 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 Sources/swcomp/Benchmarks/ConvertCommand.swift diff --git a/Sources/swcomp/Benchmarks/BenchmarkGroup.swift b/Sources/swcomp/Benchmarks/BenchmarkGroup.swift index b7042baf..40eb0aa8 100644 --- a/Sources/swcomp/Benchmarks/BenchmarkGroup.swift +++ b/Sources/swcomp/Benchmarks/BenchmarkGroup.swift @@ -15,7 +15,8 @@ final class BenchmarkGroup: CommandGroup { let children: [Routable] = [ RunBenchmarkCommand(), ShowBenchmarkCommand(), - RemoveRunCommand() + RemoveRunCommand(), + ConvertCommand() ] } diff --git a/Sources/swcomp/Benchmarks/ConvertCommand.swift b/Sources/swcomp/Benchmarks/ConvertCommand.swift new file mode 100644 index 00000000..1781af30 --- /dev/null +++ b/Sources/swcomp/Benchmarks/ConvertCommand.swift @@ -0,0 +1,25 @@ +// Copyright (c) 2026 Timofey Solomko +// Licensed under MIT License +// +// See LICENSE for license information + +import Foundation +import SwiftCLI + +final class ConvertCommand: Command { + + let name = "convert" + let shortDescription = "Converts save file to a new format" + let longDescription = "Converts specified with saved benchmark results to a new format" + + @Param var path: String + + func execute() throws { + let oldSaveFile = try OldSaveFile.load(from: self.path) + let encoder = JSONEncoder() + encoder.outputFormatting = [.prettyPrinted, .sortedKeys] + let data = try encoder.encode(SaveFile(oldSaveFile)) + try data.write(to: URL(fileURLWithPath: path)) + } + +} diff --git a/Sources/swcomp/Benchmarks/SaveFile.swift b/Sources/swcomp/Benchmarks/SaveFile.swift index 9ff6f0bf..8ee00e6f 100644 --- a/Sources/swcomp/Benchmarks/SaveFile.swift +++ b/Sources/swcomp/Benchmarks/SaveFile.swift @@ -18,6 +18,20 @@ struct SaveFile: Codable { var formatVersion = 2 var runs: [Run] + init(_ oldSaveFile: OldSaveFile) { + var d = [UUID: [BenchmarkResult]]() + for run in oldSaveFile.runs { + d[run.metadataUUID] = (d[run.metadataUUID] ?? [BenchmarkResult]()) + run.results + } + + self.runs = [Run]() + for (uuid, results) in d { + guard let metadata = oldSaveFile.metadatas[uuid] + else { swcompExit(.benchmarkOldFormatNoUUIDMetadata(uuid)) } + self.runs.append(Run(uuid: uuid, metadata: metadata, results: results.sorted(by: { $0.id < $1.id }))) + } + } + static func load(from path: String) throws -> SaveFile { let decoder = JSONDecoder() let data = try Data(contentsOf: URL(fileURLWithPath: path)) diff --git a/Sources/swcomp/SwcompError.swift b/Sources/swcomp/SwcompError.swift index 4fe3312c..bf90ed94 100644 --- a/Sources/swcomp/SwcompError.swift +++ b/Sources/swcomp/SwcompError.swift @@ -21,6 +21,7 @@ enum SwcompError { case benchmarkCannotAppendToDirectory case benchmarkBadUUID case benchmarkNoUUID + case benchmarkOldFormatNoUUIDMetadata(UUID) case containerSymLinkDestPath(String) case containerHardLinkDestPath(String) case containerNoEntryData(String) @@ -60,6 +61,8 @@ enum SwcompError { return 208 case .benchmarkNoUUID: return 218 + case .benchmarkOldFormatNoUUIDMetadata: + return 209 case .containerSymLinkDestPath: return 301 case .containerHardLinkDestPath: @@ -109,6 +112,8 @@ enum SwcompError { return "Specified run UUID is not well-formed." case .benchmarkNoUUID: return "Specified run UUID is not found in the file." + case .benchmarkOldFormatNoUUIDMetadata(let uuid): + return "No metadata found in the old format save file for UUID = \(uuid)." case .containerSymLinkDestPath(let entryName): return "Unable to get destination path for symbolic link \(entryName)." case .containerHardLinkDestPath(let entryName): From 8847fe92ce2c073e7aeb005061da9f42a69993fa Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Thu, 12 Feb 2026 13:29:12 +0800 Subject: [PATCH 55/70] [swcomp] SaveFile.load now auto-converts from old format --- Sources/swcomp/Benchmarks/SaveFile.swift | 19 +++++++++++++++++-- Sources/swcomp/SwcompError.swift | 19 +++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/Sources/swcomp/Benchmarks/SaveFile.swift b/Sources/swcomp/Benchmarks/SaveFile.swift index 8ee00e6f..4caaaa57 100644 --- a/Sources/swcomp/Benchmarks/SaveFile.swift +++ b/Sources/swcomp/Benchmarks/SaveFile.swift @@ -33,9 +33,24 @@ struct SaveFile: Codable { } static func load(from path: String) throws -> SaveFile { - let decoder = JSONDecoder() let data = try Data(contentsOf: URL(fileURLWithPath: path)) - return try decoder.decode(SaveFile.self, from: data) + guard let generalDict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] + else { swcompExit(.benchmarkUnrecognizedSaveFile) } + if let formatVersion = generalDict["formatVersion"] { + guard let intFormatVersion = formatVersion as? Int + else { swcompExit(.benchmarkUnrecognizedFormatVersion) } + guard intFormatVersion == 2 + else { swcompExit(.benchmarkUnsupportedFormatVersion(intFormatVersion)) } + let decoder = JSONDecoder() + return try decoder.decode(SaveFile.self, from: data) + } else if generalDict["metadatas"] != nil && generalDict["runs"] != nil { + let decoder = JSONDecoder() + let oldSaveFile = try decoder.decode(OldSaveFile.self, from: data) + print("WARNING: Old save file format detected. Its support will be removed in the future. Use \'benchmark convert' to upgrade.") + return SaveFile(oldSaveFile) + } else { + swcompExit(.benchmarkUnrecognizedSaveFile) + } } } diff --git a/Sources/swcomp/SwcompError.swift b/Sources/swcomp/SwcompError.swift index bf90ed94..33b740d6 100644 --- a/Sources/swcomp/SwcompError.swift +++ b/Sources/swcomp/SwcompError.swift @@ -21,6 +21,9 @@ enum SwcompError { case benchmarkCannotAppendToDirectory case benchmarkBadUUID case benchmarkNoUUID + case benchmarkUnrecognizedSaveFile + case benchmarkUnrecognizedFormatVersion + case benchmarkUnsupportedFormatVersion(Int) case benchmarkOldFormatNoUUIDMetadata(UUID) case containerSymLinkDestPath(String) case containerHardLinkDestPath(String) @@ -61,8 +64,14 @@ enum SwcompError { return 208 case .benchmarkNoUUID: return 218 - case .benchmarkOldFormatNoUUIDMetadata: + case .benchmarkUnrecognizedSaveFile: return 209 + case .benchmarkUnrecognizedFormatVersion: + return 219 + case .benchmarkUnsupportedFormatVersion: + return 229 + case .benchmarkOldFormatNoUUIDMetadata: + return 239 case .containerSymLinkDestPath: return 301 case .containerHardLinkDestPath: @@ -112,8 +121,14 @@ enum SwcompError { return "Specified run UUID is not well-formed." case .benchmarkNoUUID: return "Specified run UUID is not found in the file." + case .benchmarkUnrecognizedSaveFile: + return "The save file format is not recognized." + case .benchmarkUnrecognizedFormatVersion: + return "The save file format version is not recognized." + case .benchmarkUnsupportedFormatVersion(let formatVersion): + return "The save file format version \(formatVersion) is not supported." case .benchmarkOldFormatNoUUIDMetadata(let uuid): - return "No metadata found in the old format save file for UUID = \(uuid)." + return "No metadata found in an old format save file for UUID = \(uuid)." case .containerSymLinkDestPath(let entryName): return "Unable to get destination path for symbolic link \(entryName)." case .containerHardLinkDestPath(let entryName): From 515c11fd0e85eee89695680f448aa41a8e6428bb Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Thu, 12 Feb 2026 13:29:41 +0800 Subject: [PATCH 56/70] [swcomp] Support new format in benchmark remove-run command --- .../swcomp/Benchmarks/RemoveRunCommand.swift | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/Sources/swcomp/Benchmarks/RemoveRunCommand.swift b/Sources/swcomp/Benchmarks/RemoveRunCommand.swift index 779614e7..c2d89e04 100644 --- a/Sources/swcomp/Benchmarks/RemoveRunCommand.swift +++ b/Sources/swcomp/Benchmarks/RemoveRunCommand.swift @@ -16,19 +16,18 @@ final class RemoveRunCommand: Command { @Param var path: String func execute() throws { - var saveFile = try OldSaveFile.load(from: self.path) + var saveFile = try SaveFile.load(from: self.path) guard let uuid = UUID(uuidString: self.runUUID) else { swcompExit(.benchmarkBadUUID) } - guard saveFile.metadatas.contains(where: { $0.key == uuid} ) - else { swcompExit(.benchmarkNoUUID) } - - saveFile.metadatas.removeValue(forKey: uuid) - saveFile.runs.removeAll(where: { $0.metadataUUID == uuid }) - - let encoder = JSONEncoder() - encoder.outputFormatting = [.prettyPrinted, .sortedKeys] - let data = try encoder.encode(saveFile) - try data.write(to: URL(fileURLWithPath: path)) + if saveFile.runs.contains(where: { $0.uuid == uuid} ) { + saveFile.runs.removeAll(where: { $0.uuid == uuid }) + let encoder = JSONEncoder() + encoder.outputFormatting = [.prettyPrinted, .sortedKeys] + let data = try encoder.encode(saveFile) + try data.write(to: URL(fileURLWithPath: path)) + } else { + print("WARNING: Specified run UUID is not found in the file. No changes made.") + } } } From 52961ea60dfc695e1983ecf6a749fe7016629fed Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Thu, 12 Feb 2026 14:46:55 +0800 Subject: [PATCH 57/70] [swcomp] Update benchmark show command to use new format This allowed to rewrite implementation in a much clearer way. One user-visible change, however, is that NEW/BASE is now always printed with an index even if there is only one (implementation complexity to accommodate empty index is not worth it). --- Sources/swcomp/Benchmarks/SaveFile.swift | 11 ++ .../Benchmarks/ShowBenchmarkCommand.swift | 125 +++++++----------- 2 files changed, 56 insertions(+), 80 deletions(-) diff --git a/Sources/swcomp/Benchmarks/SaveFile.swift b/Sources/swcomp/Benchmarks/SaveFile.swift index 4caaaa57..3353c7d6 100644 --- a/Sources/swcomp/Benchmarks/SaveFile.swift +++ b/Sources/swcomp/Benchmarks/SaveFile.swift @@ -53,4 +53,15 @@ struct SaveFile: Codable { } } + static func groupResults(runs: [SaveFile.Run]) -> [String: [(Int, BenchmarkResult)]] { + var groupedResults = [String: [(Int, BenchmarkResult)]]() + for (index, run) in runs.enumerated() { + for result in run.results { + let resultId = result.id + groupedResults[resultId] = (groupedResults[resultId] ?? Array()) + [(index, result)] + } + } + return groupedResults + } + } diff --git a/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift b/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift index ac78413c..473e2677 100644 --- a/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift +++ b/Sources/swcomp/Benchmarks/ShowBenchmarkCommand.swift @@ -30,114 +30,79 @@ final class ShowBenchmarkCommand: Command { } func execute() throws { - let newSaveFile = try OldSaveFile.load(from: self.path) - var newMetadatas: [UUID: String] + let newSaveFile = try SaveFile.load(from: self.path) + var newRuns: [SaveFile.Run] if let newUUIDString = self.selfCompare { guard let newUUID = UUID(uuidString: newUUIDString) else { swcompExit(.benchmarkBadUUID) } - guard newSaveFile.metadatas.contains(where: { $0.key == newUUID} ) + guard let newRun = newSaveFile.runs.first(where: { $0.uuid == newUUID} ) else { swcompExit(.benchmarkNoUUID) } - newMetadatas = [newUUID: ""] + newRuns = [newRun] } else { - newMetadatas = Dictionary(uniqueKeysWithValues: zip(newSaveFile.metadatas.keys, (1...newSaveFile.metadatas.count).map { "(\($0))" })) - if newMetadatas.count == 1 { - newMetadatas[newMetadatas.first!.key] = "" - } - } - - for (metadataUUID, index) in newMetadatas.sorted(by: { Int($0.value.dropFirst().dropLast())! < Int($1.value.dropFirst().dropLast())! }) { - print("NEW\(index) Metadata") - print("---------------") - if self.printUuid { - print("UUID: \(metadataUUID)") - } - newSaveFile.metadatas[metadataUUID]!.print() - } - - var newResults = [String: [(BenchmarkResult, UUID)]]() - for newRun in newSaveFile.runs.filter ( { (run: OldSaveFile.Run) in newMetadatas.keys.contains(where: { $0 == run.metadataUUID }) }) { - newResults.merge(Dictionary(grouping: newRun.results.map { ($0, newRun.metadataUUID) }, by: { $0.0.id }), - uniquingKeysWith: { $0 + $1 }) + newRuns = newSaveFile.runs } + self.printMetadatas(runs: newRuns, name: "NEW") - var baseResults = [String: [(BenchmarkResult, UUID)]]() - var baseMetadatas = [UUID: String]() + var baseRuns = [SaveFile.Run]() if let comparePath = comparePath { - let baseSaveFile = try OldSaveFile.load(from: comparePath) - - baseMetadatas = Dictionary(uniqueKeysWithValues: zip(baseSaveFile.metadatas.keys, (1...baseSaveFile.metadatas.count).map { "(\($0))" })) - if baseMetadatas.count == 1 { - baseMetadatas[baseMetadatas.first!.key] = "" - } - for (metadataUUID, index) in baseMetadatas.sorted(by: { Int($0.value.dropFirst().dropLast())! < Int($1.value.dropFirst().dropLast())! }) { - print("BASE\(index) Metadata") - print("----------------") - if self.printUuid { - print("UUID: \(metadataUUID)") - } - baseSaveFile.metadatas[metadataUUID]!.print() - } - - for baseRun in baseSaveFile.runs { - baseResults.merge(Dictionary(grouping: baseRun.results.map { ($0, baseRun.metadataUUID) }, by: { $0.0.id }), - uniquingKeysWith: { $0 + $1 }) - } + let baseSaveFile = try SaveFile.load(from: comparePath) + baseRuns = baseSaveFile.runs } else if let newUUIDString = self.selfCompare { - guard let newUUID = UUID(uuidString: newUUIDString) - else { swcompExit(.benchmarkBadUUID) } - guard newSaveFile.metadatas.contains(where: { $0.key == newUUID} ) - else { swcompExit(.benchmarkNoUUID) } - baseMetadatas = Dictionary(uniqueKeysWithValues: zip(newSaveFile.metadatas.keys.filter({ $0 != newUUID }), (1...(newSaveFile.metadatas.count - 1)).map { "(\($0))" })) - if baseMetadatas.count == 1 { - baseMetadatas[baseMetadatas.first!.key] = "" - } - for (metadataUUID, index) in baseMetadatas.sorted(by: { Int($0.value.dropFirst().dropLast())! < Int($1.value.dropFirst().dropLast())! }) { - print("BASE\(index) Metadata") - print("----------------") - if self.printUuid { - print("UUID: \(metadataUUID)") - } - newSaveFile.metadatas[metadataUUID]!.print() - } - - for baseRun in newSaveFile.runs.filter ( { (run: OldSaveFile.Run) in !newMetadatas.keys.contains(where: { $0 == run.metadataUUID }) }) { - baseResults.merge(Dictionary(grouping: baseRun.results.map { ($0, baseRun.metadataUUID) }, by: { $0.0.id }), - uniquingKeysWith: { $0 + $1 }) - } + // No need to double-check validity of input UUID. It was done already when loading the "new" run. + let newUUID = UUID(uuidString: newUUIDString)! + baseRuns = newSaveFile.runs.filter({ $0.uuid != newUUID }) } + self.printMetadatas(runs: baseRuns, name: "BASE") - if self.metadataOnly { - return - } + guard !self.metadataOnly + else { return } - for resultId in newResults.keys.sorted() { - let results = newResults[resultId]! + // There might be results for the same benchmark-input-iterCount tuple in different runs. We want to print those + // results together, so we have to group them based on their `id`. + let newResults = SaveFile.groupResults(runs: newRuns) + let baseResults = SaveFile.groupResults(runs: baseRuns) + + // Even though we try to sort results before writing a save file, after grouping they can appear in `newResults` + // in essentially arbitrary order. + for id in newResults.keys.sorted() { + let results = newResults[id]! print() print("----------------") print() - print("\(results[0].0.name) => \(results[0].0.input), iterations = \(results[0].0.iterCount)") + print("\(results.first!.1.name) => \(results.first!.1.input), iterations = \(results.first!.1.iterCount)") print() - for (result, metadataUUID) in results.sorted(by: { Int(newMetadatas[$0.1]!.dropFirst().dropLast())! < Int(newMetadatas[$1.1]!.dropFirst().dropLast())! }) { + for (index, result) in results { let benchmark = Benchmarks(rawValue: result.name)?.initialized(result.input) if let warmup = result.warmup { - print("NEW\(newMetadatas[metadataUUID]!): average = \(benchmark.format(result.avg)), standard deviation = \(benchmark.format(result.std)), warmup = \(benchmark.format(warmup))") + print("NEW(\(index + 1)): average = \(benchmark.format(result.avg)), standard deviation = \(benchmark.format(result.std)), warmup = \(benchmark.format(warmup))") } else { - print("NEW\(newMetadatas[metadataUUID]!): average = \(benchmark.format(result.avg)), standard deviation = \(benchmark.format(result.std))") + print("NEW(\(index + 1)): average = \(benchmark.format(result.avg)), standard deviation = \(benchmark.format(result.std))") } - if let baseResults = baseResults[resultId] { - for (other, baseUUID) in baseResults.sorted(by: { Int(baseMetadatas[$0.1]!.dropFirst().dropLast())! < Int(baseMetadatas[$1.1]!.dropFirst().dropLast())! }) { - if let otherWarmup = other.warmup { - print("BASE\(baseMetadatas[baseUUID]!): average = \(benchmark.format(other.avg)), standard deviation = \(benchmark.format(other.std)), warmup = \(benchmark.format(otherWarmup))") + if let baseResults = baseResults[id] { + for (baseIndex, baseResult) in baseResults { + if let baseWarmup = baseResult.warmup { + print("BASE(\(baseIndex + 1)): average = \(benchmark.format(baseResult.avg)), standard deviation = \(benchmark.format(baseResult.std)), warmup = \(benchmark.format(baseWarmup))") } else { - print("BASE\(baseMetadatas[baseUUID]!): average = \(benchmark.format(other.avg)), standard deviation = \(benchmark.format(other.std))") + print("BASE(\(baseIndex + 1)): average = \(benchmark.format(baseResult.avg)), standard deviation = \(benchmark.format(baseResult.std))") } - result.printComparison(with: other) + result.printComparison(with: baseResult) } } } } } + private func printMetadatas(runs: [SaveFile.Run], name: String) { + for (index, run) in runs.enumerated() { + print("\(name)(\(index + 1)) Metadata") + print("---------------") + if self.printUuid { + print("UUID: \(run.uuid)") + } + run.metadata.print() + } + } + } fileprivate extension Optional where Wrapped == Benchmark { From d76351caa0a8552bd72558da538edc23a1eba7a4 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Thu, 12 Feb 2026 21:40:05 +0800 Subject: [PATCH 58/70] [swcomp] Update benchmark run command to use new format --- Sources/swcomp/Benchmarks/OldSaveFile.swift | 1 - .../Benchmarks/RunBenchmarkCommand.swift | 61 +++++++++---------- Sources/swcomp/Benchmarks/SaveFile.swift | 4 ++ 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/Sources/swcomp/Benchmarks/OldSaveFile.swift b/Sources/swcomp/Benchmarks/OldSaveFile.swift index 61610f01..dbaba7a3 100644 --- a/Sources/swcomp/Benchmarks/OldSaveFile.swift +++ b/Sources/swcomp/Benchmarks/OldSaveFile.swift @@ -15,7 +15,6 @@ struct OldSaveFile: Codable { } var metadatas: [UUID: BenchmarkMetadata] - var runs: [Run] static func load(from path: String) throws -> OldSaveFile { diff --git a/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift b/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift index 8b9844fd..ffa16220 100644 --- a/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift +++ b/Sources/swcomp/Benchmarks/RunBenchmarkCommand.swift @@ -44,26 +44,13 @@ final class RunBenchmarkCommand: Command { guard self.iterationCount == nil || self.iterationCount! >= 1 else { swcompExit(.benchmarkSmallIterCount) } - var baseResults = [String: [(BenchmarkResult, UUID)]]() - var baseMetadatas = [UUID: String]() + var baseRuns = [SaveFile.Run]() if let comparePath = comparePath { - let baseSaveFile = try OldSaveFile.load(from: comparePath) - - baseMetadatas = Dictionary(uniqueKeysWithValues: zip(baseSaveFile.metadatas.keys, (1...baseSaveFile.metadatas.count).map { "(\($0))" })) - if baseMetadatas.count == 1 { - baseMetadatas[baseMetadatas.first!.key] = "" - } - for (metadataUUID, index) in baseMetadatas.sorted(by: { Int($0.value.dropFirst().dropLast())! < Int($1.value.dropFirst().dropLast())! }) { - print("BASE\(index) Metadata") - print("----------------") - baseSaveFile.metadatas[metadataUUID]!.print() - } - - for baseRun in baseSaveFile.runs { - baseResults.merge(Dictionary(grouping: baseRun.results.map { ($0, baseRun.metadataUUID) }, by: { $0.0.id }), - uniquingKeysWith: { $0 + $1 }) - } + let baseSaveFile = try SaveFile.load(from: comparePath) + baseRuns = baseSaveFile.runs } + self.printBaseMetadatas(runs: baseRuns) + let baseResults = SaveFile.groupResults(runs: baseRuns) let title = "\(self.selectedBenchmark.titleName) Benchmark\n" print(String(repeating: "=", count: title.count)) @@ -115,13 +102,13 @@ final class RunBenchmarkCommand: Command { if let baseResults = baseResults[result.id] { print("\nNEW: average = \(benchmark.format(avg)), standard deviation = \(benchmark.format(std))") - for (other, baseUUID) in baseResults.sorted(by: { Int(baseMetadatas[$0.1]!.dropFirst().dropLast())! < Int(baseMetadatas[$1.1]!.dropFirst().dropLast())! }) { - if let otherWarmup = other.warmup { - print("BASE\(baseMetadatas[baseUUID]!): average = \(benchmark.format(other.avg)), standard deviation = \(benchmark.format(other.std)), warmup = \(benchmark.format(otherWarmup))") + for (baseIndex, baseResult) in baseResults { + if let baseWarmup = baseResult.warmup { + print("BASE(\(baseIndex + 1)): average = \(benchmark.format(baseResult.avg)), standard deviation = \(benchmark.format(baseResult.std)), warmup = \(benchmark.format(baseWarmup))") } else { - print("BASE\(baseMetadatas[baseUUID]!): average = \(benchmark.format(other.avg)), standard deviation = \(benchmark.format(other.std))") + print("BASE(\(baseIndex + 1)): average = \(benchmark.format(baseResult.avg)), standard deviation = \(benchmark.format(baseResult.std))") } - result.printComparison(with: other) + result.printComparison(with: baseResult) } } else { print("\nAverage = \(benchmark.format(avg)), standard deviation = \(benchmark.format(std))") @@ -133,7 +120,7 @@ final class RunBenchmarkCommand: Command { if let savePath = self.savePath { let metadata = try BenchmarkMetadata(self.description, self.preserveTimestamp) - var saveFile: OldSaveFile + var saveFile: SaveFile var isDir = ObjCBool(false) let saveFileExists = FileManager.default.fileExists(atPath: savePath, isDirectory: &isDir) @@ -142,20 +129,22 @@ final class RunBenchmarkCommand: Command { if isDir.boolValue { swcompExit(.benchmarkCannotAppendToDirectory) } - saveFile = try OldSaveFile.load(from: savePath) - var uuid: UUID - if let foundUUID = saveFile.metadatas.first(where: { $0.value == metadata })?.key { - uuid = foundUUID + saveFile = try SaveFile.load(from: savePath) + if let foundRunIndex = saveFile.runs.firstIndex(where: { $0.metadata == metadata }) { + var foundRun = saveFile.runs[foundRunIndex] + foundRun.results.append(contentsOf: newResults) + foundRun.results.sort(by: { $0.id < $1.id }) + saveFile.runs[foundRunIndex] = foundRun } else { + var uuid: UUID repeat { uuid = UUID() - } while saveFile.metadatas[uuid] != nil - saveFile.metadatas[uuid] = metadata + } while saveFile.runs.contains(where: { $0.uuid == uuid }) + saveFile.runs.append(SaveFile.Run(uuid: uuid, metadata: metadata, results: newResults.sorted(by: { $0.id < $1.id }))) } - saveFile.runs.append(OldSaveFile.Run(metadataUUID: uuid, results: newResults)) } else { let uuid = UUID() - saveFile = OldSaveFile(metadatas: [uuid: metadata], runs: [OldSaveFile.Run(metadataUUID: uuid, results: newResults)]) + saveFile = SaveFile(runs: [SaveFile.Run(uuid: uuid, metadata: metadata, results: newResults.sorted(by: { $0.id < $1.id }))]) } let encoder = JSONEncoder() @@ -165,4 +154,12 @@ final class RunBenchmarkCommand: Command { } } + private func printBaseMetadatas(runs: [SaveFile.Run]) { + for (index, run) in runs.enumerated() { + print("BASE(\(index + 1)) Metadata") + print("---------------") + run.metadata.print() + } + } + } diff --git a/Sources/swcomp/Benchmarks/SaveFile.swift b/Sources/swcomp/Benchmarks/SaveFile.swift index 3353c7d6..2226d052 100644 --- a/Sources/swcomp/Benchmarks/SaveFile.swift +++ b/Sources/swcomp/Benchmarks/SaveFile.swift @@ -32,6 +32,10 @@ struct SaveFile: Codable { } } + init(runs: [SaveFile.Run]) { + self.runs = runs + } + static func load(from path: String) throws -> SaveFile { let data = try Data(contentsOf: URL(fileURLWithPath: path)) guard let generalDict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] From f5b8a393ced262ad4311c6c2d90a42a655b67d28 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Fri, 13 Feb 2026 13:23:03 +0800 Subject: [PATCH 59/70] Cleanup gitignore --- .gitignore | 51 +++------------------------------------------------ 1 file changed, 3 insertions(+), 48 deletions(-) diff --git a/.gitignore b/.gitignore index edd94d5c..4d21141d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,9 +2,6 @@ # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore -## Custom -Tests/temp - ## Build generated build/ DerivedData/ @@ -26,58 +23,16 @@ xcuserdata/ *.xccheckout *.xcscmblueprint -## Obj-C/Swift specific -*.hmap -*.ipa -*.dSYM.zip -*.dSYM - -## Playgrounds -timeline.xctimeline -playground.xcworkspace - # Swift Package Manager # # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. # Packages/ -.build/ Package.pins - -# CocoaPods -Pods/ - -# Carthage -# Avoid checking in source code from Carthage dependencies. -Carthage/Checkouts -Carthage/Build -Cartfile.resolved - -# fastlane -# -# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the -# screenshots whenever they are needed. -# For more information about the recommended setup visit: -# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md - -fastlane/report.xml -fastlane/Preview.html -fastlane/screenshots -fastlane/test_output - -# Symbolic link to swcomp executable built by SPM -/swcomp - -# Package.resolved, because we don't want to fix dependencies' versions Package.resolved +.build/ -# Docs generated by SourceKitten +# Ignore docs generated by SourceKitten docs.json -# Docs generated by Jazzy +# Ignore docs generated by Jazzy docs/ - -# Vscode launch.json generated by Swift extension -.vscode/launch.json - -# API baselines generate by swift package diagnose-api-breaking-changes -api_baseline/ From ce1736a2b0e07e58c88ddf7901a8a0481b327eca Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Wed, 18 Feb 2026 00:10:29 +0800 Subject: [PATCH 60/70] [Huffman] Add HuffmanCodes typealias for clarity --- Sources/BZip2/BZip2.swift | 2 +- Sources/Common/CodingTree/Code.swift | 4 +++- Sources/Common/CodingTree/DecodingTree.swift | 6 +++--- Sources/Deflate/Deflate+Compress.swift | 4 ++-- Sources/Deflate/Deflate+Constants.swift | 12 ++++++------ Sources/Deflate/Deflate.swift | 11 +++++------ 6 files changed, 20 insertions(+), 19 deletions(-) diff --git a/Sources/BZip2/BZip2.swift b/Sources/BZip2/BZip2.swift index cf921553..8d991fe6 100644 --- a/Sources/BZip2/BZip2.swift +++ b/Sources/BZip2/BZip2.swift @@ -147,7 +147,7 @@ public class BZip2: DecompressionAlgorithm { } } let codes = Code.huffmanCodes(from: lengths) - let table = DecodingTree(codes: codes.codes, maxBits: codes.maxBits, bitReader) + let table = DecodingTree(codes, bitReader) tables.append(table) } diff --git a/Sources/Common/CodingTree/Code.swift b/Sources/Common/CodingTree/Code.swift index fcbe7999..f580cf9b 100644 --- a/Sources/Common/CodingTree/Code.swift +++ b/Sources/Common/CodingTree/Code.swift @@ -5,6 +5,8 @@ import Foundation +typealias HuffmanCodes = (codes: [Code], maxBits: Int) + struct Code { /// Number of bits used for `code`. @@ -13,7 +15,7 @@ struct Code { 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]) -> (codes: [Code], maxBits: Int) { + static func huffmanCodes(from lengths: [CodeLength]) -> HuffmanCodes { // Sort `lengths` array to calculate canonical Huffman code. let sortedLengths = lengths.sorted() diff --git a/Sources/Common/CodingTree/DecodingTree.swift b/Sources/Common/CodingTree/DecodingTree.swift index 8149f3e9..65e1e615 100644 --- a/Sources/Common/CodingTree/DecodingTree.swift +++ b/Sources/Common/CodingTree/DecodingTree.swift @@ -13,14 +13,14 @@ final class DecodingTree { private let tree: [Int] private let leafCount: Int - init(codes: [Code], maxBits: Int, _ bitReader: BitReader) { + init(_ huffmanCodes: HuffmanCodes, _ bitReader: BitReader) { self.bitReader = bitReader // Calculate maximum amount of leaves in a tree. - self.leafCount = 1 << (maxBits + 1) + self.leafCount = 1 << (huffmanCodes.maxBits + 1) var tree = Array(repeating: -1, count: leafCount) - for code in codes { + for code in huffmanCodes.codes { // Put code in its place in the tree. var treeCode = code.code var index = 0 diff --git a/Sources/Deflate/Deflate+Compress.swift b/Sources/Deflate/Deflate+Compress.swift index 1bb39043..b665d115 100644 --- a/Sources/Deflate/Deflate+Compress.swift +++ b/Sources/Deflate/Deflate+Compress.swift @@ -106,9 +106,9 @@ extension Deflate: CompressionAlgorithm { // Constructing Huffman trees for the case of block with preset alphabets. // In this case codes for literals and distances are fixed. /// Huffman tree for literal and length symbols/codes. - let mainLiterals = EncodingTree(codes: Constants.staticHuffmanLiteralCodes, bitWriter, reverseCodes: true) + let mainLiterals = EncodingTree(codes: Constants.staticHuffmanLiteralCodes.codes, bitWriter, reverseCodes: true) /// Huffman tree for backward distance symbols/codes. - let mainDistances = EncodingTree(codes: Constants.staticHuffmanDistanceCodes, bitWriter, reverseCodes: true) + let mainDistances = EncodingTree(codes: Constants.staticHuffmanDistanceCodes.codes, bitWriter, reverseCodes: true) for code in bldCodes { switch code { diff --git a/Sources/Deflate/Deflate+Constants.swift b/Sources/Deflate/Deflate+Constants.swift index ca7a2cea..eb30d3f3 100644 --- a/Sources/Deflate/Deflate+Constants.swift +++ b/Sources/Deflate/Deflate+Constants.swift @@ -10,8 +10,8 @@ extension Deflate { struct Constants { // Precomputed codes for the static Huffman literal and distance trees. - static let staticHuffmanLiteralCodes = - [Code(bits: 7, code: 0, symbol: 256), Code(bits: 7, code: 64, symbol: 257), + static let staticHuffmanLiteralCodes: HuffmanCodes = + ([Code(bits: 7, code: 0, symbol: 256), Code(bits: 7, code: 64, symbol: 257), Code(bits: 7, code: 32, symbol: 258), Code(bits: 7, code: 96, symbol: 259), Code(bits: 7, code: 16, symbol: 260), Code(bits: 7, code: 80, symbol: 261), Code(bits: 7, code: 48, symbol: 262), Code(bits: 7, code: 112, symbol: 263), @@ -154,10 +154,10 @@ extension Deflate { Code(bits: 9, code: 63, symbol: 248), Code(bits: 9, code: 319, symbol: 249), Code(bits: 9, code: 191, symbol: 250), Code(bits: 9, code: 447, symbol: 251), Code(bits: 9, code: 127, symbol: 252), Code(bits: 9, code: 383, symbol: 253), - Code(bits: 9, code: 255, symbol: 254), Code(bits: 9, code: 511, symbol: 255)] + Code(bits: 9, code: 255, symbol: 254), Code(bits: 9, code: 511, symbol: 255)], 9) - static let staticHuffmanDistanceCodes = - [Code(bits: 5, code: 0, symbol: 0), Code(bits: 5, code: 16, symbol: 1), + static let staticHuffmanDistanceCodes: HuffmanCodes = + ([Code(bits: 5, code: 0, symbol: 0), Code(bits: 5, code: 16, symbol: 1), Code(bits: 5, code: 8, symbol: 2), Code(bits: 5, code: 24, symbol: 3), Code(bits: 5, code: 4, symbol: 4), Code(bits: 5, code: 20, symbol: 5), Code(bits: 5, code: 12, symbol: 6), Code(bits: 5, code: 28, symbol: 7), @@ -172,7 +172,7 @@ extension Deflate { Code(bits: 5, code: 3, symbol: 24), Code(bits: 5, code: 19, symbol: 25), Code(bits: 5, code: 11, symbol: 26), Code(bits: 5, code: 27, symbol: 27), Code(bits: 5, code: 7, symbol: 28), Code(bits: 5, code: 23, symbol: 29), - Code(bits: 5, code: 15, symbol: 30), Code(bits: 5, code: 31, symbol: 31)] + Code(bits: 5, code: 15, symbol: 30), Code(bits: 5, code: 31, symbol: 31)], 5) static let codeLengthOrders: [Int] = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15] diff --git a/Sources/Deflate/Deflate.swift b/Sources/Deflate/Deflate.swift index c49a3504..60c2970a 100644 --- a/Sources/Deflate/Deflate.swift +++ b/Sources/Deflate/Deflate.swift @@ -77,8 +77,8 @@ public class Deflate: DecompressionAlgorithm { if blockType == 1 { // Static Huffman // In this case codes for literals and distances are fixed. // Initialize trees from bootstraps. - mainLiterals = DecodingTree(codes: Constants.staticHuffmanLiteralCodes, maxBits: 9, bitReader) - mainDistances = DecodingTree(codes: Constants.staticHuffmanDistanceCodes, maxBits: 5, bitReader) + mainLiterals = DecodingTree(Constants.staticHuffmanLiteralCodes, bitReader) + mainDistances = DecodingTree(Constants.staticHuffmanDistanceCodes, bitReader) } else { // Dynamic Huffman // In this case there are Huffman codes for two alphabets in data right after block header. // Each code defined by a sequence of code lengths (which are compressed themselves with Huffman). @@ -102,8 +102,7 @@ public class Deflate: DecompressionAlgorithm { } let dynamicCodes = Code.huffmanCodes(from: Deflate.lengths(from: orderedCodeLengths)) /// Huffman tree for code lengths. Each code in the main alphabets is coded with this tree. - let dynamicCodeTree = DecodingTree(codes: dynamicCodes.codes, maxBits: dynamicCodes.maxBits, - bitReader) + let dynamicCodeTree = DecodingTree(dynamicCodes, bitReader) // Now we need to read codes (code lengths) for two main alphabets (trees). var codeLengths: [Int] = [] @@ -154,10 +153,10 @@ public class Deflate: DecompressionAlgorithm { // We have read codeLengths for both trees at once. // Now we need to split them and make corresponding trees. let literalCodes = Code.huffmanCodes(from: Deflate.lengths(from: Array(codeLengths[0.. Date: Wed, 18 Feb 2026 00:12:55 +0800 Subject: [PATCH 61/70] [Huffman] Remove codes argument label from EncodingTree.init --- Sources/BZip2/BZip2+Compress.swift | 4 ++-- Sources/Common/CodingTree/EncodingTree.swift | 2 +- Sources/Deflate/Deflate+Compress.swift | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/BZip2/BZip2+Compress.swift b/Sources/BZip2/BZip2+Compress.swift index 20d117bf..ba016be6 100644 --- a/Sources/BZip2/BZip2+Compress.swift +++ b/Sources/BZip2/BZip2+Compress.swift @@ -122,9 +122,9 @@ extension BZip2: CompressionAlgorithm { // Otherwise, let's create a new table and check if it gives us better results. // First, we calculate code lengths and codes for our current stats. let lengths = BZip2.lengths(from: stats) - let codes = Code.huffmanCodes(from: lengths) + let codes = Code.huffmanCodes(from: lengths).codes // Then, using these codes, we create a new Huffman tree. - let table = EncodingTree(codes: codes.codes, bitWriter) + let table = EncodingTree(codes, bitWriter) if table.bitSize(for: stats) < minimumSize { tables.append(table) tablesLengths.append(lengths.sorted { $0.symbol < $1.symbol }.map { $0.codeLength }) diff --git a/Sources/Common/CodingTree/EncodingTree.swift b/Sources/Common/CodingTree/EncodingTree.swift index 9c5125d4..a68e991b 100644 --- a/Sources/Common/CodingTree/EncodingTree.swift +++ b/Sources/Common/CodingTree/EncodingTree.swift @@ -19,7 +19,7 @@ final class EncodingTree { private let codingIndices: [CodingIndex] - init(codes: [Code], _ bitWriter: BitWriter, reverseCodes: Bool = false) { + init(_ codes: [Code], _ bitWriter: BitWriter, reverseCodes: Bool = false) { self.bitWriter = bitWriter var codingIndices = Array(repeating: CodingIndex(treeCode: -1, bitSize: -1), count: codes.count) diff --git a/Sources/Deflate/Deflate+Compress.swift b/Sources/Deflate/Deflate+Compress.swift index b665d115..e434ed57 100644 --- a/Sources/Deflate/Deflate+Compress.swift +++ b/Sources/Deflate/Deflate+Compress.swift @@ -106,9 +106,9 @@ extension Deflate: CompressionAlgorithm { // Constructing Huffman trees for the case of block with preset alphabets. // In this case codes for literals and distances are fixed. /// Huffman tree for literal and length symbols/codes. - let mainLiterals = EncodingTree(codes: Constants.staticHuffmanLiteralCodes.codes, bitWriter, reverseCodes: true) + let mainLiterals = EncodingTree(Constants.staticHuffmanLiteralCodes.codes, bitWriter, reverseCodes: true) /// Huffman tree for backward distance symbols/codes. - let mainDistances = EncodingTree(codes: Constants.staticHuffmanDistanceCodes.codes, bitWriter, reverseCodes: true) + let mainDistances = EncodingTree(Constants.staticHuffmanDistanceCodes.codes, bitWriter, reverseCodes: true) for code in bldCodes { switch code { From 273b6ed9e92929082090df8d57a9cbd339ed896d Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Wed, 18 Feb 2026 12:21:57 +0800 Subject: [PATCH 62/70] Remove redundant imports --- Sources/7-Zip/7zCoder+Equatable.swift | 2 -- Sources/7-Zip/7zCoder.swift | 1 - Sources/7-Zip/7zCoderInfo.swift | 1 - Sources/7-Zip/7zError.swift | 2 -- Sources/7-Zip/7zPackInfo.swift | 1 - Sources/7-Zip/7zProperty.swift | 1 - Sources/7-Zip/7zStreamInfo.swift | 1 - Sources/7-Zip/7zSubstreamInfo.swift | 1 - Sources/7-Zip/CompressionMethod+7z.swift | 2 -- Sources/7-Zip/MsbBitReader+7z.swift | 1 - Sources/BZip2/BZip2+BlockSize.swift | 2 -- Sources/BZip2/BZip2+Lengths.swift | 2 -- Sources/BZip2/BurrowsWheeler.swift | 2 -- Sources/BZip2/SuffixArray.swift | 2 -- Sources/Common/CodingTree/Code.swift | 2 -- Sources/Common/CodingTree/CodeLength.swift | 2 -- Sources/Common/CodingTree/DecodingTree.swift | 1 - Sources/Common/CodingTree/EncodingTree.swift | 1 - Sources/Common/CompressionMethod.swift | 2 -- Sources/Common/FileSystemType.swift | 2 -- Sources/Deflate/Deflate+Constants.swift | 2 -- Sources/Deflate/Deflate+Lengths.swift | 2 -- Sources/Deflate/DeflateError.swift | 2 -- Sources/GZip/FileSystemType+Gzip.swift | 2 -- Sources/GZip/GzipError.swift | 2 -- Sources/LZ4/LZ4+Compress.swift | 1 - Sources/LZMA/LZMABitTreeDecoder.swift | 2 -- Sources/LZMA/LZMAConstants.swift | 2 -- Sources/LZMA/LZMADecoder.swift | 1 - Sources/LZMA/LZMAError.swift | 2 -- Sources/LZMA/LZMALenDecoder.swift | 2 -- Sources/LZMA2/LZMA2Decoder.swift | 1 - Sources/LZMA2/LZMA2Error.swift | 2 -- Sources/TAR/ContainerEntryType+Tar.swift | 2 -- Sources/TAR/LittleEndianByteReader+Tar.swift | 1 - Sources/TAR/TarContainer.swift | 1 - Sources/TAR/TarError.swift | 2 -- Sources/XZ/LittleEndianByteReader+XZ.swift | 1 - Sources/XZ/XZStreamHeader.swift | 1 - Sources/ZIP/CompressionMethod+Zip.swift | 2 -- Sources/ZIP/FileSystemType+Zip.swift | 2 -- Sources/ZIP/ZipCentralDirectoryEntry.swift | 1 - Sources/ZIP/ZipEndOfCentralDirectory.swift | 1 - Sources/ZIP/ZipEntryInfoHelper.swift | 1 - Sources/ZIP/ZipError.swift | 2 -- Sources/ZIP/ZipLocalHeader.swift | 1 - 46 files changed, 72 deletions(-) diff --git a/Sources/7-Zip/7zCoder+Equatable.swift b/Sources/7-Zip/7zCoder+Equatable.swift index 96ed9f68..619dd0f0 100644 --- a/Sources/7-Zip/7zCoder+Equatable.swift +++ b/Sources/7-Zip/7zCoder+Equatable.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - extension SevenZipCoder: Equatable { static func == (lhs: SevenZipCoder, rhs: SevenZipCoder) -> Bool { diff --git a/Sources/7-Zip/7zCoder.swift b/Sources/7-Zip/7zCoder.swift index fc41c480..483eb068 100644 --- a/Sources/7-Zip/7zCoder.swift +++ b/Sources/7-Zip/7zCoder.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import BitByteData class SevenZipCoder { diff --git a/Sources/7-Zip/7zCoderInfo.swift b/Sources/7-Zip/7zCoderInfo.swift index 98ad29c9..d160080a 100644 --- a/Sources/7-Zip/7zCoderInfo.swift +++ b/Sources/7-Zip/7zCoderInfo.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import BitByteData class SevenZipCoderInfo { diff --git a/Sources/7-Zip/7zError.swift b/Sources/7-Zip/7zError.swift index 4b3492a0..29fce7b8 100644 --- a/Sources/7-Zip/7zError.swift +++ b/Sources/7-Zip/7zError.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - /** Represents an error which happened while processing a 7-Zip container. It may indicate that either container is damaged or it might not be 7-Zip container at all. diff --git a/Sources/7-Zip/7zPackInfo.swift b/Sources/7-Zip/7zPackInfo.swift index fe8bb595..6ccd01b5 100644 --- a/Sources/7-Zip/7zPackInfo.swift +++ b/Sources/7-Zip/7zPackInfo.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import BitByteData class SevenZipPackInfo { diff --git a/Sources/7-Zip/7zProperty.swift b/Sources/7-Zip/7zProperty.swift index e5751078..c26aa9dc 100644 --- a/Sources/7-Zip/7zProperty.swift +++ b/Sources/7-Zip/7zProperty.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import BitByteData struct SevenZipProperty { diff --git a/Sources/7-Zip/7zStreamInfo.swift b/Sources/7-Zip/7zStreamInfo.swift index b7e82a19..a978efd5 100644 --- a/Sources/7-Zip/7zStreamInfo.swift +++ b/Sources/7-Zip/7zStreamInfo.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import BitByteData class SevenZipStreamInfo { diff --git a/Sources/7-Zip/7zSubstreamInfo.swift b/Sources/7-Zip/7zSubstreamInfo.swift index 442ea2b0..eed68239 100644 --- a/Sources/7-Zip/7zSubstreamInfo.swift +++ b/Sources/7-Zip/7zSubstreamInfo.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import BitByteData class SevenZipSubstreamInfo { diff --git a/Sources/7-Zip/CompressionMethod+7z.swift b/Sources/7-Zip/CompressionMethod+7z.swift index f5168bc9..aeff7fa0 100644 --- a/Sources/7-Zip/CompressionMethod+7z.swift +++ b/Sources/7-Zip/CompressionMethod+7z.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - extension CompressionMethod { init(_ coderID: [UInt8]) { diff --git a/Sources/7-Zip/MsbBitReader+7z.swift b/Sources/7-Zip/MsbBitReader+7z.swift index 70044388..2a22679e 100644 --- a/Sources/7-Zip/MsbBitReader+7z.swift +++ b/Sources/7-Zip/MsbBitReader+7z.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import BitByteData extension MsbBitReader { diff --git a/Sources/BZip2/BZip2+BlockSize.swift b/Sources/BZip2/BZip2+BlockSize.swift index 215edbc0..a477bbef 100644 --- a/Sources/BZip2/BZip2+BlockSize.swift +++ b/Sources/BZip2/BZip2+BlockSize.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - extension BZip2 { /// Represents the size of the blocks in which data is split during BZip2 compression. diff --git a/Sources/BZip2/BZip2+Lengths.swift b/Sources/BZip2/BZip2+Lengths.swift index 799507f8..4d52359e 100644 --- a/Sources/BZip2/BZip2+Lengths.swift +++ b/Sources/BZip2/BZip2+Lengths.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - // BZip2 specific function for generation of HuffmanLength array from stats. extension BZip2 { diff --git a/Sources/BZip2/BurrowsWheeler.swift b/Sources/BZip2/BurrowsWheeler.swift index 80cae086..a3029e4b 100644 --- a/Sources/BZip2/BurrowsWheeler.swift +++ b/Sources/BZip2/BurrowsWheeler.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - enum BurrowsWheeler { static func transform(bytes: [Int]) -> ([Int], Int) { diff --git a/Sources/BZip2/SuffixArray.swift b/Sources/BZip2/SuffixArray.swift index 6c7018d1..f1d22647 100644 --- a/Sources/BZip2/SuffixArray.swift +++ b/Sources/BZip2/SuffixArray.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - /// Suffix Array by Induced Sorting enum SuffixArray { diff --git a/Sources/Common/CodingTree/Code.swift b/Sources/Common/CodingTree/Code.swift index f580cf9b..adaad351 100644 --- a/Sources/Common/CodingTree/Code.swift +++ b/Sources/Common/CodingTree/Code.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - typealias HuffmanCodes = (codes: [Code], maxBits: Int) struct Code { diff --git a/Sources/Common/CodingTree/CodeLength.swift b/Sources/Common/CodingTree/CodeLength.swift index 21bf5ff6..348c3b23 100644 --- a/Sources/Common/CodingTree/CodeLength.swift +++ b/Sources/Common/CodingTree/CodeLength.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - struct CodeLength: Equatable { let symbol: Int diff --git a/Sources/Common/CodingTree/DecodingTree.swift b/Sources/Common/CodingTree/DecodingTree.swift index 65e1e615..0f60ef09 100644 --- a/Sources/Common/CodingTree/DecodingTree.swift +++ b/Sources/Common/CodingTree/DecodingTree.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import BitByteData final class DecodingTree { diff --git a/Sources/Common/CodingTree/EncodingTree.swift b/Sources/Common/CodingTree/EncodingTree.swift index a68e991b..ca53a27d 100644 --- a/Sources/Common/CodingTree/EncodingTree.swift +++ b/Sources/Common/CodingTree/EncodingTree.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import BitByteData fileprivate struct CodingIndex { diff --git a/Sources/Common/CompressionMethod.swift b/Sources/Common/CompressionMethod.swift index cede6f79..10127da9 100644 --- a/Sources/Common/CompressionMethod.swift +++ b/Sources/Common/CompressionMethod.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - /// Represents a (de)compression method. public enum CompressionMethod { /// BZip2. diff --git a/Sources/Common/FileSystemType.swift b/Sources/Common/FileSystemType.swift index 7763f5ce..de6de79c 100644 --- a/Sources/Common/FileSystemType.swift +++ b/Sources/Common/FileSystemType.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - /** Represents the type of the file system on which an archive or container was created. File system determines the meaning of file attributes. diff --git a/Sources/Deflate/Deflate+Constants.swift b/Sources/Deflate/Deflate+Constants.swift index eb30d3f3..4d0176ee 100644 --- a/Sources/Deflate/Deflate+Constants.swift +++ b/Sources/Deflate/Deflate+Constants.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - extension Deflate { struct Constants { diff --git a/Sources/Deflate/Deflate+Lengths.swift b/Sources/Deflate/Deflate+Lengths.swift index 815d7f5d..dd2425f3 100644 --- a/Sources/Deflate/Deflate+Lengths.swift +++ b/Sources/Deflate/Deflate+Lengths.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - // Deflate specific functions for generation of HuffmanLength arrays from different inputs. extension Deflate { diff --git a/Sources/Deflate/DeflateError.swift b/Sources/Deflate/DeflateError.swift index 439e39aa..aa47df8a 100644 --- a/Sources/Deflate/DeflateError.swift +++ b/Sources/Deflate/DeflateError.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - /** Represents an error which happened during Deflate compression or decompression. It may indicate that either the data is damaged or it might not be compressed with Deflate at all. diff --git a/Sources/GZip/FileSystemType+Gzip.swift b/Sources/GZip/FileSystemType+Gzip.swift index d9a98280..4b8d8048 100644 --- a/Sources/GZip/FileSystemType+Gzip.swift +++ b/Sources/GZip/FileSystemType+Gzip.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - extension FileSystemType { init(_ gzipOS: UInt8) { diff --git a/Sources/GZip/GzipError.swift b/Sources/GZip/GzipError.swift index 2622dc4f..ab4fb129 100644 --- a/Sources/GZip/GzipError.swift +++ b/Sources/GZip/GzipError.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - /** Represents an error which happened while processing a GZip archive. It may indicate that either archive is damaged or it might not be GZip archive at all. diff --git a/Sources/LZ4/LZ4+Compress.swift b/Sources/LZ4/LZ4+Compress.swift index b0edcb4e..5ddd4ed0 100644 --- a/Sources/LZ4/LZ4+Compress.swift +++ b/Sources/LZ4/LZ4+Compress.swift @@ -4,7 +4,6 @@ // See LICENSE for license information import Foundation -import BitByteData extension LZ4: CompressionAlgorithm { diff --git a/Sources/LZMA/LZMABitTreeDecoder.swift b/Sources/LZMA/LZMABitTreeDecoder.swift index 65ac172a..5089dad1 100644 --- a/Sources/LZMA/LZMABitTreeDecoder.swift +++ b/Sources/LZMA/LZMABitTreeDecoder.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - /// Used to decode symbols that need several bits for storing. struct LZMABitTreeDecoder { diff --git a/Sources/LZMA/LZMAConstants.swift b/Sources/LZMA/LZMAConstants.swift index e686c5a4..770219c3 100644 --- a/Sources/LZMA/LZMAConstants.swift +++ b/Sources/LZMA/LZMAConstants.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - enum LZMAConstants { static let topValue: UInt32 = 1 << 24 diff --git a/Sources/LZMA/LZMADecoder.swift b/Sources/LZMA/LZMADecoder.swift index 095c1e14..fa8fac9b 100644 --- a/Sources/LZMA/LZMADecoder.swift +++ b/Sources/LZMA/LZMADecoder.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import BitByteData struct LZMADecoder { diff --git a/Sources/LZMA/LZMAError.swift b/Sources/LZMA/LZMAError.swift index 10175b84..6e40c5cf 100644 --- a/Sources/LZMA/LZMAError.swift +++ b/Sources/LZMA/LZMAError.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - /** Represents an error which happened during LZMA decompression. It may indicate that either data is damaged or it might not be compressed with LZMA at all. diff --git a/Sources/LZMA/LZMALenDecoder.swift b/Sources/LZMA/LZMALenDecoder.swift index 6c79a6ae..0ba1ec47 100644 --- a/Sources/LZMA/LZMALenDecoder.swift +++ b/Sources/LZMA/LZMALenDecoder.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - struct LZMALenDecoder { private var choice: Int = LZMAConstants.probInitValue diff --git a/Sources/LZMA2/LZMA2Decoder.swift b/Sources/LZMA2/LZMA2Decoder.swift index 416271c8..e6b08e78 100644 --- a/Sources/LZMA2/LZMA2Decoder.swift +++ b/Sources/LZMA2/LZMA2Decoder.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import BitByteData struct LZMA2Decoder { diff --git a/Sources/LZMA2/LZMA2Error.swift b/Sources/LZMA2/LZMA2Error.swift index af0c3472..03fd9bf3 100644 --- a/Sources/LZMA2/LZMA2Error.swift +++ b/Sources/LZMA2/LZMA2Error.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - /** Represents an error which happened during LZMA2 decompression. It may indicate that either data is damaged or it might not be compressed with LZMA2 at all. diff --git a/Sources/TAR/ContainerEntryType+Tar.swift b/Sources/TAR/ContainerEntryType+Tar.swift index 3d1e5b56..788c13ce 100644 --- a/Sources/TAR/ContainerEntryType+Tar.swift +++ b/Sources/TAR/ContainerEntryType+Tar.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - extension ContainerEntryType { init(_ fileTypeIndicator: UInt8) { diff --git a/Sources/TAR/LittleEndianByteReader+Tar.swift b/Sources/TAR/LittleEndianByteReader+Tar.swift index 4ef2a45a..7b95959c 100644 --- a/Sources/TAR/LittleEndianByteReader+Tar.swift +++ b/Sources/TAR/LittleEndianByteReader+Tar.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import BitByteData extension LittleEndianByteReader { diff --git a/Sources/TAR/TarContainer.swift b/Sources/TAR/TarContainer.swift index dc9ddaeb..61fcc593 100644 --- a/Sources/TAR/TarContainer.swift +++ b/Sources/TAR/TarContainer.swift @@ -4,7 +4,6 @@ // See LICENSE for license information import Foundation -import BitByteData /// Provides functions for work with TAR containers. public class TarContainer: Container { diff --git a/Sources/TAR/TarError.swift b/Sources/TAR/TarError.swift index 0dcce194..271d0afb 100644 --- a/Sources/TAR/TarError.swift +++ b/Sources/TAR/TarError.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - /** Represents an error which happened while processing a TAR container. It may indicate that either container is damaged or it might not be TAR container at all. diff --git a/Sources/XZ/LittleEndianByteReader+XZ.swift b/Sources/XZ/LittleEndianByteReader+XZ.swift index 94bd8890..5c135756 100644 --- a/Sources/XZ/LittleEndianByteReader+XZ.swift +++ b/Sources/XZ/LittleEndianByteReader+XZ.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import BitByteData extension LittleEndianByteReader { diff --git a/Sources/XZ/XZStreamHeader.swift b/Sources/XZ/XZStreamHeader.swift index 333f78f7..32d8ca3b 100644 --- a/Sources/XZ/XZStreamHeader.swift +++ b/Sources/XZ/XZStreamHeader.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import BitByteData struct XZStreamHeader { diff --git a/Sources/ZIP/CompressionMethod+Zip.swift b/Sources/ZIP/CompressionMethod+Zip.swift index c467859e..685a5477 100644 --- a/Sources/ZIP/CompressionMethod+Zip.swift +++ b/Sources/ZIP/CompressionMethod+Zip.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - extension CompressionMethod { init(_ compression: UInt16) { diff --git a/Sources/ZIP/FileSystemType+Zip.swift b/Sources/ZIP/FileSystemType+Zip.swift index 3ecb572f..62caadb7 100644 --- a/Sources/ZIP/FileSystemType+Zip.swift +++ b/Sources/ZIP/FileSystemType+Zip.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - extension FileSystemType { init(_ versionMadeBy: UInt16) { diff --git a/Sources/ZIP/ZipCentralDirectoryEntry.swift b/Sources/ZIP/ZipCentralDirectoryEntry.swift index b6ff0bd8..4333a038 100644 --- a/Sources/ZIP/ZipCentralDirectoryEntry.swift +++ b/Sources/ZIP/ZipCentralDirectoryEntry.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import BitByteData struct ZipCentralDirectoryEntry { diff --git a/Sources/ZIP/ZipEndOfCentralDirectory.swift b/Sources/ZIP/ZipEndOfCentralDirectory.swift index 36da217e..f0d6efab 100644 --- a/Sources/ZIP/ZipEndOfCentralDirectory.swift +++ b/Sources/ZIP/ZipEndOfCentralDirectory.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import BitByteData struct ZipEndOfCentralDirectory { diff --git a/Sources/ZIP/ZipEntryInfoHelper.swift b/Sources/ZIP/ZipEntryInfoHelper.swift index 4ebf00ea..2e1ce4e1 100644 --- a/Sources/ZIP/ZipEntryInfoHelper.swift +++ b/Sources/ZIP/ZipEntryInfoHelper.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import BitByteData /** diff --git a/Sources/ZIP/ZipError.swift b/Sources/ZIP/ZipError.swift index b6610b2b..4d03b807 100644 --- a/Sources/ZIP/ZipError.swift +++ b/Sources/ZIP/ZipError.swift @@ -3,8 +3,6 @@ // // See LICENSE for license information -import Foundation - /** Represents an error which happened while processing a ZIP container. It may indicate that either container is damaged or it might not be ZIP container at all. diff --git a/Sources/ZIP/ZipLocalHeader.swift b/Sources/ZIP/ZipLocalHeader.swift index 37366f7d..8c420779 100644 --- a/Sources/ZIP/ZipLocalHeader.swift +++ b/Sources/ZIP/ZipLocalHeader.swift @@ -3,7 +3,6 @@ // // See LICENSE for license information -import Foundation import BitByteData struct ZipLocalHeader { From 35bbf0c24e41f75153c2fc6e9f9a5fd5a9444b5f Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Wed, 18 Feb 2026 14:25:43 +0800 Subject: [PATCH 63/70] [BZip2] Improve encoding performance of code lengths deltas --- Sources/BZip2/BZip2+Compress.swift | 28 +++++++++++++++++----------- Sources/Common/Extensions.swift | 7 ++++--- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/Sources/BZip2/BZip2+Compress.swift b/Sources/BZip2/BZip2+Compress.swift index ba016be6..9360eb96 100644 --- a/Sources/BZip2/BZip2+Compress.swift +++ b/Sources/BZip2/BZip2+Compress.swift @@ -190,19 +190,25 @@ extension BZip2: CompressionAlgorithm { // Delta bit lengths. for lengths in tablesLengths { // Starting length. - var currentLength = lengths[0] - bitWriter.write(number: currentLength, bitsCount: 5) + var lastLength = lengths[0] + bitWriter.write(number: lastLength, bitsCount: 5) for length in lengths { - while currentLength != length { - bitWriter.write(bit: 1) // Alter length. - if currentLength > length { - bitWriter.write(bit: 1) // Decrement length. - currentLength -= 1 - } else { - bitWriter.write(bit: 0) // Increment length. - currentLength += 1 - } + // In the worst case delta between two lengths is 19. This delta requires 19 * 2 = 38 bits to encode + // which fits into `UInt` (at least, on modern 64-bit systems), so we can use `write(unsignedNumber:)`. + if lastLength > length { + // Bits: 11 -> 11_11 -> 11_11_11 -> 11_11_11_11 -> ... + // Numbers: 3 -> 15 -> 63 -> 255 -> ... + // https://oeis.org/A024036 + let diff = lastLength - length + bitWriter.write(unsignedNumber: (1 << (2 * diff)) - 1, bitsCount: 2 * diff) + } else if lastLength < length { + // Bits: 10 -> 10_10 -> 10_10_10 -> 10_10_10_10 -> ... + // Numbers: 2 -> 10 -> 42 -> 170 -> ... + // https://oeis.org/A020988 + let diff = length - lastLength + bitWriter.write(unsignedNumber: ((1 << (2 * diff)) - 1) * 2 / 3 , bitsCount: 2 * diff) } + lastLength = length bitWriter.write(bit: 0) } } diff --git a/Sources/Common/Extensions.swift b/Sources/Common/Extensions.swift index ea8ba358..50493caf 100644 --- a/Sources/Common/Extensions.swift +++ b/Sources/Common/Extensions.swift @@ -32,10 +32,11 @@ extension Int { /// Returns an integer with reversed order of bits. func reversed(bits count: Int) -> Int { - var a = 1 << 0 - var b = 1 << (count - 1) + // Arithmetic operations amount: 3 + 8 * ceil(count / 2) + var a = 1 + var b = 1 << (count &- 1) var z = 0 - for i in Swift.stride(from: count - 1, to: -1, by: -2) { + for i in Swift.stride(from: count &- 1, to: -1, by: -2) { z |= (self >> i) & a z |= (self << i) & b a <<= 1 From 022d76aee126caf82df159bfc06321f5990dc666 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Wed, 18 Feb 2026 14:42:01 +0800 Subject: [PATCH 64/70] [BZip2] Use UInt8 arrays instead of Data --- Sources/BZip2/BZip2+Compress.swift | 22 +++++++++++----------- Sources/BZip2/BZip2.swift | 10 +++++----- Sources/Common/CheckSums.swift | 4 ++-- Sources/Common/Extensions.swift | 9 +++++++++ 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/Sources/BZip2/BZip2+Compress.swift b/Sources/BZip2/BZip2+Compress.swift index 9360eb96..8151b505 100644 --- a/Sources/BZip2/BZip2+Compress.swift +++ b/Sources/BZip2/BZip2+Compress.swift @@ -51,8 +51,8 @@ extension BZip2: CompressionAlgorithm { var totalCRC: UInt32 = 0 for i in stride(from: data.startIndex, to: data.endIndex, by: rawBlockSize) { - let blockData = data[i..> 31) totalCRC ^= blockCRC @@ -61,7 +61,7 @@ extension BZip2: CompressionAlgorithm { bitWriter.write(bits: blockMarker) // Block magic number. bitWriter.write(number: blockCRC.toInt(), bitsCount: 32) // Block crc32. - process(block: blockData, bitWriter) + process(block, bitWriter) } // EOS magic number. @@ -73,8 +73,8 @@ extension BZip2: CompressionAlgorithm { return bitWriter.data } - private static func process(block data: Data, _ bitWriter: MsbBitWriter) { - var out = initialRle(data) + private static func process(_ block: [UInt8], _ bitWriter: MsbBitWriter) { + var out = initialRle(block) var pointer = 0 (out, pointer) = BurrowsWheeler.transform(bytes: out) @@ -151,7 +151,7 @@ extension BZip2: CompressionAlgorithm { bitWriter.write(number: 0, bitsCount: 1) // "Randomized". bitWriter.write(number: pointer, bitsCount: 24) // Original pointer (from BWT). - var usedMap = Array(repeating: UInt8(0), count: 16) + var usedMap = Array(repeating: 0 as UInt8, count: 16) for usedByte in usedBytes { guard usedByte <= 255 else { fatalError("Incorrect used byte.") } @@ -233,16 +233,16 @@ extension BZip2: CompressionAlgorithm { } /// Initial Run Length Encoding. - private static func initialRle(_ data: Data) -> [Int] { + private static func initialRle(_ block: [UInt8]) -> [Int] { var out = [Int]() - var index = data.startIndex - while index < data.endIndex { + var index = block.startIndex + while index < block.endIndex { var runLength = 1 - while index + 1 < data.endIndex && data[index] == data[index + 1] && runLength < 255 { + while index + 1 < block.endIndex && block[index] == block[index + 1] && runLength < 255 { runLength += 1 index += 1 } - let byte = data[index].toInt() + let byte = block[index].toInt() for _ in 0..> 31) totalCRC ^= blockCRC32 @@ -69,7 +69,7 @@ public class BZip2: DecompressionAlgorithm { return out } - private static func decode(_ bitReader: MsbBitReader, _ blockSize: BlockSize) throws -> Data { + private static func decode(_ bitReader: MsbBitReader, _ blockSize: BlockSize) throws -> [UInt8] { let isRandomized = bitReader.bit() guard isRandomized == 0 else { throw BZip2Error.randomizedBlock } @@ -221,7 +221,7 @@ public class BZip2: DecompressionAlgorithm { } } - return Data(out) + return out } } diff --git a/Sources/Common/CheckSums.swift b/Sources/Common/CheckSums.swift index 047cbe7a..d61fdc98 100644 --- a/Sources/Common/CheckSums.swift +++ b/Sources/Common/CheckSums.swift @@ -27,9 +27,9 @@ enum CheckSums { return ~crc } - static func bzip2crc32(_ data: Data) -> UInt32 { + static func bzip2crc32(_ block: [UInt8]) -> UInt32 { var crc: UInt32 = 0xFFFFFFFF - for byte in data { + for byte in block { let index = UInt32(truncatingIfNeeded: byte) crc = (crc << 8) ^ CheckSums.bzip2CRC32table[((crc >> 24) ^ index).toInt()] } diff --git a/Sources/Common/Extensions.swift b/Sources/Common/Extensions.swift index 50493caf..28211283 100644 --- a/Sources/Common/Extensions.swift +++ b/Sources/Common/Extensions.swift @@ -5,6 +5,15 @@ import Foundation +extension Data { + + @inlinable @inline(__always) + func toByteArray() -> [UInt8] { + return self.withUnsafeBytes { $0.map { $0 } } + } + +} + extension UnsignedInteger { @inlinable @inline(__always) From 49d783e4df3b94117433dee49d29713318a150c8 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Wed, 18 Feb 2026 15:02:09 +0800 Subject: [PATCH 65/70] [BZip2] Use better versions of bit writer functions First, write(unsignedNumber:) is better than write(number:) when the argument is already UInt, since it does one fewer precondition check and does not do additional integer conversion. Secondly, using write(number:) to write one bit was very inefficient. --- Sources/BZip2/BZip2+Compress.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/BZip2/BZip2+Compress.swift b/Sources/BZip2/BZip2+Compress.swift index 8151b505..5c6dee1c 100644 --- a/Sources/BZip2/BZip2+Compress.swift +++ b/Sources/BZip2/BZip2+Compress.swift @@ -45,8 +45,8 @@ extension BZip2: CompressionAlgorithm { // In the worst case initial RLE causes expansion by a factor of 1.25, so 1000 / 1.25 = 800. let rawBlockSize = blockSize.sizeInKilobytes * 800 // BZip2 Header. - bitWriter.write(number: 0x425a, bitsCount: 16) // Magic number = 'BZ'. - bitWriter.write(number: 0x68, bitsCount: 8) // Version = 'h'. + bitWriter.write(unsignedNumber: 0x425a, bitsCount: 16) // Magic number = 'BZ'. + bitWriter.write(unsignedNumber: 0x68, bitsCount: 8) // Version = 'h'. bitWriter.write(number: blockSize.headerByte, bitsCount: 8) // Block size. var totalCRC: UInt32 = 0 @@ -148,7 +148,7 @@ extension BZip2: CompressionAlgorithm { // Now, we perform encoding itself. // But first, we need to finish block header. - bitWriter.write(number: 0, bitsCount: 1) // "Randomized". + bitWriter.write(bit: 0) // "Randomized". bitWriter.write(number: pointer, bitsCount: 24) // Original pointer (from BWT). var usedMap = Array(repeating: 0 as UInt8, count: 16) From 23186336e911e51f90044fe065f1298cef065c55 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Wed, 18 Feb 2026 16:04:49 +0800 Subject: [PATCH 66/70] [BZip2] Change a couple of guard/if-checks to assertions --- Sources/BZip2/BZip2+Compress.swift | 36 +++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/Sources/BZip2/BZip2+Compress.swift b/Sources/BZip2/BZip2+Compress.swift index 5c6dee1c..5de8c9e7 100644 --- a/Sources/BZip2/BZip2+Compress.swift +++ b/Sources/BZip2/BZip2+Compress.swift @@ -151,10 +151,16 @@ extension BZip2: CompressionAlgorithm { bitWriter.write(bit: 0) // "Randomized". bitWriter.write(number: pointer, bitsCount: 24) // Original pointer (from BWT). + // Encode which symbols (bytes) are used in the data. All possible 256 symbols (0...255) are split into "maps" + // of 16 consequent symbols. Each map is a sequence of 16 bits where a set bit indicates that a symbol is used. + // If a map would consist of only zero bits, then it is omitted. This is determined in the construction of `usedMap`. var usedMap = Array(repeating: 0 as UInt8, count: 16) for usedByte in usedBytes { - guard usedByte <= 255 - else { fatalError("Incorrect used byte.") } + // `usedBytes` is an output of the BW transform which does not change symbols used. The input of the BW + // transform is an output of initial RLE encoding. Since a run length value is 255 or less and the input + // data consists of normal bytes which also have values of 255 or less, `usedByte` cannot be larger than 255 + // by construction. + assert(usedByte <= 255, "Incorrect usedByte.") usedMap[usedByte / 16] = 1 } bitWriter.write(bits: usedMap) @@ -163,7 +169,7 @@ extension BZip2: CompressionAlgorithm { for i in 0..<16 { guard usedMap[i] == 1 else { continue } for j in 0..<16 { - if usedBytesIndex < usedBytes.count && i * 16 + j == usedBytes[usedBytesIndex] { + if usedBytesIndex < usedBytes.endIndex && i * 16 + j == usedBytes[usedBytesIndex] { bitWriter.write(bit: 1) usedBytesIndex += 1 } else { @@ -215,20 +221,20 @@ extension BZip2: CompressionAlgorithm { // Contents. var encoded = 0 - var selectorPointer = 0 - var t: EncodingTree? + var table = tables[selectors[selectors.startIndex]] + var selectorIndex = selectors.startIndex &+ 1 for symbol in out { - encoded -= 1 - if encoded <= 0 { - encoded = 50 - if selectorPointer == selectors.count { - fatalError("Incorrect selector.") - } else if selectorPointer < selectors.count { - t = tables[selectors[selectorPointer]] - selectorPointer += 1 - } + // New table is selected every 50 symbols. + if encoded >= 50 { + // Selectors were added every 50 symbols. So by construction `selectorIndex` can never exceed the + // amount of available selectors. + assert(selectorIndex < selectors.endIndex, "Incorrect selectorIndex.") + table = tables[selectors[selectorIndex]] + selectorIndex &+= 1 + encoded = 0 } - t?.code(symbol: symbol) + table.code(symbol: symbol) + encoded &+= 1 } } From 52669c1ec988a99bbd830fc8c25b7b8f8240f19b Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Wed, 18 Feb 2026 17:10:33 +0800 Subject: [PATCH 67/70] [LZMA] Remove LZMARangeDecoder.isCorrupted property According to LZMA specification this property is ignored by the reference implementation. In addition, specification suggests that other LZMA decoders should also ignore it. We weren't using this property for anything anyway, so its removal does not really change anything, except for better compliance with LZMA specification. In addition, range decoder initialization error is now thrown in accordance with reference implementation. --- Sources/LZMA/LZMARangeDecoder.swift | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Sources/LZMA/LZMARangeDecoder.swift b/Sources/LZMA/LZMARangeDecoder.swift index 91b896b8..26f583a9 100644 --- a/Sources/LZMA/LZMARangeDecoder.swift +++ b/Sources/LZMA/LZMARangeDecoder.swift @@ -12,7 +12,6 @@ struct LZMARangeDecoder { private var range = 0xFFFFFFFF as UInt32 private var code = 0 as UInt32 - private(set) var isCorrupted = false var isFinishedOK: Bool { return self.code == 0 @@ -27,7 +26,7 @@ struct LZMARangeDecoder { let byte = self.byteReader.byte() self.code = self.byteReader.uint32().byteSwapped - guard byte == 0 && self.code != self.range + guard byte == 0 else { throw LZMAError.rangeDecoderInitError } } @@ -53,10 +52,6 @@ struct LZMARangeDecoder { let t = 0 &- (self.code >> 31) self.code = self.code &+ (self.range & t) - if self.code == self.range { - self.isCorrupted = true - } - self.normalize() res <<= 1 From d10edc9e21e634b6e5693977a9499a2f678f40c1 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Thu, 19 Feb 2026 10:54:56 +0800 Subject: [PATCH 68/70] [swcomp] Remove empty warmupIteration function from comp-ratio benchmarks These functions weren't called anyway since 5e8f568f6486b0a27a71c0dc77b5ef7624f5e502 commit where Benchmark protocol requirement for this function acquired Double return type. In any event, skipping of warmup iteration is better controlled by the corresponding option of "benchmark run" command. --- Sources/swcomp/Benchmarks/Benchmarks.swift | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Sources/swcomp/Benchmarks/Benchmarks.swift b/Sources/swcomp/Benchmarks/Benchmarks.swift index 448ab359..502bfe06 100644 --- a/Sources/swcomp/Benchmarks/Benchmarks.swift +++ b/Sources/swcomp/Benchmarks/Benchmarks.swift @@ -270,8 +270,6 @@ struct CompRatioDeflate: Benchmark { } } - func warmupIteration() { } - func measure() -> Double { let outputData = Deflate.compress(data: self.data) guard outputData.count > 0 @@ -325,8 +323,6 @@ struct CompRatioBz2: Benchmark { } } - func warmupIteration() { } - func measure() -> Double { let outputData = BZip2.compress(data: self.data) guard outputData.count > 0 @@ -380,8 +376,6 @@ struct CompRatioLz4: Benchmark { } } - func warmupIteration() { } - func measure() -> Double { let outputData = LZ4.compress(data: self.data) guard outputData.count > 0 @@ -436,8 +430,6 @@ struct CompRatioLz4Bd: Benchmark { } } - func warmupIteration() { } - func measure() -> Double { let outputData = LZ4.compress(data: self.data, independentBlocks: false, blockChecksums: false, contentChecksum: true, contentSize: false, blockSize: 4 * 1024 * 1024, dictionary: nil, dictionaryID: nil) From 13d50975b59998e4da7b55e957cf446aea15f469 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Thu, 19 Feb 2026 15:29:49 +0800 Subject: [PATCH 69/70] [Deflate] Optimize creation of uncompressed blocks --- Sources/Deflate/Deflate+Compress.swift | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/Sources/Deflate/Deflate+Compress.swift b/Sources/Deflate/Deflate+Compress.swift index e434ed57..6b7e5e88 100644 --- a/Sources/Deflate/Deflate+Compress.swift +++ b/Sources/Deflate/Deflate+Compress.swift @@ -73,24 +73,13 @@ extension Deflate: CompressionAlgorithm { } private static func createUncompressedBlock(_ data: Data) -> Data { - let bitWriter = LsbBitWriter() - - // Write block header. + assert(data.count <= 65535, "Cannot create uncompressed Deflate blocks larger than 65535 bytes.") + // Write block header, data's length and n-length. It is more efficient to avoid using LsbBitWriter. // Note: Only one block is supported for now. - bitWriter.write(bit: 1) - bitWriter.write(bits: [0, 0]) - - // Before writing lengths we need to discard remaining bits in current byte. - bitWriter.align() - - // Write data's length. - bitWriter.write(number: data.count, bitsCount: 16) - // Write data's n-length. - bitWriter.write(number: data.count ^ (1 << 16 - 1), bitsCount: 16) - - var out = bitWriter.data + let nLength = data.count ^ ((1 << 16) - 1) + var out = Data([1, UInt8(truncatingIfNeeded: data.count & 0xFF), UInt8(truncatingIfNeeded: (data.count >> 8) & 0xFF), + UInt8(truncatingIfNeeded: nLength & 0xFF), UInt8(truncatingIfNeeded: (nLength >> 8) & 0xFF)]) out.append(data) - return out } From 16028afbee56b104ce7a20699330c186517827f2 Mon Sep 17 00:00:00 2001 From: meowlgm Date: Sat, 21 Feb 2026 19:34:58 +0800 Subject: [PATCH 70/70] Add AES-256 decryption support for encrypted 7z archives Implement AES-256-CBC decryption with SHA-256 key derivation, matching the official 7-Zip source (7zAes.cpp). Changes: - Add 7zAESDecryptor.swift: AES-256-CBC decryption using CommonCrypto, with properties parsing and key derivation matching 7-Zip's CalcKey() - Modify 7zFolder.unpack(): handle encryption coder with password - Modify 7zHeader.init: pass password through to folder.unpack() - Modify 7zContainer: add password parameter to info() and open(), maintaining backward compatibility with the Container protocol --- Sources/7-Zip/7zAESDecryptor.swift | 182 +++++++++++++++++++++++++++++ Sources/7-Zip/7zContainer.swift | 96 ++++++++------- Sources/7-Zip/7zFolder.swift | 66 ++++++++--- Sources/7-Zip/7zHeader.swift | 18 +-- 4 files changed, 297 insertions(+), 65 deletions(-) create mode 100644 Sources/7-Zip/7zAESDecryptor.swift diff --git a/Sources/7-Zip/7zAESDecryptor.swift b/Sources/7-Zip/7zAESDecryptor.swift new file mode 100644 index 00000000..b69c333f --- /dev/null +++ b/Sources/7-Zip/7zAESDecryptor.swift @@ -0,0 +1,182 @@ +// Copyright (c) 2026 Timofey Solomko +// Licensed under MIT License +// +// See LICENSE for license information + +import CommonCrypto +import Foundation + +/// AES-256-CBC decryptor for 7-Zip encrypted streams. +/// +/// 7-Zip uses coder ID `[0x06, 0xF1, 0x07, 0x01]` for AES-256 + SHA-256 encryption. +/// +/// Properties format (from 7-Zip source `7zAes.cpp` SetDecoderProperties2): +/// - Byte 0 (b0): +/// - bits 0-5: numCyclesPower (SHA-256 iterations = 2^numCyclesPower) +/// - bit 6: contributes +1 to ivSize +/// - bit 7: contributes +1 to saltSize +/// - Byte 1 (b1, present if b0 bits 6-7 != 0): +/// - upper nibble (b1 >> 4): adds to saltSize +/// - lower nibble (b1 & 0x0F): adds to ivSize +/// - saltSize = ((b0 >> 7) & 1) + (b1 >> 4) +/// - ivSize = ((b0 >> 6) & 1) + (b1 & 0x0F) +/// - Remaining bytes: salt (saltSize bytes) + IV (ivSize bytes) +/// +/// Key derivation (from CalcKey): +/// buf = salt + password_as_raw_bytes + 8_zero_bytes (counter placeholder) +/// For each round (0..<2^numCyclesPower): +/// write round counter as UInt32 LE at buf[saltSize+passwordSize..saltSize+passwordSize+4] +/// (upper 4 bytes of 8-byte counter remain zero since numRounds fits in UInt32) +/// SHA256.update(buf) +/// key = SHA256.finalize() +enum SevenZipAESDecryptor { + + /// Decrypt data using 7z AES-256-CBC with the given password and coder properties. + static func decrypt(data: Data, properties: [UInt8], password: String) throws -> Data { + guard !properties.isEmpty else { + throw SevenZipError.internalStructureError + } + + // Parse properties - matching 7-Zip's SetDecoderProperties2 + let b0 = properties[0] + let numCyclesPower = Int(b0 & 0x3F) + + var saltSize = 0 + var ivSize = 0 + + if (b0 & 0xC0) != 0 { + // Has salt/IV size info + guard properties.count > 1 else { + throw SevenZipError.internalStructureError + } + let b1 = properties[1] + saltSize = Int((b0 >> 7) & 1) + Int(b1 >> 4) + ivSize = Int((b0 >> 6) & 1) + Int(b1 & 0x0F) + + guard properties.count == 2 + saltSize + ivSize else { + throw SevenZipError.internalStructureError + } + } + + // Extract salt + var salt = Data() + if saltSize > 0 { + salt = Data(properties[2..<(2 + saltSize)]) + } + + // Extract IV (pad to 16 bytes with zeros) + var iv = Data(count: 16) + if ivSize > 0 { + let ivStart = 2 + saltSize + for i in 0.. Data + { + if numCyclesPower == 0x3F { + // Special case: direct key from salt + password (no hashing) + var key = Data(count: 32) + var pos = 0 + for i in 0..> 8) & 0xFF) + buf[counterOffset + 2] = UInt8((round >> 16) & 0xFF) + buf[counterOffset + 3] = UInt8((round >> 24) & 0xFF) + + buf.withUnsafeBytes { ptr in + _ = CC_SHA256_Update(&context, ptr.baseAddress, CC_LONG(bufSize)) + } + } + + var hash = Data(count: Int(CC_SHA256_DIGEST_LENGTH)) + hash.withUnsafeMutableBytes { ptr in + _ = CC_SHA256_Final(ptr.bindMemory(to: UInt8.self).baseAddress, &context) + } + + return hash + } + + /// AES-256-CBC decryption using CommonCrypto. + private static func aes256CBCDecrypt(data: Data, key: Data, iv: Data) throws -> Data { + let outputLength = data.count + kCCBlockSizeAES128 + var outputData = Data(count: outputLength) + var numBytesDecrypted: size_t = 0 + + let status = outputData.withUnsafeMutableBytes { outputPtr in + data.withUnsafeBytes { dataPtr in + key.withUnsafeBytes { keyPtr in + iv.withUnsafeBytes { ivPtr in + CCCrypt( + CCOperation(kCCDecrypt), + CCAlgorithm(kCCAlgorithmAES), + CCOptions(0), // No padding — 7z handles padding via data trimming + keyPtr.baseAddress, key.count, + ivPtr.baseAddress, + dataPtr.baseAddress, data.count, + outputPtr.baseAddress, outputLength, + &numBytesDecrypted + ) + } + } + } + } + + guard status == kCCSuccess else { + throw SevenZipError.internalStructureError + } + + outputData.count = numBytesDecrypted + return outputData + } +} diff --git a/Sources/7-Zip/7zContainer.swift b/Sources/7-Zip/7zContainer.swift index 685d58d5..e0f49fa1 100644 --- a/Sources/7-Zip/7zContainer.swift +++ b/Sources/7-Zip/7zContainer.swift @@ -3,8 +3,8 @@ // // See LICENSE for license information -import Foundation import BitByteData +import Foundation /// Provides functions for work with 7-Zip containers. public class SevenZipContainer: Container { @@ -13,23 +13,28 @@ public class SevenZipContainer: Container { /** Processes 7-Zip container and returns an array of `SevenZipEntry` with information and data for all entries. - + - Important: The order of entries is defined by 7-Zip container and, particularly, by the creator of a given 7-Zip container. It is likely that directories will be encountered earlier than files stored in those directories, but no particular order is guaranteed. - + - Parameter container: 7-Zip container's data. - + - Throws: `SevenZipError` or any other error associated with compression type depending on the type of the problem. It may indicate that either container is damaged or it might not be 7-Zip container at all. - + - Returns: Array of `SevenZipEntry`. */ public static func open(container data: Data) throws -> [SevenZipEntry] { + return try open(container: data, password: nil) + } + + /// Processes 7-Zip container with an optional password for encrypted archives. + public static func open(container data: Data, password: String?) throws -> [SevenZipEntry] { var entries = [SevenZipEntry]() - guard let header = try readHeader(data), + guard let header = try readHeader(data, password: password), let files = header.fileInfo?.files - else { return [] } + else { return [] } /// Total count of non-empty files. Used to iterate over SubstreamInfo. var nonEmptyFileIndex = 0 @@ -56,7 +61,9 @@ public class SevenZipContainer: Container { for file in files { if file.isEmptyStream { - let info = file.isEmptyFile && !file.isAntiFile ? SevenZipEntryInfo(file, 0) : SevenZipEntryInfo(file) + let info = + file.isEmptyFile && !file.isAntiFile + ? SevenZipEntryInfo(file, 0) : SevenZipEntryInfo(file) let data = file.isEmptyFile && !file.isAntiFile ? Data() : nil entries.append(SevenZipEntry(info, data)) continue @@ -65,7 +72,7 @@ public class SevenZipContainer: Container { // Without `SevenZipStreamInfo` and `SevenZipPackInfo` we cannot find file data in container. guard let streamInfo = header.mainStreams, let packInfo = streamInfo.packInfo - else { throw SevenZipError.internalStructureError } + else { throw SevenZipError.internalStructureError } // SubstreamInfo is required to get files' data, and without it we can only return files' info. guard let substreamInfo = streamInfo.substreamInfo else { @@ -75,7 +82,7 @@ public class SevenZipContainer: Container { // Check if there is enough folders. guard folderIndex < streamInfo.coderInfo.numFolders - else { throw SevenZipError.internalStructureError } + else { throw SevenZipError.internalStructureError } /// Folder which contains current file. let folder = streamInfo.coderInfo.folders[folderIndex] @@ -89,7 +96,7 @@ public class SevenZipContainer: Container { // enountered in the same order, as they are placed in the container. Thus, we have to start moving // to stream's offset from the beginning. // TODO: Is this correct or the order of streams is guaranteed? - byteReader.offset = signatureHeaderSize + packInfo.packPosition // Pack offset. + byteReader.offset = signatureHeaderSize + packInfo.packPosition // Pack offset. if streamIndex != 0 { for i in 0..= folder.numUnpackSubstreams { // If we read all files in folder... + if folderFileIndex >= folder.numUnpackSubstreams { // If we read all files in folder... // Check folder's unpacked size as well as its CRC32 (if it is available). guard folderUnpackSize == folder.unpackSize() - else { throw SevenZipError.wrongSize } + else { throw SevenZipError.wrongSize } if let storedFolderCRC = folder.crc { guard folderCRC == storedFolderCRC - else { throw SevenZipError.wrongCRC } + else { throw SevenZipError.wrongCRC } } // Reset folder's unpack size and CRC32. folderCRC = CheckSums.crc32(Data()) @@ -165,31 +174,38 @@ public class SevenZipContainer: Container { /** Processes 7-Zip container and returns an array of `SevenZipEntryInfo` with information about entries in this container. - + - Important: The order of entries is defined by 7-Zip container and, particularly, by the creator of a given 7-Zip container. It is likely that directories will be encountered earlier than files stored in those directories, but no particular order is guaranteed. - + - Parameter container: 7-Zip container's data. - + - Throws: `SevenZipError` or any other error associated with compression type depending on the type of the problem. It may indicate that either container is damaged or it might not be 7-Zip container at all. - + - Returns: Array of `SevenZipEntryInfo`. */ public static func info(container data: Data) throws -> [SevenZipEntryInfo] { + return try info(container: data, password: nil) + } + + /// Processes 7-Zip container info with an optional password for encrypted archives. + public static func info(container data: Data, password: String?) throws -> [SevenZipEntryInfo] { var entries = [SevenZipEntryInfo]() - guard let header = try readHeader(data), + guard let header = try readHeader(data, password: password), let files = header.fileInfo?.files - else { return [] } + else { return [] } var nonEmptyFileIndex = 0 for file in files { if !file.isEmptyStream, let substreamInfo = header.mainStreams?.substreamInfo { - let size = nonEmptyFileIndex < substreamInfo.unpackSizes.count ? - substreamInfo.unpackSizes[nonEmptyFileIndex] : nil - let crc = nonEmptyFileIndex < substreamInfo.digests.count ? - substreamInfo.digests[nonEmptyFileIndex] : nil + let size = + nonEmptyFileIndex < substreamInfo.unpackSizes.count + ? substreamInfo.unpackSizes[nonEmptyFileIndex] : nil + let crc = + nonEmptyFileIndex < substreamInfo.digests.count + ? substreamInfo.digests[nonEmptyFileIndex] : nil entries.append(SevenZipEntryInfo(file, size, crc)) nonEmptyFileIndex += 1 } else { @@ -201,10 +217,11 @@ public class SevenZipContainer: Container { return entries } - private static func readHeader(_ data: Data) throws -> SevenZipHeader? { + private static func readHeader(_ data: Data, password: String? = nil) throws -> SevenZipHeader? + { // Valid 7-Zip container must contain at least a SignatureHeader, which is 32 bytes long. guard data.count >= 32 - else { throw SevenZipError.wrongSignature } + else { throw SevenZipError.wrongSignature } let bitReader = MsbBitReader(data: data) @@ -212,13 +229,13 @@ public class SevenZipContainer: Container { // Check signature. guard bitReader.bytes(count: 6) == [0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C] - else { throw SevenZipError.wrongSignature } + else { throw SevenZipError.wrongSignature } // Check archive version. let majorVersion = bitReader.byte() let minorVersion = bitReader.byte() guard majorVersion == 0 && minorVersion > 0 && minorVersion <= 4 - else { throw SevenZipError.wrongFormatVersion } + else { throw SevenZipError.wrongFormatVersion } let startHeaderCRC = bitReader.uint32() @@ -229,12 +246,12 @@ public class SevenZipContainer: Container { bitReader.offset -= 20 guard CheckSums.crc32(bitReader.bytes(count: 20)) == startHeaderCRC - else { throw SevenZipError.wrongCRC } + else { throw SevenZipError.wrongCRC } // **Header** bitReader.offset += nextHeaderOffset if bitReader.isFinished { - return nil // In case of completely empty container. + return nil // In case of completely empty container. } let headerStartIndex = bitReader.offset @@ -245,7 +262,8 @@ public class SevenZipContainer: Container { if type == 0x17 { let packedHeaderStreamInfo = try SevenZipStreamInfo(bitReader) headerEndIndex = bitReader.offset - header = try SevenZipHeader(bitReader, using: packedHeaderStreamInfo) + header = try SevenZipHeader( + bitReader, using: packedHeaderStreamInfo, password: password) } else if type == 0x01 { header = try SevenZipHeader(bitReader) headerEndIndex = bitReader.offset @@ -255,12 +273,12 @@ public class SevenZipContainer: Container { // Check header size guard headerEndIndex - headerStartIndex == nextHeaderSize - else { throw SevenZipError.wrongSize } + else { throw SevenZipError.wrongSize } // Check header CRC bitReader.offset = headerStartIndex guard CheckSums.crc32(bitReader.bytes(count: nextHeaderSize)) == nextHeaderCRC - else { throw SevenZipError.wrongCRC } + else { throw SevenZipError.wrongCRC } return header } diff --git a/Sources/7-Zip/7zFolder.swift b/Sources/7-Zip/7zFolder.swift index edcfdd7c..64e176c1 100644 --- a/Sources/7-Zip/7zFolder.swift +++ b/Sources/7-Zip/7zFolder.swift @@ -3,8 +3,8 @@ // // See LICENSE for license information -import Foundation import BitByteData +import Foundation class SevenZipFolder { @@ -53,7 +53,7 @@ class SevenZipFolder { } guard totalOutputStreams != 0 - else { throw SevenZipError.internalStructureError } + else { throw SevenZipError.internalStructureError } numBindPairs = totalOutputStreams - 1 if numBindPairs > 0 { @@ -63,7 +63,7 @@ class SevenZipFolder { } guard totalInputStreams >= numBindPairs - else { throw SevenZipError.internalStructureError } + else { throw SevenZipError.internalStructureError } numPackedStreams = totalInputStreams - numBindPairs if numPackedStreams == 1 { @@ -135,11 +135,11 @@ class SevenZipFolder { return 0 } - func unpack(data: Data) throws -> Data { + func unpack(data: Data, password: String? = nil) throws -> Data { var decodedData = data for coder in self.orderedCoders() { guard coder.numInStreams == 1 || coder.numOutStreams == 1 - else { throw SevenZipError.multiStreamNotSupported } + else { throw SevenZipError.multiStreamNotSupported } let unpackSize = self.unpackSize(for: coder) @@ -147,48 +147,78 @@ class SevenZipFolder { case .copy: continue case .deflate: - decodedData = try Deflate.decompress(data: decodedData) + #if (!SWCOMPRESSION_POD_SEVENZIP) || (SWCOMPRESSION_POD_SEVENZIP && SWCOMPRESSION_POD_DEFLATE) + decodedData = try Deflate.decompress(data: decodedData) + #else + throw SevenZipError.compressionNotSupported + #endif case .bzip2: - decodedData = try BZip2.decompress(data: decodedData) + #if (!SWCOMPRESSION_POD_SEVENZIP) || (SWCOMPRESSION_POD_SEVENZIP && SWCOMPRESSION_POD_BZ2) + decodedData = try BZip2.decompress(data: decodedData) + #else + throw SevenZipError.compressionNotSupported + #endif case .lzma2: // Dictionary size is stored in coder's properties. guard let properties = coder.properties, properties.count == 1 - else { throw LZMA2Error.wrongDictionarySize } + else { throw LZMA2Error.wrongDictionarySize } - decodedData = try LZMA2.decompress(LittleEndianByteReader(data: decodedData), properties[0]) + decodedData = try LZMA2.decompress( + LittleEndianByteReader(data: decodedData), properties[0]) case .lzma: // Both properties' byte (lp, lc, pb) and dictionary size are stored in coder's properties. guard let properties = coder.properties, properties.count == 5 - else { throw LZMAError.wrongProperties } + else { throw LZMAError.wrongProperties } var dictionarySize = 0 for i in 1..<4 { dictionarySize |= properties[i].toInt() << (8 * (i - 1)) } let lzmaProperties = try LZMAProperties(lzmaByte: properties[0], dictionarySize) - decodedData = try LZMA.decompress(data: decodedData, - properties: lzmaProperties, - uncompressedSize: unpackSize) + decodedData = try LZMA.decompress( + data: decodedData, + properties: lzmaProperties, + uncompressedSize: unpackSize) default: if coder.id == [0x03] { guard let properties = coder.properties, properties.count == 1 - else { throw SevenZipError.internalStructureError } + else { throw SevenZipError.internalStructureError } - decodedData = DeltaFilter.decode(LittleEndianByteReader(data: decodedData), (properties[0] &+ 1).toInt()) + decodedData = DeltaFilter.decode( + LittleEndianByteReader(data: decodedData), (properties[0] &+ 1).toInt()) } else if coder.id == [0x04, 0xF7, 0x11, 0x04] { - decodedData = try LZ4.decompress(data: decodedData) + #if (!SWCOMPRESSION_POD_SEVENZIP) || (SWCOMPRESSION_POD_SEVENZIP && SWCOMPRESSION_POD_LZ4) + decodedData = try LZ4.decompress(data: decodedData) + #else + throw SevenZipError.compressionNotSupported + #endif } else if coder.isEncryptionMethod { - throw SevenZipError.encryptionNotSupported + guard let password = password, !password.isEmpty else { + throw SevenZipError.encryptionNotSupported + } + guard let properties = coder.properties else { + throw SevenZipError.internalStructureError + } + decodedData = try SevenZipAESDecryptor.decrypt( + data: decodedData, + properties: properties, + password: password + ) + // AES-CBC output may have padding; trim to expected unpack size. + if decodedData.count > unpackSize { + decodedData = decodedData.prefix(unpackSize) + } + continue // Skip the size check below since we already trimmed. } else { throw SevenZipError.compressionNotSupported } } guard decodedData.count == unpackSize - else { throw SevenZipError.wrongSize } + else { throw SevenZipError.wrongSize } } return decodedData } diff --git a/Sources/7-Zip/7zHeader.swift b/Sources/7-Zip/7zHeader.swift index 301e0e78..587c2408 100644 --- a/Sources/7-Zip/7zHeader.swift +++ b/Sources/7-Zip/7zHeader.swift @@ -3,8 +3,8 @@ // // See LICENSE for license information -import Foundation import BitByteData +import Foundation class SevenZipHeader { @@ -36,31 +36,33 @@ class SevenZipHeader { } guard type == 0x00 - else { throw SevenZipError.internalStructureError } + else { throw SevenZipError.internalStructureError } } - convenience init(_ bitReader: MsbBitReader, using streamInfo: SevenZipStreamInfo) throws { + convenience init( + _ bitReader: MsbBitReader, using streamInfo: SevenZipStreamInfo, password: String? = nil + ) throws { let folder = streamInfo.coderInfo.folders[0] guard let packInfo = streamInfo.packInfo - else { throw SevenZipError.internalStructureError } + else { throw SevenZipError.internalStructureError } let folderOffset = SevenZipContainer.signatureHeaderSize + packInfo.packPosition bitReader.offset = folderOffset let packedHeaderData = Data(bitReader.bytes(count: packInfo.packSizes[0])) - let headerData = try folder.unpack(data: packedHeaderData) + let headerData = try folder.unpack(data: packedHeaderData, password: password) guard headerData.count == folder.unpackSize() - else { throw SevenZipError.wrongSize } + else { throw SevenZipError.wrongSize } if let crc = folder.crc { guard CheckSums.crc32(headerData) == crc - else { throw SevenZipError.wrongCRC } + else { throw SevenZipError.wrongCRC } } let headerBitReader = MsbBitReader(data: headerData) guard headerBitReader.byte() == 0x01 - else { throw SevenZipError.internalStructureError } + else { throw SevenZipError.internalStructureError } try self.init(headerBitReader) }