Merge branch 'release-4.6.0' into develop

This commit is contained in:
Timofey Solomko
2021-08-04 15:51:48 +03:00
16 changed files with 110 additions and 83 deletions
+2 -2
View File
@@ -3,11 +3,11 @@ sourcekitten_sourcefile: docs.json
clean: false
author: Timofey Solomko
module: SWCompression
module_version: 4.5.11
module_version: 4.6.0
copyright: '© 2021 Timofey Solomko'
readme: README.md
github_url: https://github.com/tsolomko/SWCompression
github_file_prefix: https://github.com/tsolomko/SWCompression/tree/4.5.11
github_file_prefix: https://github.com/tsolomko/SWCompression/tree/4.6.0
theme: fullwidth
custom_categories:
+21
View File
@@ -1,5 +1,26 @@
# Changelog
## 4.6.0
- Swift 4.2 is no longer supported.
- Minimum iOS deployment version (when installed via CocoaPods or Carthage) is now 9.0.
- Increased the lowest required version of BitByteData dependency to 2.0.
- Added the `TarContainer.create(from:force:)` function which allows to specify TAR format.
- It is now possible to create TAR containers of GNU, ustar (POSIX), and pre-POSIX formats.
- The `TarContainer.create(from:)` function (alongside with the newly added function) no longer throws.
- The `TarCreateError.utf8NonEncodable` error is now never thrown.
- Handling of truncated TAR containers should now be more consistent.
- Previously introduced check for truncated containers now throws the `TarError.tooSmallFileIsPassed` error instead
of `TarError.wrongField`.
- Documentation has been updated:
- Adjusted documentation of the `TarEntryInfo` properties to account for other formats used in creation of a
container.
- Adjusted documentation of the `TarError.tooSmallFileIsPassed` error to mention its use in all situations when
truncated data is encountered.
- swcomp changes:
- `zip -i` command now prints entry comments only if they are not empty.
- Slight grammatical improvements to the help messages of swcomp.
## 4.5.11
- Fixed a crash when processing a truncated TAR file.
+1 -1
View File
@@ -1 +1 @@
github "tsolomko/BitByteData" "2.0.0-test.2"
github "tsolomko/BitByteData" ~> 2.0.0
+1 -1
View File
@@ -13,7 +13,7 @@ let package = Package(
// .package(url: "https://github.com/jakeheis/SwiftCLI",
// from: "5.2.0"),
.package(url: "https://github.com/tsolomko/BitByteData",
from: "2.0.0-test.2"),
from: "2.0.0"),
],
targets: [
// SWCOMP: Uncomment the lines below to build swcomp example program.
+15 -16
View File
@@ -1,6 +1,5 @@
# SWCompression
[![Swift 4.2](https://img.shields.io/badge/Swift-4.2-blue.svg)](https://developer.apple.com/swift/)
[![Swift 5.X](https://img.shields.io/badge/Swift-5.X-blue.svg)](https://developer.apple.com/swift/)
[![GitHub license](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://raw.githubusercontent.com/tsolomko/SWCompression/master/LICENSE)
[![Build Status](https://travis-ci.com/tsolomko/SWCompression.svg?branch=develop)](https://travis-ci.com/tsolomko/SWCompression)
@@ -10,7 +9,7 @@ A framework with (de)compression algorithms and functions for working with vario
## What is this?
SWCompression — is a framework with a collection of functions for:
SWCompression is a framework with a collection of functions for:
1. Decompression (and sometimes compression) using different algorithms.
2. Reading (and sometimes writing) archives of different formats.
@@ -34,7 +33,7 @@ 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, CocoaPods, or Carthage.
### Swift Package Manager
@@ -47,7 +46,7 @@ let package = Package(
name: "PackageName",
dependencies: [
.package(url: "https://github.com/tsolomko/SWCompression.git",
from: "4.5.0")
from: "4.6.0")
],
targets: [
.target(
@@ -62,7 +61,7 @@ More details you can find in [Swift Package Manager's Documentation](https://git
### CocoaPods
Add `pod 'SWCompression', '~> 4.5'` and `use_frameworks!` lines to your Podfile.
Add `pod 'SWCompression', '~> 4.6'` and `use_frameworks!` lines to your Podfile.
To complete installation, run `pod install`.
@@ -105,19 +104,17 @@ BZip2 and LZMA/LZMA2 support).
### Carthage
__Important:__ Only Swift 5.x is supported when installing SWCompression via Carthage.
Add to your Cartfile `github "tsolomko/SWCompression" ~> 4.5`.
Add to your Cartfile `github "tsolomko/SWCompression" ~> 4.6`.
Then:
1. If you use Xcode 12 or later you should run `carthage update --use-xcframeworks --no-use-binaries`. 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.
1. If you use Xcode 12 or later 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.
2. If you use Xcode 11 or earlier you should run `carthage update --no-use-binaries`. After that drag and drop both
`SWCompression.framework` and `BitByteData.framework` files from from the `Carthage/Build/<platform>/` directory into the
"Embedded Binaries" section of your target's "General" tab in Xcode.
2. If you use Xcode 11 or earlier you should run `carthage update`. After that drag and drop both
`SWCompression.framework` and `BitByteData.framework` files from from the `Carthage/Build/<platform>/` directory into
the "Embedded Binaries" section of your target's "General" tab in Xcode.
For Xcode 12 or later you can currently also use the
[xconfig workaround](https://github.com/Carthage/Carthage/blob/master/Documentation/Xcode12Workaround.md).
@@ -165,7 +162,8 @@ do {
### Documentation
Every function or type of SWCompression's public API is documented. This documentation can be found at its own
[website](http://tsolomko.github.io/SWCompression).
[website](http://tsolomko.github.io/SWCompression) or via a slightly shorter link:
[swcompression.tsolomko.me](http://swcompression.tsolomko.me)
### Sophisticated example
@@ -175,7 +173,8 @@ you need to uncomment several lines in "Package.swift" and run `swift build -c r
## Contributing
Whether you find a bug, have a suggestion, idea, feedback or something else, please
[create an issue](https://github.com/tsolomko/SWCompression/issues) on GitHub.
[create an issue](https://github.com/tsolomko/SWCompression/issues) on GitHub. If you have any questions, you can ask
them on the [Discussions](https://github.com/tsolomko/SWCompression/discussions) page.
In the case of a bug, it will be especially helpful if you attach a file (archive, etc.) that caused the bug to occur.
+2 -2
View File
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = "SWCompression"
s.version = "4.5.11"
s.version = "4.6.0"
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."
@@ -22,7 +22,7 @@ Pod::Spec.new do |s|
s.swift_versions = ["5"]
s.dependency "BitByteData", "~> 1.4.4"
s.dependency "BitByteData", "~> 2.0"
s.subspec "Deflate" do |sp|
sp.source_files = "Sources/{Deflate/*,Common/*,Common/CodingTree/*}.swift"
+2 -2
View File
@@ -15,9 +15,9 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>4.5.11</string>
<string>4.6.0</string>
<key>CFBundleVersion</key>
<string>78</string>
<string>80</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2021 Timofey Solomko</string>
</dict>
@@ -15,8 +15,8 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>4.5.11</string>
<string>4.6.0</string>
<key>CFBundleVersion</key>
<string>78</string>
<string>80</string>
</dict>
</plist>
+4 -4
View File
@@ -1270,7 +1270,7 @@
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CURRENT_PROJECT_VERSION = 78;
CURRENT_PROJECT_VERSION = 80;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@@ -1351,7 +1351,7 @@
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CURRENT_PROJECT_VERSION = 78;
CURRENT_PROJECT_VERSION = 80;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
"FRAMEWORK_SEARCH_PATHS[sdk=appletvos*]" = (
@@ -1412,7 +1412,7 @@
APPLICATION_EXTENSION_API_ONLY = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 78;
DYLIB_CURRENT_VERSION = 80;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = SWCompression.xcodeproj/SWCompression.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@@ -1439,7 +1439,7 @@
APPLICATION_EXTENSION_API_ONLY = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 78;
DYLIB_CURRENT_VERSION = 80;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = SWCompression.xcodeproj/SWCompression.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+3 -1
View File
@@ -29,6 +29,9 @@ extension Data {
}
// Base-256 encoding.
// As long as we have at least 8 bytes for our value, conversion to base-256 will always succeed, since (64-bit)
// Int.max neatly fits into 8 bytes of 256-base encoding.
assert(maxLength >= 8 && Int.bitWidth <= 64)
var buffer = Array(repeating: 0 as UInt8, count: maxLength)
for i in stride(from: maxLength - 1, to: 0, by: -1) {
buffer[i] = UInt8(truncatingIfNeeded: value & 0xFF)
@@ -50,7 +53,6 @@ extension Data {
/// This should work in the same way as `String.padding(toLength: length, withPad: "\0", startingAt: 0)`.
@inline(__always)
private func zeroPad(_ length: Int) -> Data {
// TODO: Maybe this should modify self
var out = length < self.count ? self.prefix(upTo: length) : self
out.append(Data(count: length - out.count))
return out
+29 -13
View File
@@ -47,11 +47,10 @@ public class TarContainer: Container {
} else if specialEntryType == .longName || specialEntryType == .longLinkName {
return .gnu
}
case .entryInfo(let info, _):
// TODO: Probably this case (depending on how info.format is set) is already covered by the above.
switch info.format {
case .entryInfo(_, _, let headerFormat):
switch headerFormat {
case .pax:
return .pax
fatalError("Unexpected format of basic header: pax")
case .gnu:
return .gnu
case .ustar:
@@ -84,7 +83,24 @@ public class TarContainer: Container {
return create(from: entries, force: .pax)
}
public static func create(from entries: [TarEntry], force format: TarContainer.Format) -> Data {
/**
Creates a new TAR container with `entries` as its content and generates its `Data` using the specified `format`.
This function forces the usage of the `format`, meaning that certain properties about the `entries` may be missing
from the resulting container data if the chosen format doesn't support certain features. For example, relatively
long names (and linknames) will be truncated if the `.ustar` or `.prePosix` format is specified.
It is highly recommended to use the `TarContainer.create(from:)` function (or use the `.pax` format) to ensure the
best representation of the `entries` in the output. Other (non-PAX) formats should only be used if you have a
specific need for them and you understand limitations of those formats.
- Parameter entries: TAR entries to store in the container.
- Parameter force: For the usage of the specified format.
- SeeAlso: `TarEntryInfo` properties documenation to see how their values are connected with the specific TAR
format used during container creation.
*/
public static func create(from entries: [TarEntry], force format: TarContainer.Format) -> Data {
// The general strategy is as follows. For each entry we:
// 1. Create special entries if required by the entry's info and if supported by the format.
// 2. For each special entry we create a TarHeader.
@@ -94,17 +110,17 @@ public class TarContainer: Container {
// Every time we append something to the output we also make sure that the data is padded to 512 byte-long blocks.
// In theory if the counters are big enough, the names of the special entries can become long enough to cause
// problems (truncation, etc.). In practice, the largest possible counter (Int.max) is 19 symbols long, which
// problems (truncation, etc.). In practice, the largest possible counter (UInt.max) is 20 symbols long, which
// when combined with the longest used special entry name can never cause any problems, since it is still
// shorter then 99 symbols available in the "name" field of the TAR header.
// However, if in the distant future Int.max becomes large enough to cause any issues (e.g. 128-bit and higher
// integers), the following check will catch it.
assert(String(Int.max).count < 100 - 19) // "SWC_LocalPaxHeader_".count == 19
assert(String(UInt.max).count < 100 - 19) // "SWC_LocalPaxHeader_".count == 19
// We also use &+ when incrementing counters to prevent integer overflow crashes: we rather deal with the special
// entries having the same name, then crash the program.
var longNameCounter = 0
var longLinkNameCounter = 0
var localPaxHeaderCounter = 0
// entries having repeating names, then crash the program.
var longNameCounter = 0 as UInt
var longLinkNameCounter = 0 as UInt
var localPaxHeaderCounter = 0 as UInt
var out = Data()
for entry in entries {
@@ -178,7 +194,7 @@ public class TarContainer: Container {
switch result {
case .specialEntry:
continue parsingLoop
case .entryInfo(let info, let blockStartIndex):
case .entryInfo(let info, let blockStartIndex, _):
if info.type == .directory {
var entry = TarEntry(info: info, data: nil)
entry.info.size = 0
@@ -230,7 +246,7 @@ public class TarContainer: Container {
switch result {
case .specialEntry:
continue parsingLoop
case .entryInfo(let info, _):
case .entryInfo(let info, _, _):
entries.append(info)
case .truncated:
// We don't have an error with a more suitable name.
-14
View File
@@ -167,8 +167,6 @@ public struct TarEntryInfo: ContainerEntryInfo {
*/
public var unknownExtendedHeaderRecords: [String: String]?
let format: TarContainer.Format
/**
Initializes the entry's info with its name and type.
@@ -181,8 +179,6 @@ public struct TarEntryInfo: ContainerEntryInfo {
self.name = name
self.type = type
self.linkName = ""
// These properties are only used when entry is loaded from the container.
self.format = .pax
}
init(_ header: TarHeader, _ global: TarExtendedHeader?, _ local: TarExtendedHeader?,
@@ -215,16 +211,6 @@ public struct TarEntryInfo: ContainerEntryInfo {
self.deviceMajorNumber = header.deviceMajorNumber
self.deviceMinorNumber = header.deviceMinorNumber
if local != nil || global != nil {
self.format = .pax
} else if header.format == .gnu || longName != nil || longLinkName != nil {
self.format = .gnu
} else if header.format == .ustar {
self.format = .ustar
} else {
self.format = .prePosix
}
// Set `name` and `linkName` to values from PAX or GNU format if possible.
var name = header.name
if let prefix = header.prefix, prefix != "" {
+4 -3
View File
@@ -157,7 +157,7 @@ struct TarHeader {
init(_ info: TarEntryInfo) {
self.name = info.name
self.type = .normal(info.type)
self.size = info.size ?? 0 // TODO: tarInt(...) may not work as expected for 0 instead of nil.
self.size = info.size ?? 0
self.atime = info.accessTime
self.ctime = info.creationTime
self.mtime = info.modificationTime
@@ -169,7 +169,7 @@ struct TarHeader {
self.deviceMajorNumber = info.deviceMajorNumber
self.deviceMinorNumber = info.deviceMinorNumber
self.linkName = info.linkName
self.format = .pax // TODO: If TarEntryInfo.format is not removed than this should be `info.format`.
self.format = .pax
// Unused if header was created using this initializer.
self.blockStartIndex = -1
}
@@ -237,7 +237,6 @@ struct TarHeader {
// ustar and pax formats contain prefix field.
if format == .ustar || format == .pax {
// Splitting the name property into the name and prefix fields.
// TODO: Review
let nameData = Data(self.name.utf8)
if nameData.count > 100 {
var maxPrefixLength = nameData.count
@@ -252,6 +251,8 @@ struct TarHeader {
// Looking for the last slash in the potential prefix. -1 if not found.
// It determines the end of the actual prefix and the beginning of the updated name field.
// This way of finding the last slash works, since there is no other Unicode character that contains
// the 0x2F byte when encoded in UTF-8.
let lastPrefixSlashIndex = nameData.prefix(upTo: maxPrefixLength)
.range(of: Data([0x2f]), options: .backwards)?.lowerBound ?? -1
let updatedNameLength = nameData.count - lastPrefixSlashIndex - 1
+2 -2
View File
@@ -15,7 +15,7 @@ struct TarParser {
case eofMarker
case finished
case specialEntry(TarHeader.SpecialEntryType)
case entryInfo(TarEntryInfo, Int)
case entryInfo(TarEntryInfo, Int, TarContainer.Format)
}
private let reader: LittleEndianByteReader
@@ -70,7 +70,7 @@ struct TarParser {
lastLocalExtendedHeader = nil
longName = nil
longLinkName = nil
return .entryInfo(info, header.blockStartIndex)
return .entryInfo(info, header.blockStartIndex, header.format)
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ import Foundation
import SWCompression
import SwiftCLI
let cli = CLI(name: "swcomp", version: "4.5.11",
let cli = CLI(name: "swcomp", version: "4.6.0",
description: """
swcomp - a small command-line client for SWCompression framework.
Serves as an example of SWCompression usage.
+21 -19
View File
@@ -219,27 +219,29 @@ class TarCreateTests: XCTestCase {
}
func testBigUid() throws {
let uid = (1 << 32) - 1
var info = TarEntryInfo(name: "file.txt", type: .regular)
info.ownerID = uid
// Int.max tests that base-256 encoding of integer fields works in the edge case.
for uid in [(1 << 32) - 1, Int.max] {
var info = TarEntryInfo(name: "file.txt", type: .regular)
info.ownerID = uid
let containerData = TarContainer.create(from: [TarEntry(info: info, data: Data())])
XCTAssertEqual(try TarContainer.formatOf(container: containerData), .pax)
let newInfo = try TarContainer.open(container: containerData)[0].info
let containerData = TarContainer.create(from: [TarEntry(info: info, data: Data())])
XCTAssertEqual(try TarContainer.formatOf(container: containerData), .pax)
let newInfo = try TarContainer.open(container: containerData)[0].info
XCTAssertEqual(newInfo.name, "file.txt")
XCTAssertEqual(newInfo.type, .regular)
XCTAssertEqual(newInfo.size, 0)
XCTAssertEqual(newInfo.ownerID, uid)
XCTAssertEqual(newInfo.linkName, "")
XCTAssertEqual(newInfo.ownerUserName, "")
XCTAssertEqual(newInfo.ownerGroupName, "")
XCTAssertNil(newInfo.permissions)
XCTAssertNil(newInfo.groupID)
XCTAssertNil(newInfo.accessTime)
XCTAssertNil(newInfo.creationTime)
XCTAssertNil(newInfo.modificationTime)
XCTAssertNil(newInfo.comment)
XCTAssertEqual(newInfo.name, "file.txt")
XCTAssertEqual(newInfo.type, .regular)
XCTAssertEqual(newInfo.size, 0)
XCTAssertEqual(newInfo.ownerID, uid)
XCTAssertEqual(newInfo.linkName, "")
XCTAssertEqual(newInfo.ownerUserName, "")
XCTAssertEqual(newInfo.ownerGroupName, "")
XCTAssertNil(newInfo.permissions)
XCTAssertNil(newInfo.groupID)
XCTAssertNil(newInfo.accessTime)
XCTAssertNil(newInfo.creationTime)
XCTAssertNil(newInfo.modificationTime)
XCTAssertNil(newInfo.comment)
}
}
func testGnuLongName() throws {