Merge branch 'develop'

This commit is contained in:
Timofey Solomko
2026-05-22 19:46:17 +08:00
169 changed files with 1400 additions and 1368 deletions
+3 -48
View File
@@ -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/
+3 -3
View File
@@ -3,11 +3,11 @@ sourcekitten_sourcefile: docs.json
clean: false
author: Timofey Solomko
module: SWCompression
module_version: 4.8.7
copyright: '© 2024 Timofey Solomko'
module_version: 4.9.0
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.7
github_file_prefix: https://github.com/tsolomko/SWCompression/tree/4.9.0
theme: fullwidth
custom_categories:
+32 -2
View File
@@ -1,5 +1,35 @@
# Changelog
## 4.9.0
- Swift versions from 5.3 up to and including 5.8 are no longer supported.
- Cocoapods and Carthage installation methods are no longer supported.
- Enabled visionOS platform for SPM.
- Increased minimum deployment versions for Darwin platforms: macOS from 10.13 to 14.0, iOS from 11.0 to 17.0, tvOS from
11.0 to 17.0, and watchOS from 4.0 to 10.0.
- Increased the lowest required version of BitByteData dependency to 2.1.
- Added `BZip2.multiDecompress` function.
- Improved performance of BZip2 compression and decompression.
- Slighlty improved performance of LZMA/LZMA2 decompression.
- Improved performance of Deflate compression and decompression.
- Fixed potential crashes when processing truncated BZip2 archives.
- Fixed potential crashes when processing truncated Zlib archives.
- Fixed potential crashes when processing truncated or corrupted data compressed with Deflate.
- Fixed incorrect parsing of LZMA dictionary sizes larger than 2<sup>24</sup>-1 in 7-Zip containers (PR #61 by @analytical-engines and @nekonya).
- swcomp changes:
- SPM target is now enabled only on macOS.
- Added `-m`, `--multi-decompress` option to `bz2` command.
- Benchmark metadatas are now printed in a standard order.
- Benchmarking warmup iteration results are now printed and saved into a file.
- Benchmarking raw data is now saved into a file.
- Improved benchmarking output for multi-way comparsions.
- Added `--print-uuid` option to `benchmark show` command.
- Added `--metadata-only` option to `benchmark show` command.
- Added `--self-compare` option to `benchmark show` command.
- Added `benchmark remove-run` command.
- Updated benchmark results file format.
- Added `benchmark convert` command.
## 4.8.7
- Pinned BitByteData version to 2.0.4.
@@ -7,8 +37,8 @@
## 4.8.6
- Swift 5.2 is no longer supported.
- Enable visionOS deployment target (only for Carthage users).
- Add PrivacyInfo file.
- Enabled visionOS deployment target (only for Carthage users).
- Added PrivacyInfo file.
- Fixed a documentation error in `TarEntryInfo.deviceMinorNumber`.
## 4.8.5
-1
View File
@@ -1 +0,0 @@
github "tsolomko/BitByteData" ~> 2.0.0
+1 -1
View File
@@ -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
+15 -18
View File
@@ -1,15 +1,14 @@
// swift-tools-version:5.3
// swift-tools-version:5.9
import PackageDescription
let package = Package(
var package = Package(
name: "SWCompression",
platforms: [
.macOS(.v10_13),
.iOS(.v11),
.tvOS(.v11),
.watchOS(.v4),
// TODO: Enable after upgrading to Swift 5.9.
// .visionOS(.v1)
.macOS(.v14),
.iOS(.v17),
.tvOS(.v17),
.watchOS(.v10),
.visionOS(.v1)
],
products: [
.library(
@@ -17,10 +16,7 @@ let package = Package(
targets: ["SWCompression"]),
],
dependencies: [
.package(name: "BitByteData", url: "https://github.com/tsolomko/BitByteData",
.exact("2.0.4")),
.package(name: "SwiftCLI", url: "https://github.com/jakeheis/SwiftCLI",
from: "6.0.0"),
.package(url: "https://github.com/tsolomko/BitByteData", from: "2.1.0"),
],
targets: [
.target(
@@ -30,12 +26,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(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
+9 -72
View File
@@ -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.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)
@@ -32,11 +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.
### Swift Package Manager
__Note:__ SWCompression versions 4.8.6 and earlier were also made available via CocoaPods or Carthage.
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
@@ -45,7 +46,7 @@ let package = Package(
name: "PackageName",
dependencies: [
.package(name: "SWCompression", url: "https://github.com/tsolomko/SWCompression.git",
from: "4.8.0")
from: "4.9.0")
],
targets: [
.target(
@@ -58,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
@@ -157,7 +105,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.
@@ -171,15 +119,11 @@ 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:
```bash
./utils.py download-bbd-macos
git submodule update --init --recursive
cd "Tests/Test Files"
cp gitattributes-copy .gitattributes
@@ -187,15 +131,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
[separate repository](https://github.com/tsolomko/SWCompression-Test-Files), using Git LFS. There are two reasons for
These commands will download the files used in tests which 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
Xcode don't always work well with git-lfs-enabled repositories. To prevent any potential problems test files were moved
-95
View File
@@ -1,95 +0,0 @@
Pod::Spec.new do |s|
s.name = "SWCompression"
s.version = "4.8.7"
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 = "11.0"
s.osx.deployment_target = "10.13"
s.tvos.deployment_target = "11.0"
s.watchos.deployment_target = "4.0"
# s.visionos.deployment_target = "1.0"
s.swift_versions = ["5"]
s.dependency "BitByteData", "~> 2.0"
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
end
+3 -3
View File
@@ -15,10 +15,10 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>4.8.7</string>
<string>4.9.0</string>
<key>CFBundleVersion</key>
<string>92</string>
<string>93</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2024 Timofey Solomko</string>
<string>Copyright © 2026 Timofey Solomko</string>
</dict>
</plist>
@@ -15,8 +15,8 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>4.8.7</string>
<string>4.9.0</string>
<key>CFBundleVersion</key>
<string>92</string>
<string>93</string>
</dict>
</plist>
+96 -122
View File
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objectVersion = 60;
objects = {
/* Begin PBXBuildFile section */
@@ -64,7 +64,6 @@
0686A6451FA4C47300E89C9E /* FileSystemType+Zip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0686A6441FA4C47300E89C9E /* FileSystemType+Zip.swift */; };
068D070221368242002A6C3B /* DeltaFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068D070121368242002A6C3B /* DeltaFilter.swift */; };
068D070421368617002A6C3B /* test_delta_filter.7z in Resources */ = {isa = PBXBuildFile; fileRef = 068D070321368617002A6C3B /* test_delta_filter.7z */; };
06912BD21F5C8F3D0070BB60 /* Deflate+Lengths.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06912BD11F5C8F3D0070BB60 /* Deflate+Lengths.swift */; };
0694A74D1F7C0DF00023BC99 /* BurrowsWheeler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0694A74C1F7C0DF00023BC99 /* BurrowsWheeler.swift */; };
06955E491F65C761004D5D79 /* BZip2+Lengths.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06955E481F65C761004D5D79 /* BZip2+Lengths.swift */; };
069864CA21403CE700755D9B /* test_sha256.xz in Resources */ = {isa = PBXBuildFile; fileRef = 069864C921403CE700755D9B /* test_sha256.xz */; };
@@ -96,7 +95,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 */; };
@@ -222,6 +221,7 @@
E609D7B6271AFAA400068E79 /* test_small_dict_B5_BD.lz4 in Resources */ = {isa = PBXBuildFile; fileRef = E609D7B3271AFAA400068E79 /* test_small_dict_B5_BD.lz4 */; };
E609D7B7271AFAA400068E79 /* test_small_dict_B5.lz4 in Resources */ = {isa = PBXBuildFile; fileRef = E609D7B4271AFAA400068E79 /* test_small_dict_B5.lz4 */; };
E60CBA8A26AF379200A20130 /* test_only_dir_header.tar in Resources */ = {isa = PBXBuildFile; fileRef = E60CBA8926AF379200A20130 /* test_only_dir_header.tar */; };
E61055CF2FB3546600A684F6 /* test_lzma_big_dict.7z in Resources */ = {isa = PBXBuildFile; fileRef = E61055CE2FB3546600A684F6 /* test_lzma_big_dict.7z */; };
E631055927084D5D006EACC3 /* LZ4+Compress.swift in Sources */ = {isa = PBXBuildFile; fileRef = E631055827084D5D006EACC3 /* LZ4+Compress.swift */; };
E6421E8F271B54C6000359B6 /* test_7z_lz4.7z in Resources */ = {isa = PBXBuildFile; fileRef = E6421E8E271B54C6000359B6 /* test_7z_lz4.7z */; };
E644E11E2833AB2400EEFBAD /* minimal.gz in Resources */ = {isa = PBXBuildFile; fileRef = E644E11D2833AB2400EEFBAD /* minimal.gz */; };
@@ -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 */; };
@@ -263,6 +262,10 @@
E652FF002700A028006BC312 /* test_B6.lz4 in Resources */ = {isa = PBXBuildFile; fileRef = E652FEF42700A028006BC312 /* test_B6.lz4 */; };
E652FF012700A028006BC312 /* test_B5.lz4 in Resources */ = {isa = PBXBuildFile; fileRef = E652FEF52700A028006BC312 /* test_B5.lz4 */; };
E652FF022700A028006BC312 /* test_B4.lz4 in Resources */ = {isa = PBXBuildFile; fileRef = E652FEF62700A028006BC312 /* test_B4.lz4 */; };
E66D884E2F4F529100707F23 /* DeflateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E66D884D2F4F529100707F23 /* DeflateTests.swift */; };
E66D88612F4F53AB00707F23 /* test6.deflate in Resources */ = {isa = PBXBuildFile; fileRef = E66D885D2F4F53AB00707F23 /* test6.deflate */; };
E66D88622F4F53AB00707F23 /* test8.deflate in Resources */ = {isa = PBXBuildFile; fileRef = E66D885E2F4F53AB00707F23 /* test8.deflate */; };
E66D88632F4F53AB00707F23 /* test9.deflate in Resources */ = {isa = PBXBuildFile; fileRef = E66D885F2F4F53AB00707F23 /* test9.deflate */; };
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 */; };
E6791E1826FD001A003852A9 /* DataError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6791E1726FD001A003852A9 /* DataError.swift */; };
@@ -276,6 +279,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 */; };
@@ -367,7 +371,6 @@
0686A6441FA4C47300E89C9E /* FileSystemType+Zip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileSystemType+Zip.swift"; sourceTree = "<group>"; };
068D070121368242002A6C3B /* DeltaFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeltaFilter.swift; sourceTree = "<group>"; };
068D070321368617002A6C3B /* test_delta_filter.7z */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_delta_filter.7z; sourceTree = "<group>"; };
06912BD11F5C8F3D0070BB60 /* Deflate+Lengths.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deflate+Lengths.swift"; sourceTree = "<group>"; };
0694A74C1F7C0DF00023BC99 /* BurrowsWheeler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BurrowsWheeler.swift; sourceTree = "<group>"; };
06955E481F65C761004D5D79 /* BZip2+Lengths.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BZip2+Lengths.swift"; sourceTree = "<group>"; };
069864C921403CE700755D9B /* test_sha256.xz */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_sha256.xz; sourceTree = "<group>"; };
@@ -396,7 +399,7 @@
06BB8F2F1E0C2C720079FB9E /* LZMA2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LZMA2.swift; sourceTree = "<group>"; };
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 = "<group>"; };
06CC3FE11F8AAF3100BD576D /* ByteReader+XZ.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ByteReader+XZ.swift"; sourceTree = "<group>"; };
06CC3FE11F8AAF3100BD576D /* LittleEndianByteReader+XZ.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LittleEndianByteReader+XZ.swift"; sourceTree = "<group>"; };
06CDFC9D1F111D2600292758 /* Deflate+Compress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deflate+Compress.swift"; sourceTree = "<group>"; };
06CDFCA21F111D6C00292758 /* BZip2Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BZip2Error.swift; sourceTree = "<group>"; };
06CDFCA71F111D9700292758 /* DeflateError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeflateError.swift; sourceTree = "<group>"; };
@@ -523,6 +526,7 @@
E609D7B3271AFAA400068E79 /* test_small_dict_B5_BD.lz4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_small_dict_B5_BD.lz4; sourceTree = "<group>"; };
E609D7B4271AFAA400068E79 /* test_small_dict_B5.lz4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_small_dict_B5.lz4; sourceTree = "<group>"; };
E60CBA8926AF379200A20130 /* test_only_dir_header.tar */ = {isa = PBXFileReference; lastKnownFileType = archive.tar; path = test_only_dir_header.tar; sourceTree = "<group>"; };
E61055CE2FB3546600A684F6 /* test_lzma_big_dict.7z */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_lzma_big_dict.7z; sourceTree = "<group>"; };
E631055827084D5D006EACC3 /* LZ4+Compress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LZ4+Compress.swift"; sourceTree = "<group>"; };
E631055A27086132006EACC3 /* SWCompression.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = SWCompression.xctestplan; path = SWCompression.xcodeproj/SWCompression.xctestplan; sourceTree = SOURCE_ROOT; };
E6421E8E271B54C6000359B6 /* test_7z_lz4.7z */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_7z_lz4.7z; sourceTree = "<group>"; };
@@ -531,7 +535,6 @@
E64F71BB26AAFD6A008BB308 /* Data+Tar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Tar.swift"; sourceTree = "<group>"; };
E6522FB32789AF9600026B56 /* TarReaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TarReaderTests.swift; sourceTree = "<group>"; };
E6522FB52789AFA600026B56 /* TarWriterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TarWriterTests.swift; sourceTree = "<group>"; };
E6522FBC2789C4A300026B56 /* FileHandle+CloseCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileHandle+CloseCompat.swift"; sourceTree = "<group>"; };
E652D8F2263D670900FC229B /* test_nonstandard_runlength.bz2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_nonstandard_runlength.bz2; sourceTree = "<group>"; };
E652D8F4263D678000FC229B /* test_nonstandard_runlength.answer */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_nonstandard_runlength.answer; sourceTree = "<group>"; };
E652FEC127007E78006BC312 /* test3.lz4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = test3.lz4; sourceTree = "<group>"; };
@@ -565,6 +568,10 @@
E652FEF42700A028006BC312 /* test_B6.lz4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_B6.lz4; sourceTree = "<group>"; };
E652FEF52700A028006BC312 /* test_B5.lz4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_B5.lz4; sourceTree = "<group>"; };
E652FEF62700A028006BC312 /* test_B4.lz4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_B4.lz4; sourceTree = "<group>"; };
E66D884D2F4F529100707F23 /* DeflateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeflateTests.swift; sourceTree = "<group>"; };
E66D885D2F4F53AB00707F23 /* test6.deflate */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = test6.deflate; sourceTree = "<group>"; };
E66D885E2F4F53AB00707F23 /* test8.deflate */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = test8.deflate; sourceTree = "<group>"; };
E66D885F2F4F53AB00707F23 /* test9.deflate */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = test9.deflate; sourceTree = "<group>"; };
E66F36232538726E00076A6E /* test_empty.lzma */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_empty.lzma; sourceTree = "<group>"; };
E66F362B2538E2B700076A6E /* test_empty.zlib */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_empty.zlib; sourceTree = "<group>"; };
E6791E1726FD001A003852A9 /* DataError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataError.swift; sourceTree = "<group>"; };
@@ -594,6 +601,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
E6DCC9D52F30DA020042AE2E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
E6DCC9D82F30DA080042AE2E /* BitByteData in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -633,7 +648,6 @@
children = (
061FCE251DBCC4BE0052F7BE /* Deflate.swift */,
06CDFC9D1F111D2600292758 /* Deflate+Compress.swift */,
06912BD11F5C8F3D0070BB60 /* Deflate+Lengths.swift */,
0631CB051F81109E00A2E0AB /* Deflate+Constants.swift */,
06CDFCA71F111D9700292758 /* DeflateError.swift */,
);
@@ -676,7 +690,7 @@
061C06201F0E89D900832F0C /* XZError.swift */,
065146221FA8E20C007F83F4 /* XZBlock.swift */,
0651461D1FA8E004007F83F4 /* XZStreamHeader.swift */,
06CC3FE11F8AAF3100BD576D /* ByteReader+XZ.swift */,
06CC3FE11F8AAF3100BD576D /* LittleEndianByteReader+XZ.swift */,
06516B75213938B000C9823B /* Sha256.swift */,
);
path = XZ;
@@ -695,9 +709,9 @@
06BE1ABE1DB410F100EE0F59 = {
isa = PBXGroup;
children = (
E6E49D502BD507D700BFC756 /* PrivacyInfo.xcprivacy */,
06BE1ACA1DB410F100EE0F59 /* Sources */,
06F065A01FFB763300312A82 /* Tests */,
E6E49D502BD507D700BFC756 /* PrivacyInfo.xcprivacy */,
E631055A27086132006EACC3 /* SWCompression.xctestplan */,
06BE1AC91DB410F100EE0F59 /* Products */,
);
@@ -835,6 +849,7 @@
06F065AC1FFB763300312A82 /* BZip2CompressionTests.swift */,
06F065A51FFB763300312A82 /* BZip2Tests.swift */,
06F065A21FFB763300312A82 /* DeflateCompressionTests.swift */,
E66D884D2F4F529100707F23 /* DeflateTests.swift */,
06F065A71FFB763300312A82 /* GzipTests.swift */,
06F065AB1FFB763300312A82 /* LzmaTests.swift */,
E6B18E84270884E300F9AB99 /* LZ4CompressionTests.swift */,
@@ -844,7 +859,6 @@
E6522FB52789AFA600026B56 /* TarWriterTests.swift */,
06F065A61FFB763300312A82 /* TarTests.swift */,
E6522FB32789AF9600026B56 /* TarReaderTests.swift */,
E6522FBC2789C4A300026B56 /* FileHandle+CloseCompat.swift */,
06F065A91FFB763300312A82 /* XzTests.swift */,
06F065A41FFB763300312A82 /* ZipTests.swift */,
06D42DA62067B71000C1A98B /* TestZipExtraField.swift */,
@@ -861,6 +875,7 @@
06F065F61FFB763300312A82 /* 7z */,
06F065AE1FFB763300312A82 /* Answers */,
06F066081FFB763300312A82 /* BZip2 */,
E66D88602F4F53AB00707F23 /* Deflate */,
06F065B81FFB763300312A82 /* GZip */,
06F065F11FFB763300312A82 /* LZMA */,
E6791E1F26FD04D0003852A9 /* LZ4 */,
@@ -1033,6 +1048,7 @@
06F066011FFB763300312A82 /* test_7z_bzip2.7z */,
06F066021FFB763300312A82 /* test_7z_deflate.7z */,
E6421E8E271B54C6000359B6 /* test_7z_lz4.7z */,
E61055CE2FB3546600A684F6 /* test_lzma_big_dict.7z */,
06F066041FFB763300312A82 /* test_empty_file.7z */,
06F066051FFB763300312A82 /* test_empty_dir.7z */,
06F066061FFB763300312A82 /* test_win.7z */,
@@ -1063,6 +1079,16 @@
path = BZip2;
sourceTree = "<group>";
};
E66D88602F4F53AB00707F23 /* Deflate */ = {
isa = PBXGroup;
children = (
E66D885D2F4F53AB00707F23 /* test6.deflate */,
E66D885E2F4F53AB00707F23 /* test8.deflate */,
E66D885F2F4F53AB00707F23 /* test9.deflate */,
);
path = Deflate;
sourceTree = "<group>";
};
E6791E1926FD0070003852A9 /* LZ4 */ = {
isa = PBXGroup;
children = (
@@ -1128,10 +1154,12 @@
buildPhases = (
06BE1AC31DB410F100EE0F59 /* Sources */,
E6E49D512BD5081700BFC756 /* CopyFiles */,
E6DCC9D52F30DA020042AE2E /* Frameworks */,
);
buildRules = (
);
dependencies = (
E6DCC9DA2F30DB4E0042AE2E /* PBXTargetDependency */,
);
name = SWCompression;
productName = SWCompression;
@@ -1145,7 +1173,6 @@
06F065911FFB761600312A82 /* Sources */,
06F065921FFB761600312A82 /* Frameworks */,
06F065931FFB761600312A82 /* Resources */,
062BE32F1FFBADB300343CAD /* Copy BitByteData.framework into test bundle */,
);
buildRules = (
);
@@ -1165,7 +1192,7 @@
attributes = {
BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1530;
LastUpgradeCheck = 2620;
ORGANIZATIONNAME = "Timofey Solomko";
TargetAttributes = {
06BE1AC71DB410F100EE0F59 = {
@@ -1179,7 +1206,7 @@
};
};
buildConfigurationList = 06BE1AC21DB410F100EE0F59 /* Build configuration list for PBXProject "SWCompression" */;
compatibilityVersion = "Xcode 11.4";
compatibilityVersion = "Xcode 15.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@@ -1187,6 +1214,9 @@
Base,
);
mainGroup = 06BE1ABE1DB410F100EE0F59;
packageReferences = (
E6DCC9D12F30D7610042AE2E /* XCRemoteSwiftPackageReference "BitByteData" */,
);
productRefGroup = 06BE1AC91DB410F100EE0F59 /* Products */;
projectDirPath = "";
projectRoot = "";
@@ -1258,6 +1288,7 @@
06F066671FFB763400312A82 /* test2.7z in Resources */,
062D591421248758003543BD /* test_delta_filter.xz in Resources */,
0698B10B2104E11200A7C551 /* test_gnu_inc_format.tar in Resources */,
E61055CF2FB3546600A684F6 /* test_lzma_big_dict.7z in Resources */,
06F0661F1FFB763300312A82 /* test3.answer in Resources */,
06F066281FFB763400312A82 /* test5.gz in Resources */,
064D01AC20FD077D00CAE058 /* test_unicode_ustar.tar in Resources */,
@@ -1345,6 +1376,9 @@
06F066581FFB763400312A82 /* full_test.tar in Resources */,
E6EDD6A926F7767F00884532 /* test_minor_version_4.7z in Resources */,
E652FEFB2700A028006BC312 /* test_B1234_BD.lz4 in Resources */,
E66D88612F4F53AB00707F23 /* test6.deflate in Resources */,
E66D88622F4F53AB00707F23 /* test8.deflate in Resources */,
E66D88632F4F53AB00707F23 /* test9.deflate in Resources */,
06F0662B1FFB763400312A82 /* test3.gz in Resources */,
E652FEF72700A028006BC312 /* test_B7.lz4 in Resources */,
06F0664C1FFB763400312A82 /* test_pax.tar in Resources */,
@@ -1358,25 +1392,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-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";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
06BE1AC31DB410F100EE0F59 /* Sources */ = {
isa = PBXSourcesBuildPhase;
@@ -1432,7 +1447,6 @@
0631CB061F81109E00A2E0AB /* Deflate+Constants.swift in Sources */,
E648151D26A889B8009261F0 /* TarHeader.swift in Sources */,
06D95AC11F4C54E0006B46AC /* BZip2+Compress.swift in Sources */,
06912BD21F5C8F3D0070BB60 /* Deflate+Lengths.swift in Sources */,
06A9606A1F1E7E0D0078E6D1 /* 7zSubstreamInfo.swift in Sources */,
E6C4150726FE230A00F9D36F /* XxHash32.swift in Sources */,
0694A74D1F7C0DF00023BC99 /* BurrowsWheeler.swift in Sources */,
@@ -1455,7 +1469,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 */,
@@ -1491,6 +1505,7 @@
buildActionMask = 2147483647;
files = (
E6522FB62789AFA600026B56 /* TarWriterTests.swift in Sources */,
E66D884E2F4F529100707F23 /* DeflateTests.swift in Sources */,
06F066151FFB763300312A82 /* ZlibTests.swift in Sources */,
06F066141FFB763300312A82 /* DeflateCompressionTests.swift in Sources */,
E6791E3326FD05EC003852A9 /* LZ4Tests.swift in Sources */,
@@ -1506,7 +1521,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 */,
@@ -1521,6 +1535,10 @@
target = 06BE1AC71DB410F100EE0F59 /* SWCompression */;
targetProxy = 06F0659B1FFB761600312A82 /* PBXContainerItemProxy */;
};
E6DCC9DA2F30DB4E0042AE2E /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
productRef = E6DCC9D92F30DB4E0042AE2E /* BitByteData */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
@@ -1548,25 +1566,13 @@
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CURRENT_PROJECT_VERSION = 92;
CURRENT_PROJECT_VERSION = 93;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
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_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=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\"";
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -1575,22 +1581,18 @@
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 = 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";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TVOS_DEPLOYMENT_TARGET = 11.0;
TVOS_DEPLOYMENT_TARGET = 17.6;
VERSIONING_SYSTEM = "apple-generic";
WATCHOS_DEPLOYMENT_TARGET = 4.0;
XROS_DEPLOYMENT_TARGET = 1.0;
WATCHOS_DEPLOYMENT_TARGET = 10.6;
XROS_DEPLOYMENT_TARGET = 1.3;
};
name = Debug;
};
@@ -1618,24 +1620,12 @@
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CURRENT_PROJECT_VERSION = 92;
CURRENT_PROJECT_VERSION = 93;
DEAD_CODE_STRIPPING = YES;
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_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=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\"";
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
@@ -1643,21 +1633,17 @@
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;
OTHER_CODE_SIGN_FLAGS = "--deep";
OTHER_LDFLAGS = (
"-framework",
BitByteData,
);
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
MACOSX_DEPLOYMENT_TARGET = 14.6;
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;
TVOS_DEPLOYMENT_TARGET = 11.0;
TVOS_DEPLOYMENT_TARGET = 17.6;
VERSIONING_SYSTEM = "apple-generic";
WATCHOS_DEPLOYMENT_TARGET = 4.0;
XROS_DEPLOYMENT_TARGET = 1.0;
WATCHOS_DEPLOYMENT_TARGET = 10.6;
XROS_DEPLOYMENT_TARGET = 1.3;
};
name = Release;
};
@@ -1667,24 +1653,13 @@
APPLICATION_EXTENSION_API_ONLY = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 92;
DYLIB_CURRENT_VERSION = 93;
DYLIB_INSTALL_NAME_BASE = "@rpath";
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;
@@ -1697,24 +1672,13 @@
APPLICATION_EXTENSION_API_ONLY = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 92;
DYLIB_CURRENT_VERSION = 93;
DYLIB_INSTALL_NAME_BASE = "@rpath";
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;
@@ -1726,13 +1690,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)";
};
@@ -1743,13 +1700,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)";
};
@@ -1786,6 +1736,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.1.0;
};
};
/* 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 */;
}
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1530"
LastUpgradeVersion = "2620"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
extension SevenZipCoder: Equatable {
static func == (lhs: SevenZipCoder, rhs: SevenZipCoder) -> Bool {
+1 -2
View File
@@ -1,9 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
import BitByteData
class SevenZipCoder {
+1 -2
View File
@@ -1,9 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
import BitByteData
class SevenZipCoderInfo {
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// 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.
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+5 -17
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
@@ -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,
@@ -172,7 +164,7 @@ class SevenZipFolder {
else { throw LZMAError.wrongProperties }
var dictionarySize = 0
for i in 1..<4 {
for i in 1..<5 {
dictionarySize |= properties[i].toInt() << (8 * (i - 1))
}
let lzmaProperties = try LZMAProperties(lzmaByte: properties[0], dictionarySize)
@@ -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 {
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -2
View File
@@ -1,9 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
import BitByteData
class SevenZipPackInfo {
+1 -2
View File
@@ -1,9 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
import BitByteData
struct SevenZipProperty {
+1 -2
View File
@@ -1,9 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
import BitByteData
class SevenZipStreamInfo {
+1 -2
View File
@@ -1,9 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
import BitByteData
class SevenZipSubstreamInfo {
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
extension CompressionMethod {
init(_ coderID: [UInt8]) {
+1 -2
View File
@@ -1,9 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
import BitByteData
extension MsbBitReader {
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
extension BZip2 {
/// Represents the size of the blocks in which data is split during BZip2 compression.
+67 -50
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
@@ -41,17 +41,18 @@ 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'.
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
for i in stride(from: data.startIndex, to: data.endIndex, by: rawBlockSize) {
let blockData = data[i..<min(data.endIndex, i + rawBlockSize)]
let blockCRC = CheckSums.bzip2crc32(blockData)
let block = data[i..<min(data.endIndex, i + rawBlockSize)].toByteArray()
let blockCRC = CheckSums.bzip2crc32(block)
totalCRC = (totalCRC << 1) | (totalCRC >> 31)
totalCRC ^= blockCRC
@@ -60,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.
@@ -72,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)
@@ -121,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 })
@@ -147,13 +148,19 @@ 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: UInt8(0), count: 16)
// Encode which symbols (bytes) are used in the data. All possible 256 symbols (0...255) are split into 16 "maps"
// of 16 sequential symbols. Each map is a sequence of 16 bits where a non-zero bits indicate that used symbols.
// 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)
@@ -162,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 {
@@ -174,10 +181,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..<selectors.count))
// Selectors are indices into `tables` list, so by construction they can only take values between 0 and
// `tables.count - 1`. This correspondingly limits the list of characters for the MTF transform.
let mtfSelectors = mtf(selectors, maxValue: tables.count - 1)
for selector in mtfSelectors {
guard selector <= 5
else { fatalError("Incorrect selector.") }
// The output of MTF transform are the indices into the supplied characters list. Since the length of the
// characters list is given by `tables.count`, by construction `selector` should be between 0 and
// `tables.count - 1`.
assert(selector < tables.count)
bitWriter.write(bits: Array(repeating: 1, count: selector))
bitWriter.write(bit: 0)
}
@@ -185,53 +196,59 @@ extension BZip2: CompressionAlgorithm {
// Delta bit lengths.
for lengths in tablesLengths {
// Starting length.
var currentLength = lengths[0]
bitWriter.write(number: currentLength, bitsCount: 5)
var prevLength = lengths[0]
bitWriter.write(number: prevLength, 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 prevLength > length {
// Bits: 11 -> 11_11 -> 11_11_11 -> 11_11_11_11 -> ...
// Numbers: 3 -> 15 -> 63 -> 255 -> ...
// https://oeis.org/A024036
let diff = prevLength - length
bitWriter.write(unsignedNumber: (1 << (2 * diff)) - 1, bitsCount: 2 * diff)
} else if prevLength < length {
// Bits: 10 -> 10_10 -> 10_10_10 -> 10_10_10_10 -> ...
// Numbers: 2 -> 10 -> 42 -> 170 -> ...
// https://oeis.org/A020988
let diff = length - prevLength
bitWriter.write(unsignedNumber: ((1 << (2 * diff)) - 1) * 2 / 3 , bitsCount: 2 * diff)
}
prevLength = length
bitWriter.write(bit: 0)
}
}
// 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
}
}
/// 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..<min(4, runLength) {
out.append(byte)
}
@@ -243,10 +260,10 @@ extension BZip2: CompressionAlgorithm {
return out
}
private static func mtf(_ array: [Int], characters: [Int]) -> [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..<array.count {
let index = dictionary.firstIndex(of: array[i])!
out.append(index)
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
// BZip2 specific function for generation of HuffmanLength array from stats.
extension BZip2 {
+128 -82
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
@@ -25,9 +25,32 @@ public class BZip2: DecompressionAlgorithm {
return try decompress(bitReader)
}
/**
Decompresses one or more concatenated BZip2 archives found in `data`.
- Parameter data: Data that may contain one or more concatenated BZip2 archives.
- Throws: `BZip2Error` if an unexpected byte (bit) sequence was encountered or a checksum mismatch is detected for
any archive. It may indicate that the input is damaged or it might not be compressed with BZip2 at all. If
`BZip2Error.wrongCRC` is thrown, its associated value contains only the bytes decompressed for the archive that
failed the checksum.
- Returns: An array of decompressed data, one element per archive, in the same order as they appear in the input.
*/
public static func multiDecompress(data: Data) throws -> [Data] {
let reader = MsbBitReader(data: data)
var result = [Data]()
while !reader.isFinished {
result.append(try decompress(reader))
reader.align()
}
return result
}
static func decompress(_ bitReader: MsbBitReader) throws -> Data {
// Valid BZip2 "archive" must contain at least 14 bytes of data.
guard bitReader.bitsLeft >= 14 * 8
// Valid BZip2 "archive" must contain at least 14 bytes of data: magic number (2 bytes), method (1 byte), and
// block size (1 byte).
guard bitReader.bitsLeft >= 32
else { throw BZip2Error.wrongMagic }
/// An array for storing output data
@@ -44,15 +67,18 @@ public class BZip2: DecompressionAlgorithm {
var totalCRC: UInt32 = 0
while true {
// Using `Int64` because 48 bits may not fit into `Int` on some platforms.
// Each BZip2 block must contain block type (6 bytes) and block CRC32 (4 bytes).
guard bitReader.bitsLeft >= 80
else { throw BZip2Error.wrongMagic }
// 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)
if blockType == 0x314159265359 {
let blockData = try decode(bitReader, blockSize)
out.append(blockData)
guard CheckSums.bzip2crc32(blockData) == blockCRC32
let block = try decode(bitReader, blockSize)
out.append(Data(block))
guard CheckSums.bzip2crc32(block) == blockCRC32
else { throw BZip2Error.wrongCRC(out) }
totalCRC = (totalCRC << 1) | (totalCRC >> 31)
totalCRC ^= blockCRC32
@@ -68,22 +94,32 @@ 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] {
// Truncation checks: there is no such thing as "empty BZip2 blocks". Empty data after compression produces
// only a "CRC32 block". As such when processing a normal BZip2 block we can assume that various BZip2
// structures (such as table selectors or code lengths) are present, and there are at least some symbols encoded.
// Randomized bit, pointer (24 bits), and bitmap of used symbol blocks (16 bits) require 41 bits.
guard bitReader.bitsLeft >= 41
else { throw BZip2Error.wrongMagic }
let isRandomized = bitReader.bit()
guard isRandomized == 0
else { throw BZip2Error.randomizedBlock }
let pointer = bitReader.int(fromBits: 24)
// Decoding which symbols are used in Huffman tables.
// The "list" of all possible 256 symbols is split into 16 blocks.
// If no symbols from a block are in use, then the block is not present.
// First, we decode which blocks are present.
let usedBlocksBitMap = UInt16(bitReader.int(fromBits: 16))
// Decoding which symbols are used in Huffman tables. All possible 256 symbols (0...255) are split into 16 "maps"
// of 16 sequential symbols. Each map is a sequence of 16 bits where non-zero bits indicate used symbols. A map
// is omitted if none of its symbols are in use. First, we decode which maps are present.
let usedBlocksBitMap = bitReader.uint16(fromBits: 16)
// For each non-zero bit in the bitmap there should be 16 bits of used symbols. After that there should be 3
// bits for the number of Huffman tables and 15 bits for selectors count.
guard bitReader.bitsLeft >= 16 * usedBlocksBitMap.nonzeroBitCount + 3 + 15
else { throw BZip2Error.wrongMagic }
var blockMask = 1 << 15 as UInt16
var usedSymbols = [UInt8]()
// Two additional symbols are RUNA and RUNB.
var usedSymbolsCount = 2
while blockMask > 0 {
if usedBlocksBitMap & blockMask > 0 {
// Each block, if present, is a set of 16 bits which, if set, represent that the corresponding symbols
@@ -92,7 +128,6 @@ public class BZip2: DecompressionAlgorithm {
var symbolMask = 1 << 15 as UInt16
while symbolMask > 0 {
if usedSymbolsBitMask & symbolMask > 0 {
usedSymbolsCount += 1
usedSymbols.append(UInt8(blockMask.leadingZeroBitCount * 16 + symbolMask.leadingZeroBitCount))
}
symbolMask >>= 1
@@ -100,103 +135,114 @@ public class BZip2: DecompressionAlgorithm {
}
blockMask >>= 1
}
// Two additional symbols are RUNA and RUNB.
let usedSymbolsCount = 2 + usedSymbols.count
let huffmanGroups = bitReader.int(fromBits: 3)
guard huffmanGroups >= 2 && huffmanGroups <= 6
let huffmanTablesCount = bitReader.int(fromBits: 3)
guard huffmanTablesCount >= 2 && huffmanTablesCount <= 6
else { throw BZip2Error.wrongHuffmanGroups }
func computeSelectors() throws -> [Int] {
let selectorsUsed = bitReader.int(fromBits: 15)
let selectorsCount = bitReader.int(fromBits: 15)
var mtf = Array(0..<huffmanTablesCount)
var mtf = Array(0..<huffmanGroups)
var selectorsList = [Int]()
for _ in 0..<selectorsUsed {
var c = 0
while bitReader.bit() > 0 {
c += 1
guard c < huffmanGroups
else { throw BZip2Error.wrongSelector }
// It is impossible to calculate in advance how many bits encode table selectors. At minimum, if each selector
// is encoded by a single zero bit (which means only the zeroth Huffman table is utilized), `selectorsCount`
// bits are used. At maximum, when all symbols are encoded with the last Huffman table, each selector is given
// by `huffmanTablesCount - 1` non-zero bits and one zero bit. This gives `huffmanTablesCount * selectorsCount`
// bits in total.
// Due to this unpredictability we are forced to check if there are still bits left before every read operation,
// similarly to how bit reading is done in `DecodingTree.findNextSymbol()`.
var selectors = [Int]()
// Accessing `bitsLeft` property of `bitReader` incurs a lot of overhead, so we create a local copy.
var bitsLeft = bitReader.bitsLeft
for _ in 0..<selectorsCount {
var c = 0
while bitsLeft > 0 {
let bit = bitReader.bit()
bitsLeft -= 1
if bit == 0 {
break
}
if c >= 0 {
let el = mtf.remove(at: c)
mtf.insert(el, at: 0)
}
selectorsList.append(mtf[0])
c += 1
}
return selectorsList
guard c < huffmanTablesCount
else { throw BZip2Error.wrongSelector }
let el = mtf.remove(at: c)
mtf.insert(el, at: 0)
selectors.append(el)
}
let selectors = try computeSelectors()
func computeTables() throws -> [DecodingTree] {
var tables = [DecodingTree]()
for _ in 0..<huffmanGroups {
var length = bitReader.int(fromBits: 5)
var lengths = [CodeLength]()
for i in 0..<usedSymbolsCount {
guard length >= 0 && length <= 20
// Similar to decoding selectors, code lengths are also encoded in unpredictable manner. As such, we check for
// input truncation before reading every bit.
var tables = [DecodingTree]()
for _ in 0..<huffmanTablesCount {
guard bitsLeft >= 5
else { throw BZip2Error.wrongHuffmanCodeLength }
var length = bitReader.int(fromBits: 5)
bitsLeft -= 5
var codeLengths = [CodeLength]()
for i in 0..<usedSymbolsCount {
guard length >= 0 && length <= 20
else { throw BZip2Error.wrongHuffmanCodeLength }
while bitsLeft > 0 {
let bit = bitReader.bit()
bitsLeft -= 1
if bit == 0 {
break
}
guard bitsLeft > 0
else { throw BZip2Error.wrongHuffmanCodeLength }
while bitReader.bit() > 0 {
length -= (bitReader.bit().toInt() * 2 - 1)
}
if length > 0 {
lengths.append(CodeLength(symbol: i, codeLength: length))
}
length -= (bitReader.bit().toInt() * 2 - 1)
bitsLeft -= 1
}
let codes = Code.huffmanCodes(from: lengths)
let table = DecodingTree(codes: codes.codes, maxBits: codes.maxBits, bitReader)
tables.append(table)
codeLengths.append(CodeLength(symbol: i, codeLength: length))
}
return tables
let codes = Code.huffmanCodes(from: codeLengths)
let table = DecodingTree(codes, bitReader)
tables.append(table)
}
let tables = try computeTables()
var selectorPointer = 0
var decoded = 0
var table = tables[selectors[selectors.startIndex]]
var selectorIndex = selectors.startIndex &+ 1
var runLength = 0
var repeatPower = 0
var buffer: [UInt8] = []
var currentTable: DecodingTree?
var repeatPower = 1
var buffer = [UInt8]()
while true {
decoded -= 1
if decoded <= 0 {
decoded = 50
if selectorPointer == selectors.count {
throw BZip2Error.wrongSelector
} else if selectorPointer < selectors.count {
currentTable = tables[selectors[selectorPointer]]
selectorPointer += 1
}
if decoded >= 50 {
guard selectorIndex < selectorsCount
else { throw BZip2Error.wrongSelector }
table = tables[selectors[selectorIndex]]
selectorIndex &+= 1
decoded = 0
}
guard let symbol = currentTable?.findNextSymbol(), symbol != -1
let symbol = table.findNextSymbol()
guard symbol != -1
else { throw BZip2Error.symbolNotFound }
decoded &+= 1
if symbol == 0 || symbol == 1 { // RUNA and RUNB symbols.
if runLength == 0 {
repeatPower = 1
}
runLength += repeatPower << symbol
runLength &+= repeatPower << symbol
repeatPower <<= 1
continue
} else if runLength > 0 {
}
if runLength > 0 {
// There might have been a repeat run right before EOS symbol.
for _ in 0..<runLength {
buffer.append(usedSymbols[0])
}
runLength = 0
repeatPower = 1
}
if symbol == usedSymbolsCount - 1 { // End of stream symbol.
break
} else { // Move to front inverse.
let element = usedSymbols.remove(at: symbol - 1)
usedSymbols.insert(element, at: 0)
buffer.append(element)
}
// Move to front inverse.
let element = usedSymbols.remove(at: symbol - 1)
usedSymbols.insert(element, at: 0)
buffer.append(element)
}
let nt = BurrowsWheeler.reverse(bytes: buffer, pointer)
@@ -220,7 +266,7 @@ public class BZip2: DecompressionAlgorithm {
}
}
return Data(out)
return out
}
}
+11 -3
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
@@ -29,8 +29,16 @@ public enum BZip2Error: Error {
/// Symbol wasn't found in Huffman tree.
case symbolNotFound
/**
Computed checksum of uncompressed data doesn't match the value stored in archive.
Associated value of the error contains already decompressed data.
Computed checksum of the uncompressed data does not match the value stored in the archive.
Associated value contains the data that were successfully decompressed up to the point where the mismatch was
detected.
- When using `BZip2.decompress(data:)`: The associated value comes from a single BZip2 archive. If the mismatch
happens at the final checksum verification, this is usually the entire decompressed output.
- When using `BZip2.multiDecompress(data:)`: The input may contain several concatenated BZip2 archives. The error
is thrown for the current archive that fails the checksum. The associated value contains only the data decompressed
for that archive. Results from earlier archives are not included and are not returned once this error is thrown.
*/
case wrongCRC(Data)
}
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
enum BurrowsWheeler {
static func transform(bytes: [Int]) -> ([Int], Int) {
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
/// Suffix Array by Induced Sorting
enum SuffixArray {
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+3 -3
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
@@ -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()]
}
+4 -6
View File
@@ -1,9 +1,9 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
typealias HuffmanCodes = (codes: [Code], maxBits: Int)
struct Code {
@@ -12,8 +12,7 @@ struct Code {
let code: Int
let symbol: Int
/// `lengths` don't have to be sorted, but there must not be any 0 code lengths.
static func huffmanCodes(from lengths: [CodeLength]) -> (codes: [Code], maxBits: Int) {
static func huffmanCodes(from lengths: [CodeLength]) -> HuffmanCodes {
// Sort `lengths` array to calculate canonical Huffman code.
let sortedLengths = lengths.sorted()
@@ -24,8 +23,7 @@ struct Code {
var loopBits = -1
var symbol = -1
for length in sortedLengths {
precondition(length.codeLength > 0, "Code length must not be 0 during HuffmanTree construction.")
for length in sortedLengths where length.codeLength > 0 {
symbol += 1
// We sometimes need to make symbol to have length.bits bit length.
let bits = length.codeLength
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
struct CodeLength: Equatable {
let symbol: Int
+6 -7
View File
@@ -1,26 +1,25 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
import BitByteData
final class DecodingTree {
struct DecodingTree {
private let bitReader: BitReader
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)
// Maximum amount of leaves in a binary tree with the height equal to `huffmanCodes.maxBits`.
self.leafCount = (1 << (huffmanCodes.maxBits + 1)) - 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
+3 -4
View File
@@ -1,9 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
import BitByteData
fileprivate struct CodingIndex {
@@ -13,13 +12,13 @@ fileprivate struct CodingIndex {
}
final class EncodingTree {
struct EncodingTree {
private let bitWriter: BitWriter
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)
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
/// Represents a (de)compression method.
public enum CompressionMethod {
/// BZip2.
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+14 -4
View File
@@ -1,10 +1,19 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
extension Data {
@inlinable @inline(__always)
func toByteArray() -> [UInt8] {
return self.withUnsafeBytes { $0.map { $0 } }
}
}
extension UnsignedInteger {
@inlinable @inline(__always)
@@ -32,10 +41,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
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// 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.
+9 -21
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
@@ -30,8 +30,7 @@ extension Deflate: CompressionAlgorithm {
// Static Huffman size is more complicated...
let staticHuffmanBlockSize = staticHuffmanBitSize(bldCodes.stats)
// Since `length` of uncompressed block is 16-bit integer,
// there is a limitation on size of uncompressed block.
// Since `length` of uncompressed block is 16-bit integer, there is a limitation on size of uncompressed block.
// Falling back to static Huffman encoding in case of big uncompressed block is a band-aid solution.
if uncompBlockSize <= staticHuffmanBlockSize && uncompBlockSize <= 65535 {
// If according to our calculations static huffman will not make output smaller than input,
@@ -73,24 +72,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
}
@@ -106,9 +94,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(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(Constants.staticHuffmanDistanceCodes.codes, bitWriter, reverseCodes: true)
for code in bldCodes {
switch code {
+7 -9
View File
@@ -1,17 +1,15 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
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 +152,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 +170,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]
-20
View File
@@ -1,20 +0,0 @@
// Copyright (c) 2024 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
// Deflate specific functions for generation of HuffmanLength arrays from different inputs.
extension Deflate {
/// - Note: Skips zero codeLengths.
static func lengths(from orderedCodeLengths: [Int]) -> [CodeLength] {
var lengths = [CodeLength]()
for (i, codeLength) in orderedCodeLengths.enumerated() where codeLength > 0 {
lengths.append(CodeLength(symbol: i, codeLength: codeLength))
}
return lengths
}
}
+50 -39
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
@@ -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).
@@ -88,6 +88,11 @@ public class Deflate: DecompressionAlgorithm {
/// Number of literals codes.
let literals = bitReader.int(fromBits: 5) + 257
// According to Deflate specification maximum amount of literal/length codes is 286. However, it
// is possible to encode a number larger than this with 5 bits. Specification seems to be silent
// about this scenario, so we treat it as a corrupted input.
guard literals <= 286
else { throw DeflateError.wrongSymbol }
/// Number of distances codes.
let distances = bitReader.int(fromBits: 5) + 1
/// Number of code lengths codes.
@@ -96,68 +101,70 @@ public class Deflate: DecompressionAlgorithm {
guard bitReader.bitsLeft >= 3 * codeLengthsCount
else { throw DeflateError.symbolNotFound }
// Code lengths of the literal/length and distance Huffman trees are encoded with another Huffman
// tree using a special alphabet. The code lengths for this latter tree are presented in a specific
// order which is stored in `Constants.codeLengthOrders`: `codeLengthOrders[i]` is the code lengths
// alphabet symbol at the i-th place in the sequence.
var orderedCodeLengths = Array(repeating: 0, count: 19)
for i in 0..<codeLengthsCount {
orderedCodeLengths[Constants.codeLengthOrders[i]] = bitReader.int(fromBits: 3)
}
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] = []
var n = 0
while n < (literals + distances) {
var codeLengths = Array(repeating: 0, count: literals + distances)
var n = codeLengths.startIndex
while n < codeLengths.endIndex {
// Finding next Huffman tree's symbol in data.
let symbol = dynamicCodeTree.findNextSymbol()
guard symbol != -1 else { throw DeflateError.symbolNotFound }
let count: Int
let what: Int
if symbol >= 0 && symbol <= 15 {
// It is a raw code length.
count = 1
what = symbol
} else if symbol == 16 {
// Copy previous code length 3 to 6 times.
// Next two bits show how many times we need to copy.
codeLengths[n] = symbol
n += 1
} else if symbol == 16 && n > codeLengths.startIndex {
// Copy previous code length 3 to 6 times. Next two bits show how many times we need to copy.
// This symbol cannot be the first symbol encoding code lengths, since there is nothing to
// copy at this point.
guard bitReader.bitsLeft >= 2
else { throw DeflateError.symbolNotFound }
count = bitReader.int(fromBits: 2) + 3
what = codeLengths.last!
let copyCount = bitReader.int(fromBits: 2) + 3
guard n + copyCount <= codeLengths.endIndex
else { throw DeflateError.wrongSymbol }
for i in 0..<copyCount {
codeLengths[n + i] = codeLengths[n - 1]
}
n += copyCount
} else if symbol == 17 {
// Repeat code length 0 from 3 to 10 times.
// Next three bits show how many times we need to copy.
// Repeat code length 0 from 3 to 10 times. Next 3 bits show how many times we need to copy.
// Since `codeLengths` array was already preinitialized with zeros, we just have to skip
// ahead.
guard bitReader.bitsLeft >= 3
else { throw DeflateError.symbolNotFound }
count = bitReader.int(fromBits: 3) + 3
what = 0
n += bitReader.int(fromBits: 3) + 3
} else if symbol == 18 {
// Repeat code length 0 from 11 to 138 times.
// Next seven bits show how many times we need to do this.
// Repeat code length 0 from 11 to 138 times. Next 7 bits show how many times we need to
// copy. Since `codeLengths` array was already preinitialized with zeros, we just have to
// skip ahead.
guard bitReader.bitsLeft >= 7
else { throw DeflateError.symbolNotFound }
count = bitReader.int(fromBits: 7) + 11
what = 0
n += bitReader.int(fromBits: 7) + 11
} else {
throw DeflateError.wrongSymbol
}
for _ in 0..<count {
codeLengths.append(what)
}
n += count
}
// 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..<literals])))
mainLiterals = DecodingTree(codes: literalCodes.codes, maxBits: literalCodes.maxBits, bitReader)
let distanceCodes = Code.huffmanCodes(from: Deflate.lengths(from:
Array(codeLengths[literals..<codeLengths.count])))
mainDistances = DecodingTree(codes: distanceCodes.codes, maxBits: distanceCodes.maxBits, bitReader)
// Self-consistency check. Failure here can happen if at some point a zero code length was repeated
// too many times indicating a corrupted input.
guard n == codeLengths.endIndex
else { throw DeflateError.wrongSymbol }
// We have read `codeLengths` for both trees at once, so we split them and make corresponding trees.
let literalCodes = Code.huffmanCodes(from: Deflate.lengths(from: Array(codeLengths.prefix(upTo: literals))))
mainLiterals = DecodingTree(literalCodes, bitReader)
let distanceCodes = Code.huffmanCodes(from: Deflate.lengths(from: Array(codeLengths.suffix(from: literals))))
mainDistances = DecodingTree(distanceCodes, bitReader)
}
// Main loop of data decompression.
@@ -241,4 +248,8 @@ public class Deflate: DecompressionAlgorithm {
return Data(out)
}
private static func lengths(from orderedCodeLengths: [Int]) -> [CodeLength] {
return orderedCodeLengths.enumerated().map({ CodeLength(symbol: $0, codeLength: $1) })
}
}
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// 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.
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
extension FileSystemType {
init(_ gzipOS: UInt8) {
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// 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.
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+5 -6
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
@@ -118,12 +118,11 @@ public struct GzipHeader {
headerBytes.append(byte)
}
// There must be least four bytes of extra fields for SI1, SI2, and 2 bytes of the length parameter
// filled with zeros (minimal variant).
guard reader.bytesLeft >= xlen && xlen >= 4
else { throw GzipError.wrongMagic }
while xlen > 0 {
// There must be least four bytes of extra fields for SI1, SI2, and 2 bytes of the length parameter
// filled with zeros (minimal variant).
guard reader.bytesLeft >= xlen && xlen >= 4
else { throw GzipError.wrongMagic }
let si1 = reader.byte()
headerBytes.append(si1)
+1 -2
View File
@@ -1,10 +1,9 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
import BitByteData
extension LZ4: CompressionAlgorithm {
+12 -13
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
@@ -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..<matchLength {
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
/// Used to decode symbols that need several bits for storing.
struct LZMABitTreeDecoder {
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
enum LZMAConstants {
static let topValue: UInt32 = 1 << 24
+1 -2
View File
@@ -1,9 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
import BitByteData
struct LZMADecoder {
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// 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.
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
struct LZMALenDecoder {
private var choice: Int = LZMAConstants.probInitValue
+2 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
@@ -18,7 +18,7 @@ public struct LZMAProperties {
public var pb: Int = 2
/**
Size of the dictionary. Default value is 1 << 24.
Size of the dictionary. Default value is `1 << 24`.
- Note: Dictionary size cannot be less than 4096. In case of attempt to set it to the value less than 4096 it will
be automatically set to 4096 instead.
+2 -7
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
@@ -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
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -2
View File
@@ -1,9 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
import BitByteData
struct LZMA2Decoder {
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// 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.
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
extension ContainerEntryType {
init(_ fileTypeIndicator: UInt8) {
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -2
View File
@@ -1,9 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
import BitByteData
extension LittleEndianByteReader {
+1 -2
View File
@@ -1,10 +1,9 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
import BitByteData
/// Provides functions for work with TAR containers.
public class TarContainer: Container {
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// 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.
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+3 -3
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
@@ -6,8 +6,8 @@
import Foundation
import BitByteData
// While it is tempting to make Provider conform to `IteratorProtocol` and `Sequence` protocols, it is in fact
// impossible to do so, since `TarHeader.init(...)` is throwing and `IteratorProtocol.next()` cannot be throwing.
// While it is tempting to make the parser conform to `IteratorProtocol` and `Sequence` protocols, it is in fact
// impossible to do so, since `TarHeader.init(_)` is throwing and `IteratorProtocol.next()` cannot be throwing.
struct TarParser {
enum ParsingResult {
+18 -44
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
@@ -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()
}
}
+3 -14
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
@@ -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()
}
}
@@ -1,9 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
import BitByteData
extension LittleEndianByteReader {
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -2
View File
@@ -1,9 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
import BitByteData
struct XZStreamHeader {
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
extension CompressionMethod {
init(_ compression: UInt16) {
+1 -3
View File
@@ -1,10 +1,8 @@
// Copyright (c) 2024 Timofey Solomko
// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
extension FileSystemType {
init(_ versionMadeBy: UInt16) {

Some files were not shown because too many files have changed in this diff Show More