mirror of
https://github.com/tsolomko/SWCompression.git
synced 2026-06-23 14:56:41 +00:00
652 lines
19 KiB
Swift
652 lines
19 KiB
Swift
// Copyright (c) 2022 Timofey Solomko
|
|
// Licensed under MIT License
|
|
//
|
|
// See LICENSE for license information
|
|
|
|
#if os(Linux)
|
|
import CoreFoundation
|
|
#endif
|
|
|
|
import Foundation
|
|
import SWCompression
|
|
import SwiftCLI
|
|
|
|
enum Benchmarks: String, CaseIterable, ConvertibleFromString {
|
|
case unGzip = "un-gzip"
|
|
case unBz2 = "un-bz2"
|
|
case unLz4 = "un-lz4"
|
|
case unXz = "un-xz"
|
|
case compDeflate = "comp-deflate"
|
|
case compRatioDeflate = "comp-ratio-deflate"
|
|
case compBz2 = "comp-bz2"
|
|
case compRatioBz2 = "comp-ratio-bz2"
|
|
case compLz4 = "comp-lz4"
|
|
case compRatioLz4 = "comp-ratio-lz4"
|
|
case compLz4Bd = "comp-lz4-bd"
|
|
case compRatioLz4Bd = "comp-ratio-lz4-bd"
|
|
case info7z = "info-7z"
|
|
case infoTar = "info-tar"
|
|
case infoZip = "info-zip"
|
|
case createTar = "create-tar"
|
|
case readerTar = "reader-tar"
|
|
case writerTar = "writer-tar"
|
|
|
|
var titleName: String {
|
|
switch self {
|
|
case .unGzip:
|
|
return "GZip Unarchive"
|
|
case .unBz2:
|
|
return "BZip2 Decompress"
|
|
case .unLz4:
|
|
return "LZ4 Decompress"
|
|
case .unXz:
|
|
return "XZ Unarchive"
|
|
case .compDeflate:
|
|
return "Deflate Compress"
|
|
case .compRatioDeflate:
|
|
return "Deflate Compression Ratio"
|
|
case .compBz2:
|
|
return "BZip2 Compress"
|
|
case .compRatioBz2:
|
|
return "BZip2 Compression Ratio"
|
|
case .compLz4:
|
|
return "LZ4 Compress"
|
|
case .compRatioLz4:
|
|
return "LZ4 Compression Ratio"
|
|
case .compLz4Bd:
|
|
return "LZ4 Compress (dependent blocks)"
|
|
case .compRatioLz4Bd:
|
|
return "LZ4 Compression Ratio (dependent blocks)"
|
|
case .info7z:
|
|
return "7-Zip Info"
|
|
case .infoTar:
|
|
return "TAR Info"
|
|
case .infoZip:
|
|
return "ZIP Info"
|
|
case .createTar:
|
|
return "TAR Create"
|
|
case .readerTar:
|
|
return "TAR Reader"
|
|
case .writerTar:
|
|
return "TAR Writer"
|
|
}
|
|
}
|
|
|
|
func initialized(_ input: String) -> Benchmark {
|
|
switch self {
|
|
case .unGzip:
|
|
return UnGzip(input)
|
|
case .unBz2:
|
|
return UnBz2(input)
|
|
case .unLz4:
|
|
return UnLz4(input)
|
|
case .unXz:
|
|
return UnXz(input)
|
|
case .compDeflate:
|
|
return CompDeflate(input)
|
|
case .compRatioDeflate:
|
|
return CompRatioDeflate(input)
|
|
case .compBz2:
|
|
return CompBz2(input)
|
|
case .compRatioBz2:
|
|
return CompRatioBz2(input)
|
|
case .compLz4:
|
|
return CompLz4(input)
|
|
case .compRatioLz4:
|
|
return CompRatioLz4(input)
|
|
case .compLz4Bd:
|
|
return CompLz4Bd(input)
|
|
case .compRatioLz4Bd:
|
|
return CompRatioLz4Bd(input)
|
|
case .info7z:
|
|
return Info7z(input)
|
|
case .infoTar:
|
|
return InfoTar(input)
|
|
case .infoZip:
|
|
return InfoZip(input)
|
|
case .createTar:
|
|
return CreateTar(input)
|
|
case .readerTar:
|
|
return ReaderTar(input)
|
|
case .writerTar:
|
|
return WriterTar(input)
|
|
}
|
|
}
|
|
|
|
static var allBenchmarks: String {
|
|
return Self.allCases.map { $0.rawValue }.joined(separator: ", ")
|
|
}
|
|
}
|
|
|
|
struct UnGzip: Benchmark {
|
|
|
|
private let data: Data
|
|
private let size: Double
|
|
|
|
init(_ input: String) {
|
|
do {
|
|
let inputURL = URL(fileURLWithPath: input)
|
|
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
|
|
self.size = Double(self.data.count)
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
|
|
}
|
|
}
|
|
|
|
func measure() -> Double {
|
|
do {
|
|
let startTime = DispatchTime.now().uptimeNanoseconds
|
|
_ = try GzipArchive.unarchive(archive: self.data)
|
|
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
|
|
return self.size / timeElapsed
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotMeasure(Self.self, error))
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
struct UnBz2: Benchmark {
|
|
|
|
private let data: Data
|
|
private let size: Double
|
|
|
|
init(_ input: String) {
|
|
do {
|
|
let inputURL = URL(fileURLWithPath: input)
|
|
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
|
|
self.size = Double(self.data.count)
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
|
|
}
|
|
}
|
|
|
|
func measure() -> Double {
|
|
do {
|
|
let startTime = DispatchTime.now().uptimeNanoseconds
|
|
_ = try BZip2.decompress(data: self.data)
|
|
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
|
|
return self.size / timeElapsed
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotMeasure(Self.self, error))
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
struct UnLz4: Benchmark {
|
|
|
|
private let data: Data
|
|
private let size: Double
|
|
|
|
init(_ input: String) {
|
|
do {
|
|
let inputURL = URL(fileURLWithPath: input)
|
|
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
|
|
self.size = Double(self.data.count)
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
|
|
}
|
|
}
|
|
|
|
func measure() -> Double {
|
|
do {
|
|
let startTime = DispatchTime.now().uptimeNanoseconds
|
|
_ = try LZ4.decompress(data: self.data)
|
|
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
|
|
return self.size / timeElapsed
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotMeasure(Self.self, error))
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
struct UnXz: Benchmark {
|
|
|
|
private let data: Data
|
|
private let size: Double
|
|
|
|
init(_ input: String) {
|
|
do {
|
|
let inputURL = URL(fileURLWithPath: input)
|
|
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
|
|
self.size = Double(self.data.count)
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
|
|
}
|
|
}
|
|
|
|
func measure() -> Double {
|
|
do {
|
|
let startTime = DispatchTime.now().uptimeNanoseconds
|
|
_ = try XZArchive.unarchive(archive: self.data)
|
|
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
|
|
return self.size / timeElapsed
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotMeasure(Self.self, error))
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
struct CompDeflate: Benchmark {
|
|
|
|
private let data: Data
|
|
private let size: Double
|
|
|
|
init(_ input: String) {
|
|
do {
|
|
let inputURL = URL(fileURLWithPath: input)
|
|
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
|
|
self.size = Double(self.data.count)
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
|
|
}
|
|
}
|
|
|
|
func measure() -> Double {
|
|
let startTime = DispatchTime.now().uptimeNanoseconds
|
|
_ = Deflate.compress(data: self.data)
|
|
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
|
|
return self.size / timeElapsed
|
|
}
|
|
|
|
}
|
|
|
|
struct CompRatioDeflate: Benchmark {
|
|
|
|
let defaultIterationCount: Int = 1
|
|
private let data: Data
|
|
private let size: Double
|
|
|
|
init(_ input: String) {
|
|
do {
|
|
let inputURL = URL(fileURLWithPath: input)
|
|
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
|
|
self.size = Double(self.data.count)
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
|
|
}
|
|
}
|
|
|
|
func warmupIteration() { }
|
|
|
|
func measure() -> Double {
|
|
let outputData = Deflate.compress(data: self.data)
|
|
guard outputData.count > 0
|
|
else { swcompExit(.benchmarkCannotMeasureBadOutSize(Self.self)) }
|
|
return Double(self.size) / Double(outputData.count)
|
|
}
|
|
|
|
func format(_ value: Double) -> String {
|
|
return String(format: "%.3f", value)
|
|
}
|
|
|
|
}
|
|
|
|
struct CompBz2: Benchmark {
|
|
|
|
private let data: Data
|
|
private let size: Double
|
|
|
|
init(_ input: String) {
|
|
do {
|
|
let inputURL = URL(fileURLWithPath: input)
|
|
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
|
|
self.size = Double(self.data.count)
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
|
|
}
|
|
}
|
|
|
|
func measure() -> Double {
|
|
let startTime = DispatchTime.now().uptimeNanoseconds
|
|
_ = BZip2.compress(data: self.data)
|
|
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
|
|
return self.size / timeElapsed
|
|
}
|
|
|
|
}
|
|
|
|
struct CompRatioBz2: Benchmark {
|
|
|
|
let defaultIterationCount: Int = 1
|
|
private let data: Data
|
|
private let size: Double
|
|
|
|
init(_ input: String) {
|
|
do {
|
|
let inputURL = URL(fileURLWithPath: input)
|
|
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
|
|
self.size = Double(self.data.count)
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
|
|
}
|
|
}
|
|
|
|
func warmupIteration() { }
|
|
|
|
func measure() -> Double {
|
|
let outputData = BZip2.compress(data: self.data)
|
|
guard outputData.count > 0
|
|
else { swcompExit(.benchmarkCannotMeasureBadOutSize(Self.self)) }
|
|
return Double(self.size) / Double(outputData.count)
|
|
}
|
|
|
|
func format(_ value: Double) -> String {
|
|
return String(format: "%.3f", value)
|
|
}
|
|
|
|
}
|
|
|
|
struct CompLz4: Benchmark {
|
|
|
|
private let data: Data
|
|
private let size: Double
|
|
|
|
init(_ input: String) {
|
|
do {
|
|
let inputURL = URL(fileURLWithPath: input)
|
|
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
|
|
self.size = Double(self.data.count)
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
|
|
}
|
|
}
|
|
|
|
func measure() -> Double {
|
|
let startTime = DispatchTime.now().uptimeNanoseconds
|
|
_ = LZ4.compress(data: self.data)
|
|
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
|
|
return self.size / timeElapsed
|
|
}
|
|
|
|
}
|
|
|
|
struct CompRatioLz4: Benchmark {
|
|
|
|
let defaultIterationCount: Int = 1
|
|
private let data: Data
|
|
private let size: Double
|
|
|
|
init(_ input: String) {
|
|
do {
|
|
let inputURL = URL(fileURLWithPath: input)
|
|
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
|
|
self.size = Double(self.data.count)
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
|
|
}
|
|
}
|
|
|
|
func warmupIteration() { }
|
|
|
|
func measure() -> Double {
|
|
let outputData = LZ4.compress(data: self.data)
|
|
guard outputData.count > 0
|
|
else { swcompExit(.benchmarkCannotMeasureBadOutSize(Self.self)) }
|
|
return Double(self.size) / Double(outputData.count)
|
|
}
|
|
|
|
func format(_ value: Double) -> String {
|
|
return String(format: "%.3f", value)
|
|
}
|
|
|
|
}
|
|
|
|
struct CompLz4Bd: Benchmark {
|
|
|
|
private let data: Data
|
|
private let size: Double
|
|
|
|
init(_ input: String) {
|
|
do {
|
|
let inputURL = URL(fileURLWithPath: input)
|
|
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
|
|
self.size = Double(self.data.count)
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
|
|
}
|
|
}
|
|
|
|
func measure() -> Double {
|
|
let startTime = DispatchTime.now().uptimeNanoseconds
|
|
_ = LZ4.compress(data: self.data, independentBlocks: false, blockChecksums: false, contentChecksum: true,
|
|
contentSize: false, blockSize: 4 * 1024 * 1024, dictionary: nil, dictionaryID: nil)
|
|
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
|
|
return self.size / timeElapsed
|
|
}
|
|
|
|
}
|
|
|
|
struct CompRatioLz4Bd: Benchmark {
|
|
|
|
let defaultIterationCount: Int = 1
|
|
private let data: Data
|
|
private let size: Double
|
|
|
|
init(_ input: String) {
|
|
do {
|
|
let inputURL = URL(fileURLWithPath: input)
|
|
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
|
|
self.size = Double(self.data.count)
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
|
|
}
|
|
}
|
|
|
|
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)
|
|
guard outputData.count > 0
|
|
else { swcompExit(.benchmarkCannotMeasureBadOutSize(Self.self)) }
|
|
return Double(self.size) / Double(outputData.count)
|
|
}
|
|
|
|
func format(_ value: Double) -> String {
|
|
return String(format: "%.3f", value)
|
|
}
|
|
|
|
}
|
|
|
|
struct Info7z: Benchmark {
|
|
|
|
private let data: Data
|
|
private let size: Double
|
|
|
|
init(_ input: String) {
|
|
do {
|
|
let inputURL = URL(fileURLWithPath: input)
|
|
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
|
|
self.size = Double(self.data.count)
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
|
|
}
|
|
}
|
|
|
|
func measure() -> Double {
|
|
do {
|
|
let startTime = DispatchTime.now().uptimeNanoseconds
|
|
_ = try SevenZipContainer.info(container: self.data)
|
|
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
|
|
return self.size / timeElapsed
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotMeasure(Self.self, error))
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
struct InfoTar: Benchmark {
|
|
|
|
private let data: Data
|
|
private let size: Double
|
|
|
|
init(_ input: String) {
|
|
do {
|
|
let inputURL = URL(fileURLWithPath: input)
|
|
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
|
|
self.size = Double(self.data.count)
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
|
|
}
|
|
}
|
|
|
|
func measure() -> Double {
|
|
do {
|
|
let startTime = DispatchTime.now().uptimeNanoseconds
|
|
_ = try TarContainer.info(container: self.data)
|
|
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
|
|
return self.size / timeElapsed
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotMeasure(Self.self, error))
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
struct InfoZip: Benchmark {
|
|
|
|
private let data: Data
|
|
private let size: Double
|
|
|
|
init(_ input: String) {
|
|
do {
|
|
let inputURL = URL(fileURLWithPath: input)
|
|
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
|
|
self.size = Double(self.data.count)
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
|
|
}
|
|
}
|
|
|
|
func measure() -> Double {
|
|
do {
|
|
let startTime = DispatchTime.now().uptimeNanoseconds
|
|
_ = try ZipContainer.info(container: self.data)
|
|
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
|
|
return self.size / timeElapsed
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotMeasure(Self.self, error))
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
struct CreateTar: Benchmark {
|
|
|
|
private let entries: [TarEntry]
|
|
private let size: Double
|
|
|
|
init(_ input: String) {
|
|
do {
|
|
self.entries = try TarEntry.createEntries(input, false)
|
|
self.size = try Double(URL(fileURLWithPath: input).directorySize())
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
|
|
}
|
|
}
|
|
|
|
func measure() -> Double {
|
|
let startTime = DispatchTime.now().uptimeNanoseconds
|
|
_ = TarContainer.create(from: self.entries)
|
|
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
|
|
return self.size / timeElapsed
|
|
}
|
|
|
|
}
|
|
|
|
struct ReaderTar: Benchmark {
|
|
|
|
private let url: URL
|
|
private let size: Double
|
|
|
|
init(_ input: String) {
|
|
do {
|
|
self.url = URL(fileURLWithPath: input)
|
|
let resourceValues = try self.url.resourceValues(forKeys: [.fileSizeKey])
|
|
if let fileSize = resourceValues.fileSize {
|
|
self.size = Double(fileSize)
|
|
} else {
|
|
swcompExit(.benchmarkReaderTarNoInputSize(input))
|
|
}
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
|
|
}
|
|
}
|
|
|
|
func measure() -> Double {
|
|
do {
|
|
let handle = try FileHandle(forReadingFrom: self.url)
|
|
let startTime = DispatchTime.now().uptimeNanoseconds
|
|
var reader = TarReader(fileHandle: handle)
|
|
var isFinished = false
|
|
var infos = [TarEntryInfo]()
|
|
while !isFinished {
|
|
isFinished = try reader.process { (entry: TarEntry?) -> Bool in
|
|
guard entry != nil
|
|
else { return true }
|
|
infos.append(entry!.info)
|
|
return false
|
|
}
|
|
}
|
|
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
|
|
try handle.closeCompat()
|
|
return self.size / timeElapsed
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotMeasure(Self.self, error))
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
struct WriterTar: Benchmark {
|
|
|
|
private let entries: [TarEntry]
|
|
private let size: Double
|
|
|
|
init(_ input: String) {
|
|
do {
|
|
self.entries = try TarEntry.createEntries(input, false)
|
|
self.size = try Double(URL(fileURLWithPath: input).directorySize())
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
|
|
}
|
|
}
|
|
|
|
func measure() -> Double {
|
|
do {
|
|
let url = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString, isDirectory: false)
|
|
try "".write(to: url, atomically: true, encoding: .utf8)
|
|
let handle = try FileHandle(forWritingTo: url)
|
|
let startTime = DispatchTime.now().uptimeNanoseconds
|
|
var writer = TarWriter(fileHandle: handle)
|
|
for entry in self.entries {
|
|
try writer.append(entry)
|
|
}
|
|
try writer.finalize()
|
|
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
|
|
try handle.closeCompat()
|
|
try FileManager.default.removeItem(at: url)
|
|
return self.size / timeElapsed
|
|
} catch let error {
|
|
swcompExit(.benchmarkCannotMeasure(Self.self, error))
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
}
|