diff --git a/.jazzy.yaml b/.jazzy.yaml index 9e1d8f58..74b84506 100644 --- a/.jazzy.yaml +++ b/.jazzy.yaml @@ -3,11 +3,11 @@ sourcekitten_sourcefile: docs.json clean: false author: Timofey Solomko module: SWCompression -module_version: 4.6.0 +module_version: 4.6.1 copyright: '© 2021 Timofey Solomko' readme: README.md github_url: https://github.com/tsolomko/SWCompression -github_file_prefix: https://github.com/tsolomko/SWCompression/tree/4.6.0 +github_file_prefix: https://github.com/tsolomko/SWCompression/tree/4.6.1 theme: fullwidth custom_categories: diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c82083ca..00000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -# TODO: There is probably a way to simplify this very explicit build matrix. -git: - depth: 1 - submodules: false -jobs: - include: - - stage: test - language: swift - os: osx - osx_image: xcode12.5 # Swift 5.4 - env: - - HOMEBREW_NO_INSTALL_CLEANUP=1 - - WATCHOS_ACTIONS='clean test' - - WATCHOS_SIMULATOR='Apple Watch Series 6 - 44mm' - before_install: - - brew update - install: - - ./utils.py ci install-macos - before_script: - - ./utils.py prepare-workspace macos --xcf - script: - - ./utils.py ci script-macos diff --git a/CHANGELOG.md b/CHANGELOG.md index 85bb1312..fb937343 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 4.6.1 + +- Fixed a crash in LZMA2 when trying to process an empty `Data`. +- swcomp changes: + - Added `benchmark create-tar` benchmarking command. + - Benchmarking results are now printed in the units of speed instead of time elapsed. + ## 4.6.0 - Swift 4.2 is no longer supported. diff --git a/README.md b/README.md index d59f3905..7f81eea8 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ [![Swift 5.X](https://img.shields.io/badge/Swift-5.X-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://travis-ci.com/tsolomko/SWCompression.svg?branch=develop)](https://travis-ci.com/tsolomko/SWCompression) [![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) A framework with (de)compression algorithms and functions for working with various archives and containers. diff --git a/SWCompression.podspec b/SWCompression.podspec index 428d48fe..c87570c7 100644 --- a/SWCompression.podspec +++ b/SWCompression.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "SWCompression" - s.version = "4.6.0" + s.version = "4.6.1" 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." diff --git a/SWCompression.xcodeproj/SWCompression.plist b/SWCompression.xcodeproj/SWCompression.plist index f6f8a4ef..b18df6cd 100644 --- a/SWCompression.xcodeproj/SWCompression.plist +++ b/SWCompression.xcodeproj/SWCompression.plist @@ -15,9 +15,9 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 4.6.0 + 4.6.1 CFBundleVersion - 80 + 81 NSHumanReadableCopyright Copyright © 2021 Timofey Solomko diff --git a/SWCompression.xcodeproj/TestSWCompression.plist b/SWCompression.xcodeproj/TestSWCompression.plist index 95886263..eefd1cd8 100644 --- a/SWCompression.xcodeproj/TestSWCompression.plist +++ b/SWCompression.xcodeproj/TestSWCompression.plist @@ -15,8 +15,8 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 4.6.0 + 4.6.1 CFBundleVersion - 80 + 81 diff --git a/SWCompression.xcodeproj/project.pbxproj b/SWCompression.xcodeproj/project.pbxproj index bb016374..c3ee7d09 100644 --- a/SWCompression.xcodeproj/project.pbxproj +++ b/SWCompression.xcodeproj/project.pbxproj @@ -220,6 +220,9 @@ E652D8F5263D678000FC229B /* test_nonstandard_runlength.answer in Resources */ = {isa = PBXBuildFile; fileRef = E652D8F4263D678000FC229B /* test_nonstandard_runlength.answer */; }; E66F36242538726E00076A6E /* test_empty.lzma in Resources */ = {isa = PBXBuildFile; fileRef = E66F36232538726E00076A6E /* test_empty.lzma */; }; E66F362C2538E2B700076A6E /* test_empty.zlib in Resources */ = {isa = PBXBuildFile; fileRef = E66F362B2538E2B700076A6E /* test_empty.zlib */; }; + E6EDD6A826F7767F00884532 /* test_minor_version_3.7z in Resources */ = {isa = PBXBuildFile; fileRef = E6EDD6A626F7767E00884532 /* test_minor_version_3.7z */; }; + E6EDD6A926F7767F00884532 /* test_minor_version_4.7z in Resources */ = {isa = PBXBuildFile; fileRef = E6EDD6A726F7767E00884532 /* test_minor_version_4.7z */; }; + E6EDD6AC26F77C0E00884532 /* test_minor_version_2.7z in Resources */ = {isa = PBXBuildFile; fileRef = E6EDD6AB26F77C0E00884532 /* test_minor_version_2.7z */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -447,6 +450,9 @@ E652D8F4263D678000FC229B /* test_nonstandard_runlength.answer */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_nonstandard_runlength.answer; sourceTree = ""; }; E66F36232538726E00076A6E /* test_empty.lzma */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_empty.lzma; sourceTree = ""; }; E66F362B2538E2B700076A6E /* test_empty.zlib */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_empty.zlib; sourceTree = ""; }; + E6EDD6A626F7767E00884532 /* test_minor_version_3.7z */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_minor_version_3.7z; sourceTree = ""; }; + E6EDD6A726F7767E00884532 /* test_minor_version_4.7z */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_minor_version_4.7z; sourceTree = ""; }; + E6EDD6AB26F77C0E00884532 /* test_minor_version_2.7z */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_minor_version_2.7z; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -883,6 +889,9 @@ 06F066061FFB763300312A82 /* test_win.7z */, 0627A4D220FE521B0023987C /* test_unicode.7z */, 068D070321368617002A6C3B /* test_delta_filter.7z */, + E6EDD6AB26F77C0E00884532 /* test_minor_version_2.7z */, + E6EDD6A626F7767E00884532 /* test_minor_version_3.7z */, + E6EDD6A726F7767E00884532 /* test_minor_version_4.7z */, 06F066071FFB763300312A82 /* SWCompressionSourceCode.7z */, ); path = 7z; @@ -949,7 +958,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1250; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = "Timofey Solomko"; TargetAttributes = { 06BE1AC71DB410F100EE0F59 = { @@ -991,6 +1000,7 @@ 06F066201FFB763300312A82 /* test1.answer in Resources */, 06F0664E1FFB763400312A82 /* test_v7.tar in Resources */, 06F0662E1FFB763400312A82 /* test2.gz in Resources */, + E6EDD6AC26F77C0E00884532 /* test_minor_version_2.7z in Resources */, 06F0665C1FFB763400312A82 /* test9.lzma in Resources */, 06F066661FFB763400312A82 /* test3.7z in Resources */, 06F0664B1FFB763400312A82 /* test.tar in Resources */, @@ -1073,6 +1083,7 @@ 06F066351FFB763400312A82 /* test5.xz in Resources */, 06F066631FFB763400312A82 /* test_complicated_coding_scheme.7z in Resources */, 06F066641FFB763400312A82 /* test_anti_file.7z in Resources */, + E6EDD6A826F7767F00884532 /* test_minor_version_3.7z in Resources */, 064D01AD20FD077D00CAE058 /* test_unicode_pax.tar in Resources */, 06F0665E1FFB763400312A82 /* test_empty_cont.7z in Resources */, 06F066731FFB763400312A82 /* test3.bz2 in Resources */, @@ -1083,6 +1094,7 @@ 06F066571FFB763400312A82 /* test_empty_file.tar in Resources */, 06F066441FFB763400312A82 /* test_data_descriptor.zip in Resources */, 06F066581FFB763400312A82 /* full_test.tar in Resources */, + E6EDD6A926F7767F00884532 /* test_minor_version_4.7z in Resources */, 06F0662B1FFB763400312A82 /* test3.gz in Resources */, 06F0664C1FFB763400312A82 /* test_pax.tar in Resources */, 06F0663A1FFB763400312A82 /* test_multi.xz in Resources */, @@ -1270,7 +1282,7 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -1351,7 +1363,7 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_STRICT_OBJC_MSGSEND = YES; "FRAMEWORK_SEARCH_PATHS[sdk=appletvos*]" = ( @@ -1412,7 +1424,7 @@ APPLICATION_EXTENSION_API_ONLY = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 80; + DYLIB_CURRENT_VERSION = 81; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = SWCompression.xcodeproj/SWCompression.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -1439,7 +1451,7 @@ APPLICATION_EXTENSION_API_ONLY = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 80; + DYLIB_CURRENT_VERSION = 81; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = SWCompression.xcodeproj/SWCompression.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; diff --git a/SWCompression.xcodeproj/xcshareddata/xcschemes/SWCompression.xcscheme b/SWCompression.xcodeproj/xcshareddata/xcschemes/SWCompression.xcscheme index d423d3f1..de89491d 100644 --- a/SWCompression.xcodeproj/xcshareddata/xcschemes/SWCompression.xcscheme +++ b/SWCompression.xcodeproj/xcshareddata/xcschemes/SWCompression.xcscheme @@ -1,6 +1,6 @@ Data { let byteReader = LittleEndianByteReader(data: data) + guard byteReader.bytesLeft >= 1 + else { throw LZMAError.rangeDecoderInitError } return try decompress(byteReader, byteReader.byte()) } diff --git a/Sources/ZIP/LittleEndianByteReader+Zip.swift b/Sources/ZIP/LittleEndianByteReader+Zip.swift index bcb9f870..f5246e35 100644 --- a/Sources/ZIP/LittleEndianByteReader+Zip.swift +++ b/Sources/ZIP/LittleEndianByteReader+Zip.swift @@ -6,10 +6,6 @@ import Foundation import BitByteData -#if os(Linux) - import CoreFoundation -#endif - extension LittleEndianByteReader { func zipString(_ length: Int, _ useUtf8: Bool) -> String? { @@ -21,12 +17,7 @@ extension LittleEndianByteReader { return String(data: stringData, encoding: .utf8) } if String.cp437Available && !stringData.needsUtf8() { - #if os(Windows) return String(data: stringData, encoding: String.cp437Encoding) - #else - return String(data: stringData, encoding: String.Encoding(rawValue: - CFStringConvertEncodingToNSStringEncoding(String.cp437Encoding))) - #endif } else { return String(data: stringData, encoding: .utf8) } @@ -36,21 +27,9 @@ extension LittleEndianByteReader { fileprivate extension String { - #if os(Linux) - #if compiler(>=5.3) - static let cp437Encoding: CFStringEncoding = CFStringEncoding(CFStringEncodings.dosLatinUS.rawValue) - #else - static let cp437Encoding: CFStringEncoding = UInt32(truncatingIfNeeded: UInt(kCFStringEncodingDOSLatinUS)) - #endif - static let cp437Available: Bool = CFStringIsEncodingAvailable(cp437Encoding) - #elseif os(Windows) - // "Latin-US (DOS)" CP437-2147483120 - static let cp437Encoding = String.Encoding(rawValue: 0x80000400) - static let cp437Available = String.availableStringEncodings.contains(cp437Encoding) - #else - static let cp437Encoding = CFStringEncoding(CFStringEncodings.dosLatinUS.rawValue) - static let cp437Available = CFStringIsEncodingAvailable(cp437Encoding) - #endif + // "Latin-US (DOS)" CP437-2147483120 + static let cp437Encoding = String.Encoding(rawValue: 0x80000400) + static let cp437Available = String.availableStringEncodings.contains(cp437Encoding) } diff --git a/Sources/swcomp/Benchmarks/BenchmarkCommand.swift b/Sources/swcomp/Benchmarks/BenchmarkCommand.swift index c731780c..fa80bce7 100644 --- a/Sources/swcomp/Benchmarks/BenchmarkCommand.swift +++ b/Sources/swcomp/Benchmarks/BenchmarkCommand.swift @@ -13,12 +13,28 @@ import SwiftCLI protocol BenchmarkCommand: Command { - var files: CollectedParameter { get } + associatedtype InputType + + var inputs: CollectedParameter { get } var benchmarkName: String { get } - var benchmarkFunction: (Data) throws -> Any { get } + func loadInput(_ input: String) throws -> (InputType, Double) + var benchmarkFunction: (InputType) throws -> Any { get } + +} + +extension BenchmarkCommand where InputType == Data { + + func loadInput(_ input: String) throws -> (Data, Double) { + let inputURL = URL(fileURLWithPath: input) + let inputData = try Data(contentsOf: inputURL, options: .mappedIfSafe) + let attr = try FileManager.default.attributesOfItem(atPath: input) + let inputSize = Double(attr[.size] as! UInt64) + return (inputData, inputSize) + } + } extension BenchmarkCommand { @@ -28,16 +44,15 @@ extension BenchmarkCommand { print(String(repeating: "=", count: title.count)) print(title) - for file in self.files.value { - print("File: \(file)") + for input in self.inputs.value { + print("Input: \(input)") - let inputURL = URL(fileURLWithPath: file) - let fileData = try Data(contentsOf: inputURL, options: .mappedIfSafe) + let (loadedInput, inputSize) = try self.loadInput(input) - var totalTime: Double = 0 + var totalSpeed: Double = 0 - var maxTime = Double(Int.min) - var minTime = Double(Int.max) + var maxSpeed = Double(Int.min) + var minSpeed = Double(Int.max) print("Iterations: ", terminator: "") #if !os(Linux) @@ -45,30 +60,35 @@ extension BenchmarkCommand { #endif // Zeroth (excluded) iteration. let startTime = CFAbsoluteTimeGetCurrent() - _ = try benchmarkFunction(fileData) + _ = try benchmarkFunction(loadedInput) let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime - print(String(format: "(%.3f) ", timeElapsed), terminator: "") + let speed = inputSize / timeElapsed + print("(\(SpeedFormat(speed).format())), ", terminator: "") #if !os(Linux) fflush(__stdoutp) #endif for _ in 1...10 { let startTime = CFAbsoluteTimeGetCurrent() - _ = try benchmarkFunction(fileData) + _ = try benchmarkFunction(loadedInput) let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime - print(String(format: "%.3f ", timeElapsed), terminator: "") + let speed = inputSize / timeElapsed + print(SpeedFormat(speed).format() + ", ", terminator: "") #if !os(Linux) fflush(__stdoutp) #endif - totalTime += timeElapsed - if timeElapsed > maxTime { - maxTime = timeElapsed + totalSpeed += speed + if speed > maxSpeed { + maxSpeed = speed } - if timeElapsed < minTime { - minTime = timeElapsed + if speed < minSpeed { + minSpeed = speed } } - print(String(format: "\nAverage: %.3f \u{B1} %.3f\n", totalTime / 10, (maxTime - minTime) / 2)) + let avgSpeed = totalSpeed / 10 + let avgSpeedFormat = SpeedFormat(avgSpeed) + let speedUncertainty = (maxSpeed - minSpeed) / 2 + print("\nAverage: \(avgSpeedFormat.format().prefix { $0 != " " }) \u{B1} \(avgSpeedFormat.format(speedUncertainty))\n") } } diff --git a/Sources/swcomp/Benchmarks/BenchmarkGroup.swift b/Sources/swcomp/Benchmarks/BenchmarkGroup.swift index 984acca1..61516c3b 100644 --- a/Sources/swcomp/Benchmarks/BenchmarkGroup.swift +++ b/Sources/swcomp/Benchmarks/BenchmarkGroup.swift @@ -4,6 +4,7 @@ // See LICENSE for license information import Foundation +import SWCompression import SwiftCLI class BenchmarkGroup: CommandGroup { @@ -11,6 +12,102 @@ class BenchmarkGroup: CommandGroup { let name = "benchmark" let shortDescription = "Run the specified benchmark using external files" - let children: [Routable] = [UnGzip(), UnXz(), UnBz2(), InfoTar(), InfoZip(), Info7z(), CompDeflate(), CompBz2()] + let children: [Routable] = [UnGzip(), UnXz(), UnBz2(), InfoTar(), InfoZip(), Info7z(), CompDeflate(), CompBz2(), CreateTar()] + +} + +class CompBz2: BenchmarkCommand { + + let name = "comp-bz2" + let shortDescription = "BZip2 compression" + + let inputs = CollectedParameter() + + let benchmarkName = "BZip2 Compression" + let benchmarkFunction: (Data) throws -> Any = BZip2.compress + +} + +class CompDeflate: BenchmarkCommand { + + let name = "comp-deflate" + let shortDescription = "Deflate compression" + + let inputs = CollectedParameter() + + let benchmarkName = "Deflate Compression" + let benchmarkFunction: (Data) throws -> Any = Deflate.compress + +} + +class Info7z: BenchmarkCommand { + + let name = "info-7z" + let shortDescription = "7-Zip info function" + + let inputs = CollectedParameter() + + let benchmarkName = "7-Zip info function" + let benchmarkFunction: (Data) throws -> Any = SevenZipContainer.info + +} + +class InfoTar: BenchmarkCommand { + + let name = "info-tar" + let shortDescription = "TAR info function" + + let inputs = CollectedParameter() + + let benchmarkName = "TAR info function" + let benchmarkFunction: (Data) throws -> Any = TarContainer.info + +} + +class InfoZip: BenchmarkCommand { + + let name = "info-zip" + let shortDescription = "ZIP info function" + + let inputs = CollectedParameter() + + let benchmarkName = "ZIP info function" + let benchmarkFunction: (Data) throws -> Any = ZipContainer.info + +} + +class UnBz2: BenchmarkCommand { + + let name = "un-bz2" + let shortDescription = "BZip2 unarchiving" + + let inputs = CollectedParameter() + + let benchmarkName = "BZip2 Unarchive" + let benchmarkFunction: (Data) throws -> Any = BZip2.decompress + +} + +class UnGzip: BenchmarkCommand { + + let name = "un-gzip" + let shortDescription = "GZip unarchiving" + + let inputs = CollectedParameter() + + let benchmarkName = "GZip Unarchive" + let benchmarkFunction: (Data) throws -> Any = GzipArchive.unarchive + +} + +class UnXz: BenchmarkCommand { + + let name = "un-xz" + let shortDescription = "XZ unarchiving" + + let inputs = CollectedParameter() + + let benchmarkName = "XZ Unarchive" + let benchmarkFunction: (Data) throws -> Any = XZArchive.unarchive } diff --git a/Sources/swcomp/Benchmarks/CompBz2.swift b/Sources/swcomp/Benchmarks/CompBz2.swift deleted file mode 100644 index a1b06f61..00000000 --- a/Sources/swcomp/Benchmarks/CompBz2.swift +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2021 Timofey Solomko -// Licensed under MIT License -// -// See LICENSE for license information - -import Foundation -import SWCompression -import SwiftCLI - -class CompBz2: BenchmarkCommand { - - let name = "comp-bz2" - let shortDescription = "BZip2 compression" - - let files = CollectedParameter() - - let benchmarkName = "BZip2 Compression" - let benchmarkFunction: (Data) throws -> Any = BZip2.compress - -} diff --git a/Sources/swcomp/Benchmarks/CompDeflate.swift b/Sources/swcomp/Benchmarks/CompDeflate.swift deleted file mode 100644 index addfaf82..00000000 --- a/Sources/swcomp/Benchmarks/CompDeflate.swift +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2021 Timofey Solomko -// Licensed under MIT License -// -// See LICENSE for license information - -import Foundation -import SWCompression -import SwiftCLI - -class CompDeflate: BenchmarkCommand { - - let name = "comp-deflate" - let shortDescription = "Deflate compression" - - let files = CollectedParameter() - - let benchmarkName = "Deflate Compression" - let benchmarkFunction: (Data) throws -> Any = Deflate.compress - -} diff --git a/Sources/swcomp/Benchmarks/CreateTar.swift b/Sources/swcomp/Benchmarks/CreateTar.swift new file mode 100644 index 00000000..404ee8a3 --- /dev/null +++ b/Sources/swcomp/Benchmarks/CreateTar.swift @@ -0,0 +1,35 @@ +// Copyright (c) 2021 Timofey Solomko +// Licensed under MIT License +// +// See LICENSE for license information + +import Foundation +import SWCompression +import SwiftCLI + +class CreateTar: BenchmarkCommand { + + let name = "create-tar" + let shortDescription = "Tar container creation" + + let inputs = CollectedParameter() + + let benchmarkName = "TAR Create" + let benchmarkFunction: ([TarEntry]) throws -> Any = TarContainer.create + + func loadInput(_ input: String) throws -> ([TarEntry], Double) { + return try (TarEntry.createEntries(input, false), Double(URL(fileURLWithPath: input).directorySize())) + } + +} + +fileprivate extension URL { + + func directorySize() throws -> Int { + let urls = FileManager.default.enumerator(at: self, includingPropertiesForKeys: nil)?.allObjects as! [URL] + return try urls.lazy.reduce(0) { + (try $1.resourceValues(forKeys: [.totalFileAllocatedSizeKey]).totalFileAllocatedSize ?? 0) + $0 + } + } + +} diff --git a/Sources/swcomp/Benchmarks/Info7z.swift b/Sources/swcomp/Benchmarks/Info7z.swift deleted file mode 100644 index 943294e4..00000000 --- a/Sources/swcomp/Benchmarks/Info7z.swift +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2021 Timofey Solomko -// Licensed under MIT License -// -// See LICENSE for license information - -import Foundation -import SWCompression -import SwiftCLI - -class Info7z: BenchmarkCommand { - - let name = "info-7z" - let shortDescription = "7-Zip info function" - - let files = CollectedParameter() - - let benchmarkName = "7-Zip info function" - let benchmarkFunction: (Data) throws -> Any = SevenZipContainer.info - -} diff --git a/Sources/swcomp/Benchmarks/InfoTar.swift b/Sources/swcomp/Benchmarks/InfoTar.swift deleted file mode 100644 index 55bf3aab..00000000 --- a/Sources/swcomp/Benchmarks/InfoTar.swift +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2021 Timofey Solomko -// Licensed under MIT License -// -// See LICENSE for license information - -import Foundation -import SWCompression -import SwiftCLI - -class InfoTar: BenchmarkCommand { - - let name = "info-tar" - let shortDescription = "TAR info function" - - let files = CollectedParameter() - - let benchmarkName = "TAR info function" - let benchmarkFunction: (Data) throws -> Any = TarContainer.info - -} diff --git a/Sources/swcomp/Benchmarks/InfoZip.swift b/Sources/swcomp/Benchmarks/InfoZip.swift deleted file mode 100644 index a44aeae9..00000000 --- a/Sources/swcomp/Benchmarks/InfoZip.swift +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2021 Timofey Solomko -// Licensed under MIT License -// -// See LICENSE for license information - -import Foundation -import SWCompression -import SwiftCLI - -class InfoZip: BenchmarkCommand { - - let name = "info-zip" - let shortDescription = "ZIP info function" - - let files = CollectedParameter() - - let benchmarkName = "ZIP info function" - let benchmarkFunction: (Data) throws -> Any = ZipContainer.info - -} diff --git a/Sources/swcomp/Benchmarks/SpeedFormat.swift b/Sources/swcomp/Benchmarks/SpeedFormat.swift new file mode 100644 index 00000000..678079b4 --- /dev/null +++ b/Sources/swcomp/Benchmarks/SpeedFormat.swift @@ -0,0 +1,60 @@ +// Copyright (c) 2021 Timofey Solomko +// Licensed under MIT License +// +// See LICENSE for license information + +import Foundation + +enum SpeedFormat { + + case bytes(Double) + case kilo(Double) + case mega(Double) + case giga(Double) + case tera(Double) + + init(_ speed: Double) { + if speed > pow(1000, 4) { + self = .tera(speed) + } else if speed > pow(1000, 3) { + self = .giga(speed) + } else if speed > pow(1000, 2) { + self = .mega(speed) + } else if speed > 1000 { + self = .kilo(speed) + } else { + self = .bytes(speed) + } + } + + func format() -> String { + switch self { + case .bytes(let speed): + return String(format: "%.3f B/s", speed) + case .kilo(let speed): + return String(format: "%.3f kB/s", speed / 1000) + case .mega(let speed): + return String(format: "%.3f MB/s", speed / 1000 / 1000) + case .giga(let speed): + return String(format: "%.3f GB/s", speed / 1000 / 1000 / 1000) + case .tera(let speed): + return String(format: "%.3f TB/s", speed / 1000 / 1000 / 1000 / 1000) + } + } + + func format(_ speed: Double) -> String { + switch self { + case .bytes(_): + return String(format: "%.3f B/s", speed) + case .kilo(_): + return String(format: "%.3f kB/s", speed / 1000) + case .mega(_): + return String(format: "%.3f MB/s", speed / 1000 / 1000) + case .giga(_): + return String(format: "%.3f GB/s", speed / 1000 / 1000 / 1000) + case .tera(_): + return String(format: "%.3f TB/s", speed / 1000 / 1000 / 1000 / 1000) + } + } + +} diff --git a/Sources/swcomp/Benchmarks/UnBz2.swift b/Sources/swcomp/Benchmarks/UnBz2.swift deleted file mode 100644 index b6c279e3..00000000 --- a/Sources/swcomp/Benchmarks/UnBz2.swift +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2021 Timofey Solomko -// Licensed under MIT License -// -// See LICENSE for license information - -import Foundation -import SWCompression -import SwiftCLI - -class UnBz2: BenchmarkCommand { - - let name = "un-bz2" - let shortDescription = "BZip2 unarchiving" - - let files = CollectedParameter() - - let benchmarkName = "BZip2 Unarchive" - let benchmarkFunction: (Data) throws -> Any = BZip2.decompress - -} diff --git a/Sources/swcomp/Benchmarks/UnGzip.swift b/Sources/swcomp/Benchmarks/UnGzip.swift deleted file mode 100644 index 690a0037..00000000 --- a/Sources/swcomp/Benchmarks/UnGzip.swift +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2021 Timofey Solomko -// Licensed under MIT License -// -// See LICENSE for license information - -import Foundation -import SWCompression -import SwiftCLI - -class UnGzip: BenchmarkCommand { - - let name = "un-gzip" - let shortDescription = "GZip unarchiving" - - let files = CollectedParameter() - - let benchmarkName = "GZip Unarchive" - let benchmarkFunction: (Data) throws -> Any = GzipArchive.unarchive - -} diff --git a/Sources/swcomp/Benchmarks/UnXz.swift b/Sources/swcomp/Benchmarks/UnXz.swift deleted file mode 100644 index f0cb7a2c..00000000 --- a/Sources/swcomp/Benchmarks/UnXz.swift +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2021 Timofey Solomko -// Licensed under MIT License -// -// See LICENSE for license information - -import Foundation -import SWCompression -import SwiftCLI - -class UnXz: BenchmarkCommand { - - let name = "un-xz" - let shortDescription = "XZ unarchiving" - - let files = CollectedParameter() - - let benchmarkName = "XZ Unarchive" - let benchmarkFunction: (Data) throws -> Any = XZArchive.unarchive - -} diff --git a/Sources/swcomp/Containers/7ZipCommand.swift b/Sources/swcomp/Containers/7ZipCommand.swift index b96fbea9..f09f801f 100644 --- a/Sources/swcomp/Containers/7ZipCommand.swift +++ b/Sources/swcomp/Containers/7ZipCommand.swift @@ -9,6 +9,8 @@ import SwiftCLI class SevenZipCommand: ContainerCommand { + typealias ContainerType = SevenZipContainer + let name = "7z" let shortDescription = "Extracts a 7-Zip container" @@ -18,7 +20,4 @@ class SevenZipCommand: ContainerCommand { let archive = Parameter() - let openFunction: (Data) throws -> [SevenZipEntry] = SevenZipContainer.open - let infoFunction: (Data) throws -> [SevenZipEntryInfo] = SevenZipContainer.info - } diff --git a/Sources/swcomp/Containers/ContainerCommand.swift b/Sources/swcomp/Containers/ContainerCommand.swift index 32459532..a156d85d 100644 --- a/Sources/swcomp/Containers/ContainerCommand.swift +++ b/Sources/swcomp/Containers/ContainerCommand.swift @@ -9,7 +9,7 @@ import SwiftCLI protocol ContainerCommand: Command { - associatedtype Entry: ContainerEntry + associatedtype ContainerType: Container var info: Flag { get } var extract: Key { get } @@ -17,9 +17,6 @@ protocol ContainerCommand: Command { var archive: Parameter { get } - var openFunction: (Data) throws -> [Entry] { get } - var infoFunction: (Data) throws -> [Entry.Info] { get } - } extension ContainerCommand { @@ -33,7 +30,7 @@ extension ContainerCommand { let fileData = try Data(contentsOf: URL(fileURLWithPath: self.archive.value), options: .mappedIfSafe) if info.value { - let entries = try infoFunction(fileData) + let entries = try ContainerType.info(container: fileData) swcomp.printInfo(entries) } else if let outputPath = self.extract.value { if try !isValidOutputDirectory(outputPath, create: true) { @@ -41,7 +38,7 @@ extension ContainerCommand { exit(1) } - let entries = try openFunction(fileData) + let entries = try ContainerType.open(container: fileData) try swcomp.write(entries, outputPath, verbose.value) } } diff --git a/Sources/swcomp/Containers/TarCommand.swift b/Sources/swcomp/Containers/TarCommand.swift index 54ec091d..c37db1ea 100644 --- a/Sources/swcomp/Containers/TarCommand.swift +++ b/Sources/swcomp/Containers/TarCommand.swift @@ -92,94 +92,10 @@ class TarCommand: Command { print("Creating new container at \"\(self.archive.value)\" from \"\(inputPath)\"") print("d = directory, f = file, l = symbolic link") } - let entries = try self.createEntries(inputPath, verbose.value) + let entries = try TarEntry.createEntries(inputPath, verbose.value) let containerData = TarContainer.create(from: entries) try containerData.write(to: URL(fileURLWithPath: self.archive.value)) } } - private func createEntries(_ inputPath: String, _ verbose: Bool) throws -> [TarEntry] { - let inputURL = URL(fileURLWithPath: inputPath) - let fileManager = FileManager.default - - let fileAttributes = try fileManager.attributesOfItem(atPath: inputPath) - - let name = inputURL.relativePath - - let entryType: ContainerEntryType - if let typeFromAttributes = fileAttributes[.type] as? FileAttributeType { - switch typeFromAttributes { - case .typeBlockSpecial: - entryType = .blockSpecial - case .typeCharacterSpecial: - entryType = .characterSpecial - case .typeDirectory: - entryType = .directory - case .typeRegular: - entryType = .regular - case .typeSocket: - entryType = .socket - case .typeSymbolicLink: - entryType = .symbolicLink - case .typeUnknown: - entryType = .unknown - default: - entryType = .unknown - } - } else { - entryType = .unknown - } - - var info = TarEntryInfo(name: name, type: entryType) - info.creationTime = fileAttributes[.creationDate] as? Date - info.groupID = (fileAttributes[.groupOwnerAccountID] as? NSNumber)?.intValue - info.ownerGroupName = fileAttributes[.groupOwnerAccountName] as? String - info.modificationTime = fileAttributes[.modificationDate] as? Date - info.ownerID = (fileAttributes[.ownerAccountID] as? NSNumber)?.intValue - info.ownerUserName = fileAttributes[.ownerAccountName] as? String - if let posixPermissions = (fileAttributes[.posixPermissions] as? NSNumber)?.intValue { - info.permissions = Permissions(rawValue: UInt32(truncatingIfNeeded: posixPermissions)) - } - - var entryData = Data() - if entryType == .symbolicLink { - info.linkName = try fileManager.destinationOfSymbolicLink(atPath: inputPath) - } else if entryType != .directory { - entryData = try Data(contentsOf: URL(fileURLWithPath: inputPath)) - } - - if verbose { - var log = "" - switch entryType { - case .regular: - log += "f: " - case .directory: - log += "d: " - case .symbolicLink: - log += "l:" - default: - log += "u: " - } - log += name - if entryType == .symbolicLink { - log += " -> " + info.linkName - } - print(log) - } - - let entry = TarEntry(info: info, data: entryData) - - var entries = [TarEntry]() - entries.append(entry) - - if entryType == .directory { - for subPath in try fileManager.contentsOfDirectory(atPath: inputPath) { - entries.append(contentsOf: try self.createEntries(inputURL.appendingPathComponent(subPath).relativePath, - verbose)) - } - } - - return entries - } - } diff --git a/Sources/swcomp/Containers/ZipCommand.swift b/Sources/swcomp/Containers/ZipCommand.swift index f104b072..3e5a04ea 100644 --- a/Sources/swcomp/Containers/ZipCommand.swift +++ b/Sources/swcomp/Containers/ZipCommand.swift @@ -9,6 +9,8 @@ import SwiftCLI class ZipCommand: ContainerCommand { + typealias ContainerType = ZipContainer + let name = "zip" let shortDescription = "Extracts a ZIP container" @@ -18,7 +20,4 @@ class ZipCommand: ContainerCommand { let archive = Parameter() - let openFunction: (Data) throws -> [ZipEntry] = ZipContainer.open - let infoFunction: (Data) throws -> [ZipEntryInfo] = ZipContainer.info - } diff --git a/Sources/swcomp/Extensions/TarEntry+Create.swift b/Sources/swcomp/Extensions/TarEntry+Create.swift new file mode 100644 index 00000000..2e6ddcaa --- /dev/null +++ b/Sources/swcomp/Extensions/TarEntry+Create.swift @@ -0,0 +1,96 @@ +// Copyright (c) 2021 Timofey Solomko +// Licensed under MIT License +// +// See LICENSE for license information + +import Foundation +import SWCompression +import SwiftCLI + +extension TarEntry { + + static func createEntries(_ inputPath: String, _ verbose: Bool) throws -> [TarEntry] { + let inputURL = URL(fileURLWithPath: inputPath) + let fileManager = FileManager.default + + let fileAttributes = try fileManager.attributesOfItem(atPath: inputPath) + + let name = inputURL.relativePath + + let entryType: ContainerEntryType + if let typeFromAttributes = fileAttributes[.type] as? FileAttributeType { + switch typeFromAttributes { + case .typeBlockSpecial: + entryType = .blockSpecial + case .typeCharacterSpecial: + entryType = .characterSpecial + case .typeDirectory: + entryType = .directory + case .typeRegular: + entryType = .regular + case .typeSocket: + entryType = .socket + case .typeSymbolicLink: + entryType = .symbolicLink + case .typeUnknown: + entryType = .unknown + default: + entryType = .unknown + } + } else { + entryType = .unknown + } + + var info = TarEntryInfo(name: name, type: entryType) + info.creationTime = fileAttributes[.creationDate] as? Date + info.groupID = (fileAttributes[.groupOwnerAccountID] as? NSNumber)?.intValue + info.ownerGroupName = fileAttributes[.groupOwnerAccountName] as? String + info.modificationTime = fileAttributes[.modificationDate] as? Date + info.ownerID = (fileAttributes[.ownerAccountID] as? NSNumber)?.intValue + info.ownerUserName = fileAttributes[.ownerAccountName] as? String + if let posixPermissions = (fileAttributes[.posixPermissions] as? NSNumber)?.intValue { + info.permissions = Permissions(rawValue: UInt32(truncatingIfNeeded: posixPermissions)) + } + + var entryData = Data() + if entryType == .symbolicLink { + info.linkName = try fileManager.destinationOfSymbolicLink(atPath: inputPath) + } else if entryType != .directory { + entryData = try Data(contentsOf: URL(fileURLWithPath: inputPath)) + } + + if verbose { + var log = "" + switch entryType { + case .regular: + log += "f: " + case .directory: + log += "d: " + case .symbolicLink: + log += "l:" + default: + log += "u: " + } + log += name + if entryType == .symbolicLink { + log += " -> " + info.linkName + } + print(log) + } + + let entry = TarEntry(info: info, data: entryData) + + var entries = [TarEntry]() + entries.append(entry) + + if entryType == .directory { + for subPath in try fileManager.contentsOfDirectory(atPath: inputPath) { + entries.append(contentsOf: try self.createEntries(inputURL.appendingPathComponent(subPath).relativePath, + verbose)) + } + } + + return entries + } + +} diff --git a/Sources/swcomp/main.swift b/Sources/swcomp/main.swift index 4b329857..47c7ba8d 100644 --- a/Sources/swcomp/main.swift +++ b/Sources/swcomp/main.swift @@ -7,7 +7,7 @@ import Foundation import SWCompression import SwiftCLI -let cli = CLI(name: "swcomp", version: "4.6.0", +let cli = CLI(name: "swcomp", version: "4.6.1", description: """ swcomp - a small command-line client for SWCompression framework. Serves as an example of SWCompression usage. diff --git a/Tests/BZip2Tests.swift b/Tests/BZip2Tests.swift index 8fbf2594..e5b1e1a0 100644 --- a/Tests/BZip2Tests.swift +++ b/Tests/BZip2Tests.swift @@ -67,4 +67,8 @@ class BZip2Tests: XCTestCase { XCTAssertThrowsError(try BZip2.decompress(data: testData)) } + func testEmptyData() throws { + XCTAssertThrowsError(try BZip2.decompress(data: Data())) + } + } diff --git a/Tests/GzipTests.swift b/Tests/GzipTests.swift index 3c350d74..cf829bb8 100644 --- a/Tests/GzipTests.swift +++ b/Tests/GzipTests.swift @@ -151,4 +151,8 @@ class GzipTests: XCTestCase { XCTAssertThrowsError(try GzipArchive.multiUnarchive(archive: testData)) } + func testEmptyData() throws { + XCTAssertThrowsError(try GzipArchive.unarchive(archive: Data())) + } + } diff --git a/Tests/LzmaTests.swift b/Tests/LzmaTests.swift index 580b5bf9..acc26d88 100644 --- a/Tests/LzmaTests.swift +++ b/Tests/LzmaTests.swift @@ -51,5 +51,9 @@ class LzmaTests: XCTestCase { XCTAssertThrowsError(try LZMA.decompress(data: testData)) } + func testEmptyData() throws { + XCTAssertThrowsError(try LZMA.decompress(data: Data())) + XCTAssertThrowsError(try LZMA2.decompress(data: Data())) + } } diff --git a/Tests/Results.md b/Tests/Results.md index 4bc90a0a..5e99b1f6 100644 --- a/Tests/Results.md +++ b/Tests/Results.md @@ -1,18 +1,24 @@ # Test Results -In this document you can find the results of benchmarking which was performed on Macbook Pro, Late 2011. The main -purpose of these results is to track progress from version to version. +In this document you can find the results of benchmarking which was performed on Macbook Air, Retina, 13-inch, Early +2020 with 1,1 GHz Quad-Core Intel Core i5 CPU. The main purpose of these results is to track progress from version to +version. -__Note:__ Since 4.1.0 version of SWCompression internal functionality related to reading/writing bits and bytes -is published as a separate framework, [BitByteData](https://github.com/tsolomko/BitByteData). -The overall performance heavily depends on the speed of reading and writing, and thus BitByteData's version, which is -specified in a separate column in the tables below, becomes relevant to benchmarking, since newer versions can contain -performance improvements. +## Changelog -__Note:__ Since 4.3.0 version of SWCompression the first (zeroth, actually) iteration is excluded from averages -calculation since this iteration has abnormally longer execution time than any of the following iterations. This -exclusion led not only to (artificially) improved results, but also to the increased quality of the results by reducing -calculated uncertainty. +__September 2021.__ The results are now listed in terms of speed (e.g. MB/s); the benchmarks for Deflate, BZip2 compression and +TAR container creation have been added; all previous results have been removed since newer hardware is now used; added +results for the `-Ounchecked` compiler option; the macOS version is now listed for results. + +__April 2018.__ The first (zeroth, actually) iteration is now excluded from averages calculation since this iteration +has abnormally longer execution time than any of the following iterations. This exclusion led not only to (artificially) +improved results, but also to the increased quality of the results by reducing calculated uncertainty. In addition, the +averages are now computed over 10 iterations instead of 6. + +__January 2018.__ SWCompression internal functionality related to reading/writing bits and bytes is published as a +separate framework, [BitByteData](https://github.com/tsolomko/BitByteData). The overall performance heavily depends on +the speed of reading and writing, and thus BitByteData's version, which is specified in a separate column in the tables +below, becomes relevant to benchmarking, since newer versions can contain performance improvements. ## Tests description @@ -29,64 +35,68 @@ using "Release" configuration. __Note:__ External commands used to create compressed files were run using their default sets of options. -__Note:__ All results are averages over 10 iterations (6 iterations until 4.3.0 version). +__Note:__ All results are averages over 10 iterations. If the uncertainty is not listed it means that it is less than +10% in terms of the units listed. ## BZip2 Decompress -|SWCompression
version|BitByteData
version|Swift
version|Test 1|Test 2|Test 3| -|:---:|:---:|:---:|---|---|---| -|4.0.0|—|4.0.X|6.821 ± 0.042|54.214 ± 1.398|7.255 ± 0.117| -|4.0.1|—|4.0.X|6.797 ± 0.080|54.046 ± 1.070|7.177 ± 0.029| -|4.1.0|1.0.1|4.0.X|4.452 ± 0.125|36.768 ± 0.382|4.880 ± 0.094| -|4.3.0|1.2.0|4.1.0|3.481 ± 0.038|27.187 ± 0.152|3.914 ± 0.091| -|4.4.0|1.2.0|4.1.2|3.548 ± 0.037|27.950 ± 0.975|3.956 ± 0.063| +|SWCompression
version|BitByteData
version|macOS
version|Swift
version|Test 1|Test 2|Test 3| +|:---:|:---:|:---:|:---:|---|---|---| +|4.6.0|2.0.1|11.5.2|5.4.2|2.764 MB/s|2.973 MB/s|2.410 MB/s| +|4.6.0-unchecked|2.0.1|11.5.2|5.4.2|3.279 ± 0.429 MB/s|4.361 ± 0.306 MB/s|3.169 ± 0.180 MB/s| ## XZ Unarchive (LZMA/LZMA2 Decompress) -|SWCompression
version|BitByteData
version|Swift
version|Test 1|Test 2|Test 3| -|:---:|:---:|:---:|---|---|---| -|4.0.0|—|4.0.X|error|24.663 ± 2.349|2.904 ± 0.076| -|4.0.1|—|4.0.X|2.475 ± 0.067|23.507 ± 0.423|2.901 ± 0.049| -|4.1.0|1.0.1|4.0.X|2.480 ± 0.091|24.126 ± 1.239|2.892 ± 0.052| -|4.3.0|1.2.0|4.1.0|2.664 ± 0.031|26.030 ± 1.200|3.111 ± 0.053| -|4.4.0|1.2.0|4.1.2|2.494 ± 0.038|23.773 ± 0.563|2.912 ± 0.018| +|SWCompression
version|BitByteData
version|macOS
version|Swift
version|Test 1|Test 2|Test 3| +|:---:|:---:|:---:|:---:|---|---|---| +|4.6.0|2.0.1|11.5.2|5.4.2|5.246 ± 0.279 MB/s|5.543 ± 0.143 MB/s|5.322 ± 0.26 MB/s| +|4.6.0-unchecked|2.0.1|11.5.2|5.4.2|6.015 ± 0.319 MB/s|6.374 ± 0.143 MB/s|5.841 ± 0.212 MB/s| ## GZip Unarchive (Deflate Decompress) -|SWCompression
version|BitByteData
version|Swift
version|Test 1|Test 2|Test 3| -|:---:|:---:|:---:|---|---|---| -|4.0.0|—|4.0.X|4.007 ± 0.196|32.043 ± 0.581|4.303 ± 0.045| -|4.0.1|—|4.0.X|3.886 ± 0.100|32.390 ± 0.896|4.295 ± 0.040| -|4.1.0|1.0.1|4.0.X|2.886 ± 0.571|22.134 ± 1.473|2.700 ± 0.168| -|4.3.0|1.2.0|4.1.0|1.622 ± 0.016|13.641 ± 0.069|1.804 ± 0.028| -|4.4.0|1.2.0|4.1.2|1.665 ± 0.037|14.046 ± 0.093|1.858 ± 0.055| +|SWCompression
version|BitByteData
version|macOS
version|Swift
version|Test 1|Test 2|Test 3| +|:---:|:---:|:---:|:---:|---|---|---| +|4.6.0|2.0.1|11.5.2|5.4.2|5.946 ± 0.432 MB/s|6.034 ± 0.175 MB/s|6.071 ± 0.25 MB/s| +|4.6.0-unchecked|2.0.1|11.5.2|5.4.2|8.562 ± 0.388 MB/s|9.908 ± 0.204 MB/s|8.286 ± 0.250 MB/s| + +## BZip2 Compress + +|SWCompression
version|BitByteData
version|macOS
version|Swift
version|Test 1|Test 2|Test 3| +|:---:|:---:|:---:|:---:|---|---|---| +|4.6.0|2.0.1|11.5.2|5.4.2|3.540 MB/s|2.862 MB/s|4.253 MB/s| +|4.6.0-unchecked|2.0.1|11.5.2|5.4.2|3.816 MB/s|3.060 MB/s|4.647 MB/s| + +## Deflate Compress + +|SWCompression
version|BitByteData
version|macOS
version|Swift
version|Test 1|Test 2|Test 3| +|:---:|:---:|:---:|:---:|---|---|---| +|4.6.0|2.0.1|11.5.2|5.4.2|12.177 ± 0.259 MB/s|8.809 MB/s|13.594 ± 0.355 MB/s| +|4.6.0-unchecked|2.0.1|11.5.2|5.4.2|12.914 ± 0.355 MB/s|9.361 MB/s|15.020 ± 0.277 MB/s| ## 7-Zip Info Function -|SWCompression
version|BitByteData
version|Swift
version|Test 1|Test 2|Test 3| -|:---:|:---:|:---:|---|---|---| -|4.0.0|—|4.0.X|0.270 ± 0.010|crash|0.601 ± 0.057| -|4.0.1|—|4.0.X|0.258 ± 0.003|0.601 ± 0.010|0.473 ± 0.008| -|4.1.0|1.0.1|4.0.X|0.062 ± 0.008|0.208 ± 0.010|0.122 ± 0.003| -|4.3.0|1.2.0|4.1.0|0.060 ± 0.006|0.208 ± 0.008|0.123 ± 0.008| -|4.4.0|1.2.0|4.1.2|0.062 ± 0.005|0.212 ± 0.008|0.122 ± 0.007| +|SWCompression
version|BitByteData
version|macOS
version|Swift
version|Test 1|Test 2|Test 3| +|:---:|:---:|:---:|:---:|---|---|---| +|4.6.0|2.0.1|11.5.2|5.4.2|77.072 ± 6.625 MB/s|111.246 ± 4.855 MB/s|38.721 ± 2.681 MB/s| +|4.6.0-unchecked|2.0.1|11.5.2|5.4.2|82.367 ± 7.381 MB/s|116.923 ± 3.812 MB/s|38.519 ± 2.988 MB/s| ## TAR Info Function -|SWCompression
version|BitByteData
version|Swift
version|Test 1|Test 2|Test 3| -|:---:|:---:|:---:|---|---|---| -|4.0.0|—|4.0.X|0.248 ± 0.172|1.563 ± 0.442|1.254 ± 0.277| -|4.0.1|—|4.0.X|0.187 ± 0.177|1.257 ± 0.404|1.016 ± 0.325| -|4.1.0|1.0.1|4.0.X|0.194 ± 0.190|1.335 ± 0.475|1.062 ± 0.353| -|4.3.0|1.2.0|4.1.0|0.111 ± 0.008|1.014 ± 0.016|0.813 ± 0.015| -|4.4.0|1.2.0|4.1.2|0.095 ± 0.005|0.810 ± 0.025|0.680 ± 0.014| +|SWCompression
version|BitByteData
version|macOS
version|Swift
version|Test 1|Test 2|Test 3| +|:---:|:---:|:---:|:---:|---|---|---| +|4.6.0|2.0.1|11.5.2|5.4.2|967.792 ± 58.436 MB/s|1.006 GB/s|217.082 ± 19.783 MB/s| +|4.6.0-unchecked|2.0.1|11.5.2|5.4.2|1.073 ± 0.115 GB/s|1.053 GB/s|246.854 ± 6.763 MB/s| ## ZIP Info Function -|SWCompression
version|BitByteData
version|Swift
version|Test 1|Test 2|Test 3| -|:---:|:---:|:---:|---|---|---| -|4.0.0|—|4.0.X|0.072 ± 0.065|0.669 ± 0.147|0.120 ± 0.081| -|4.0.1|—|4.0.X|0.076 ± 0.064|0.670 ± 0.142|0.123 ± 0.080| -|4.1.0|1.0.1|4.0.X|0.063 ± 0.076|0.148 ± 0.107|0.105 ± 0.089| -|4.3.0|1.2.0|4.1.0|0.044 ± 0.007|0.122 ± 0.009|0.086 ± 0.008| -|4.4.0|1.2.0|4.1.2|0.048 ± 0.005|0.125 ± 0.005|0.091 ± 0.010| +|SWCompression
version|BitByteData
version|macOS
version|Swift
version|Test 1|Test 2|Test 3| +|:---:|:---:|:---:|:---:|---|---|---| +|4.6.0|2.0.1|11.5.2|5.4.2|597.002 ± 75.017 MB/s|1.941 ± 0.135 GB/s|389.626 ± 22.006 MB/s| +|4.6.0-unchecked|2.0.1|11.5.2|5.4.2|685.266 ± 53.895 MB/s|2.147 GB/s|420.461 ± 15.156 MB/s| + +## TAR Create Function + +|SWCompression
version|BitByteData
version|macOS
version|Swift
version|Test 1|Test 2|Test 3| +|:---:|:---:|:---:|:---:|---|---|---| +|4.6.0|2.0.1|11.5.2|5.4.2|139.649 ± 8.310 MB/s|446.101 ± 21.476 MB/s|215.556 ± 17.5 MB/s| +|4.6.0-unchecked|2.0.1|11.5.2|5.4.2|142.681 ± 5.664 MB/s|459.403 ± 17.862 MB/s|220.238 ± 3.476 MB/s| diff --git a/Tests/SevenZipTests.swift b/Tests/SevenZipTests.swift index d01c8cc0..e4d3bfa0 100644 --- a/Tests/SevenZipTests.swift +++ b/Tests/SevenZipTests.swift @@ -19,6 +19,11 @@ class SevenZipTests: XCTestCase { XCTAssertThrowsError(try SevenZipContainer.open(container: testData)) } + func testEmptyData() throws { + XCTAssertThrowsError(try SevenZipContainer.info(container: Data())) + XCTAssertThrowsError(try SevenZipContainer.open(container: Data())) + } + func test1() throws { let testData = try Constants.data(forTest: "test1", withType: SevenZipTests.testType) let entries = try SevenZipContainer.open(container: testData) @@ -388,4 +393,65 @@ class SevenZipTests: XCTestCase { XCTAssertEqual(entries[0].data, answerData) } + func testFormatMinorVersions() throws { + let answerData = try Constants.data(forAnswer: "test2") + + var testData = try Constants.data(forTest: "test_minor_version_2", withType: SevenZipTests.testType) + var entries = try SevenZipContainer.open(container: testData) + + XCTAssertEqual(entries.count, 1) + XCTAssertEqual(entries[0].info.name, "test2.answer") + XCTAssertEqual(entries[0].info.type, .regular) + XCTAssertEqual(entries[0].info.size, answerData.count) + XCTAssertEqual(entries[0].info.permissions, Permissions(rawValue: 0)) + XCTAssertEqual(entries[0].info.dosAttributes, DosAttributes(rawValue: 0x20)) + // Checking times' values is a bit difficult since they are extremely precise. + XCTAssertNotNil(entries[0].info.modificationTime) + XCTAssertNil(entries[0].info.accessTime) + XCTAssertNil(entries[0].info.creationTime) + XCTAssertEqual(entries[0].info.hasStream, true) + XCTAssertEqual(entries[0].info.isEmpty, false) + XCTAssertEqual(entries[0].info.isAnti, false) + XCTAssertEqual(entries[0].info.crc, 4168830779) + XCTAssertEqual(entries[0].data, answerData) + + testData = try Constants.data(forTest: "test_minor_version_3", withType: SevenZipTests.testType) + entries = try SevenZipContainer.open(container: testData) + + XCTAssertEqual(entries.count, 1) + XCTAssertEqual(entries[0].info.name, "test2.answer") + XCTAssertEqual(entries[0].info.type, .regular) + XCTAssertEqual(entries[0].info.size, answerData.count) + XCTAssertEqual(entries[0].info.permissions, Permissions(rawValue: 420)) + XCTAssertEqual(entries[0].info.dosAttributes, DosAttributes(rawValue: 0x20)) + // Checking times' values is a bit difficult since they are extremely precise. + XCTAssertNotNil(entries[0].info.modificationTime) + XCTAssertNotNil(entries[0].info.accessTime) + XCTAssertNotNil(entries[0].info.creationTime) + XCTAssertEqual(entries[0].info.hasStream, true) + XCTAssertEqual(entries[0].info.isEmpty, false) + XCTAssertEqual(entries[0].info.isAnti, false) + XCTAssertEqual(entries[0].info.crc, 4168830779) + XCTAssertEqual(entries[0].data, answerData) + + testData = try Constants.data(forTest: "test_minor_version_4", withType: SevenZipTests.testType) + entries = try SevenZipContainer.open(container: testData) + + XCTAssertEqual(entries.count, 1) + XCTAssertEqual(entries[0].info.name, "test2.answer") + XCTAssertEqual(entries[0].info.type, .regular) + XCTAssertEqual(entries[0].info.size, answerData.count) + XCTAssertEqual(entries[0].info.permissions, Permissions(rawValue: 420)) + XCTAssertEqual(entries[0].info.dosAttributes, DosAttributes(rawValue: 0x20)) + // Checking times' values is a bit difficult since they are extremely precise. + XCTAssertNotNil(entries[0].info.modificationTime) + XCTAssertNotNil(entries[0].info.accessTime) + XCTAssertNotNil(entries[0].info.creationTime) + XCTAssertEqual(entries[0].info.hasStream, true) + XCTAssertEqual(entries[0].info.isEmpty, false) + XCTAssertEqual(entries[0].info.isAnti, false) + XCTAssertEqual(entries[0].info.crc, 4168830779) + XCTAssertEqual(entries[0].data, answerData) + } + } diff --git a/Tests/Test Files b/Tests/Test Files index fe35c32f..36e6d8fa 160000 --- a/Tests/Test Files +++ b/Tests/Test Files @@ -1 +1 @@ -Subproject commit fe35c32fdab1aab6becabc1ade18408a42e32113 +Subproject commit 36e6d8fa8e7468c4572206f40f6fe8c10fbd7a3e diff --git a/Tests/ZipTests.swift b/Tests/ZipTests.swift index 77aa05b9..5561d72a 100644 --- a/Tests/ZipTests.swift +++ b/Tests/ZipTests.swift @@ -18,6 +18,11 @@ class ZipTests: XCTestCase { let testData = try Constants.data(forAnswer: "test6") XCTAssertThrowsError(try ZipContainer.open(container: testData)) } + + func testEmptyData() throws { + XCTAssertThrowsError(try ZipContainer.info(container: Data())) + XCTAssertThrowsError(try ZipContainer.open(container: Data())) + } func testBigContainer() throws { let testData = try Constants.data(forTest: "SWCompressionSourceCode", withType: ZipTests.testType) diff --git a/Tests/ZlibTests.swift b/Tests/ZlibTests.swift index d90369b3..273cb812 100644 --- a/Tests/ZlibTests.swift +++ b/Tests/ZlibTests.swift @@ -52,4 +52,8 @@ class ZlibTests: XCTestCase { XCTAssertThrowsError(try ZlibArchive.unarchive(archive: testData)) } + func testEmptyData() throws { + XCTAssertThrowsError(try ZlibArchive.unarchive(archive: Data())) + } + } diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 24a89f58..2685906b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -37,12 +37,18 @@ stages: WATCHOS_ACTIONS: 'clean build' WATCHOS_SIMULATOR: 'Apple Watch Series 4 - 44mm' UTILS_PW_XCF_FLAG: '--xcf' - # macosSwift54: - # imageName: 'macOS-11.3' - # DEVELOPER_DIR: '/Applications/Xcode_12.5.app' - # WATCHOS_ACTIONS: 'clean test' - # WATCHOS_SIMULATOR: 'Apple Watch Series 6 - 44mm' - # UTILS_PW_XCF_FLAG: '--xcf' + macosSwift54: + imageName: 'macOS-11' + DEVELOPER_DIR: '/Applications/Xcode_12.5.1.app' + WATCHOS_ACTIONS: 'clean test' + WATCHOS_SIMULATOR: 'Apple Watch Series 6 - 44mm' + UTILS_PW_XCF_FLAG: '--xcf' + macosSwift55: + imageName: 'macOS-11' + DEVELOPER_DIR: '/Applications/Xcode_13.0.app' + WATCHOS_ACTIONS: 'clean test' + WATCHOS_SIMULATOR: 'Apple Watch Series 6 - 44mm' + UTILS_PW_XCF_FLAG: '--xcf' pool: vmImage: $(imageName) steps: @@ -74,7 +80,10 @@ stages: containerImage: 'swift:5.3.3-bionic' linuxSwift54: imageName: 'ubuntu-18.04' - containerImage: 'swift:5.4.2-bionic' + containerImage: 'swift:5.4.3-bionic' + linuxSwift55: + imageName: 'ubuntu-18.04' + containerImage: 'swift:5.5-bionic' pool: vmImage: $(imageName) container: $[ variables['containerImage'] ] @@ -90,7 +99,10 @@ stages: matrix: windowsSwift54: imageName: 'windows-2019' - SWIFT_VERSION: '5.4.2' + SWIFT_VERSION: '5.4.3' + windowsSwift55: + imageName: 'windows-2019' + SWIFT_VERSION: '5.5' pool: vmImage: $(imageName) variables: