lottie-ios 4.1.2 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/main.yml +27 -9
- package/Lottie.xcodeproj/project.pbxproj +158 -70
- package/Lottie.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +2 -2
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/IDEFindNavigatorScopes.plist +5 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +258 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Expressions.xcexplist +13 -2
- package/Package.swift +2 -1
- package/README.md +3 -3
- package/Rakefile +8 -4
- package/Sources/Private/CoreAnimation/Animations/CALayer+addAnimation.swift +16 -2
- package/Sources/Private/CoreAnimation/Animations/CombinedShapeAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/CustomPathAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/EllipseAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/GradientAnimations.swift +6 -6
- package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +76 -7
- package/Sources/Private/CoreAnimation/Animations/OpacityAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/RectangleAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/ShapeAnimation.swift +66 -102
- package/Sources/Private/CoreAnimation/Animations/StarAnimation.swift +2 -2
- package/Sources/Private/CoreAnimation/Animations/StrokeAnimation.swift +3 -3
- package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +66 -17
- package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +55 -32
- package/Sources/Private/CoreAnimation/Extensions/Keyframes+combined.swift +16 -12
- package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +3 -3
- package/Sources/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +24 -11
- package/Sources/Private/CoreAnimation/Layers/ImageLayer.swift +2 -2
- package/Sources/Private/CoreAnimation/Layers/PreCompLayer.swift +1 -1
- package/Sources/Private/CoreAnimation/Layers/RepeaterLayer.swift +13 -2
- package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +9 -1
- package/Sources/Private/CoreAnimation/ValueProviderStore.swift +22 -11
- package/Sources/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.swift +1 -1
- package/Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift +13 -2
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerTransformNode.swift +16 -7
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift +2 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/RectNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/StarNode.swift +2 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift +20 -8
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +28 -9
- package/Sources/Private/Model/Assets/ImageAsset.swift +4 -3
- package/Sources/Private/Model/DotLottie/DotLottieAnimation.swift +2 -8
- package/Sources/Private/Model/DotLottie/DotLottieManifest.swift +3 -14
- package/Sources/Private/Model/DotLottie/DotLottieUtils.swift +11 -1
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+BackingConfiguration.swift +147 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Helpers.swift +351 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+MemoryFile.swift +183 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Progress.swift +66 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Reading.swift +144 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+ReadingDeprecated.swift +49 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Writing.swift +385 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+WritingDeprecated.swift +91 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+ZIP64.swift +170 -0
- package/Sources/Private/Model/DotLottie/{Zip/ZipArchive.swift → ZipFoundation/Archive.swift} +150 -227
- package/Sources/Private/Model/DotLottie/ZipFoundation/Data+Compression.swift +403 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Data+CompressionDeprecated.swift +44 -0
- package/Sources/Private/Model/DotLottie/{Zip → ZipFoundation}/Data+Serialization.swift +62 -0
- package/Sources/Private/Model/DotLottie/{Zip/ZipEntry+Serialization.swift → ZipFoundation/Entry+Serialization.swift} +7 -7
- package/Sources/Private/Model/DotLottie/{Zip/ZipEntry+ZIP64.swift → ZipFoundation/Entry+ZIP64.swift} +13 -19
- package/Sources/Private/Model/DotLottie/{Zip/ZipEntry.swift → ZipFoundation/Entry.swift} +141 -10
- package/Sources/Private/Model/DotLottie/ZipFoundation/FileManager+ZIP.swift +368 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/README.md +24 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/URL+ZIP.swift +32 -0
- package/Sources/Private/Model/Extensions/Bundle.swift +5 -14
- package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +31 -8
- package/Sources/Private/Model/Objects/Transform.swift +58 -17
- package/Sources/Private/Model/ShapeItems/Repeater.swift +41 -7
- package/Sources/Private/Model/ShapeItems/ShapeTransform.swift +61 -7
- package/Sources/Private/Model/Text/TextAnimator.swift +37 -5
- package/Sources/Private/RootAnimationLayer.swift +3 -1
- package/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift +12 -4
- package/Sources/Private/Utility/Extensions/DataExtension.swift +14 -4
- package/Sources/Private/Utility/Primitives/BezierPathRoundExtension.swift +11 -0
- package/Sources/Private/Utility/Primitives/ColorExtension.swift +10 -13
- package/Sources/Private/Utility/Primitives/VectorsExtensions.swift +28 -6
- package/Sources/Public/Animation/LottieAnimationHelpers.swift +12 -10
- package/Sources/Public/Animation/LottieAnimationView.swift +213 -186
- package/Sources/Public/DotLottie/DotLottieFile.swift +11 -34
- package/Sources/Public/DotLottie/DotLottieFileHelpers.swift +101 -74
- package/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift +90 -0
- package/Sources/Public/iOS/LottieAnimationViewBase.swift +1 -1
- package/Sources/Public/macOS/LottieAnimationViewBase.macOS.swift +1 -1
- package/lottie-ios.podspec +1 -1
- package/package.json +1 -1
- package/LottieAnimation/LottieAnimation.xcodeproj/project.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/LottieAnimation/LottieAnimation.xcodeproj/project.xcworkspace/xcuserdata/valentinperignon.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/LottieAnimation/LottieAnimation.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
- package/LottieAnimation/LottieAnimation.xcodeproj/xcuserdata/valentinperignon.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -6
- package/LottieAnimation/LottieAnimation.xcodeproj/xcuserdata/valentinperignon.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
- package/Sources/Private/Model/DotLottie/Zip/Data+Compression.swift +0 -134
- package/Sources/Private/Model/DotLottie/Zip/FileManager+ZIP.swift +0 -130
- package/Sources/Private/Utility/Interpolatable/KeyframeGroup+Extensions.swift +0 -59
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Archive+Reading.swift
|
|
3
|
+
// ZIPFoundation
|
|
4
|
+
//
|
|
5
|
+
// Copyright © 2017-2021 Thomas Zoechling, https://www.peakstep.com and the ZIP Foundation project authors.
|
|
6
|
+
// Released under the MIT License.
|
|
7
|
+
//
|
|
8
|
+
// See https://github.com/weichsel/ZIPFoundation/blob/master/LICENSE for license information.
|
|
9
|
+
//
|
|
10
|
+
|
|
11
|
+
import Foundation
|
|
12
|
+
|
|
13
|
+
extension Archive {
|
|
14
|
+
/// Read a ZIP `Entry` from the receiver and write it to `url`.
|
|
15
|
+
///
|
|
16
|
+
/// - Parameters:
|
|
17
|
+
/// - entry: The ZIP `Entry` to read.
|
|
18
|
+
/// - url: The destination file URL.
|
|
19
|
+
/// - bufferSize: The maximum size of the read buffer and the decompression buffer (if needed).
|
|
20
|
+
/// - skipCRC32: Optional flag to skip calculation of the CRC32 checksum to improve performance.
|
|
21
|
+
/// - progress: A progress object that can be used to track or cancel the extract operation.
|
|
22
|
+
/// - Returns: The checksum of the processed content or 0 if the `skipCRC32` flag was set to `true`.
|
|
23
|
+
/// - Throws: An error if the destination file cannot be written or the entry contains malformed content.
|
|
24
|
+
func extract(
|
|
25
|
+
_ entry: Entry,
|
|
26
|
+
to url: URL,
|
|
27
|
+
bufferSize: Int = defaultReadChunkSize,
|
|
28
|
+
skipCRC32: Bool = false,
|
|
29
|
+
progress: Progress? = nil)
|
|
30
|
+
throws -> CRC32
|
|
31
|
+
{
|
|
32
|
+
guard bufferSize > 0 else {
|
|
33
|
+
throw ArchiveError.invalidBufferSize
|
|
34
|
+
}
|
|
35
|
+
let fileManager = FileManager()
|
|
36
|
+
var checksum = CRC32(0)
|
|
37
|
+
switch entry.type {
|
|
38
|
+
case .file:
|
|
39
|
+
guard !fileManager.itemExists(at: url) else {
|
|
40
|
+
throw CocoaError(.fileWriteFileExists, userInfo: [NSFilePathErrorKey: url.path])
|
|
41
|
+
}
|
|
42
|
+
try fileManager.createParentDirectoryStructure(for: url)
|
|
43
|
+
let destinationRepresentation = fileManager.fileSystemRepresentation(withPath: url.path)
|
|
44
|
+
guard let destinationFile: FILEPointer = fopen(destinationRepresentation, "wb+") else {
|
|
45
|
+
throw CocoaError(.fileNoSuchFile)
|
|
46
|
+
}
|
|
47
|
+
defer { fclose(destinationFile) }
|
|
48
|
+
let consumer = { _ = try Data.write(chunk: $0, to: destinationFile) }
|
|
49
|
+
checksum = try extract(
|
|
50
|
+
entry,
|
|
51
|
+
bufferSize: bufferSize,
|
|
52
|
+
skipCRC32: skipCRC32,
|
|
53
|
+
progress: progress,
|
|
54
|
+
consumer: consumer)
|
|
55
|
+
case .directory:
|
|
56
|
+
let consumer = { (_: Data) in
|
|
57
|
+
try fileManager.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
|
|
58
|
+
}
|
|
59
|
+
checksum = try extract(
|
|
60
|
+
entry,
|
|
61
|
+
bufferSize: bufferSize,
|
|
62
|
+
skipCRC32: skipCRC32,
|
|
63
|
+
progress: progress,
|
|
64
|
+
consumer: consumer)
|
|
65
|
+
case .symlink:
|
|
66
|
+
guard !fileManager.itemExists(at: url) else {
|
|
67
|
+
throw CocoaError(.fileWriteFileExists, userInfo: [NSFilePathErrorKey: url.path])
|
|
68
|
+
}
|
|
69
|
+
let consumer = { (data: Data) in
|
|
70
|
+
guard let linkPath = String(data: data, encoding: .utf8) else { throw ArchiveError.invalidEntryPath }
|
|
71
|
+
try fileManager.createParentDirectoryStructure(for: url)
|
|
72
|
+
try fileManager.createSymbolicLink(atPath: url.path, withDestinationPath: linkPath)
|
|
73
|
+
}
|
|
74
|
+
checksum = try extract(
|
|
75
|
+
entry,
|
|
76
|
+
bufferSize: bufferSize,
|
|
77
|
+
skipCRC32: skipCRC32,
|
|
78
|
+
progress: progress,
|
|
79
|
+
consumer: consumer)
|
|
80
|
+
}
|
|
81
|
+
let attributes = FileManager.attributes(from: entry)
|
|
82
|
+
try fileManager.setAttributes(attributes, ofItemAtPath: url.path)
|
|
83
|
+
return checksum
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/// Read a ZIP `Entry` from the receiver and forward its contents to a `Consumer` closure.
|
|
87
|
+
///
|
|
88
|
+
/// - Parameters:
|
|
89
|
+
/// - entry: The ZIP `Entry` to read.
|
|
90
|
+
/// - bufferSize: The maximum size of the read buffer and the decompression buffer (if needed).
|
|
91
|
+
/// - skipCRC32: Optional flag to skip calculation of the CRC32 checksum to improve performance.
|
|
92
|
+
/// - progress: A progress object that can be used to track or cancel the extract operation.
|
|
93
|
+
/// - consumer: A closure that consumes contents of `Entry` as `Data` chunks.
|
|
94
|
+
/// - Returns: The checksum of the processed content or 0 if the `skipCRC32` flag was set to `true`..
|
|
95
|
+
/// - Throws: An error if the destination file cannot be written or the entry contains malformed content.
|
|
96
|
+
func extract(
|
|
97
|
+
_ entry: Entry,
|
|
98
|
+
bufferSize: Int = defaultReadChunkSize,
|
|
99
|
+
skipCRC32: Bool = false,
|
|
100
|
+
progress: Progress? = nil,
|
|
101
|
+
consumer: Consumer)
|
|
102
|
+
throws -> CRC32
|
|
103
|
+
{
|
|
104
|
+
guard bufferSize > 0 else {
|
|
105
|
+
throw ArchiveError.invalidBufferSize
|
|
106
|
+
}
|
|
107
|
+
var checksum = CRC32(0)
|
|
108
|
+
let localFileHeader = entry.localFileHeader
|
|
109
|
+
guard entry.dataOffset <= .max else { throw ArchiveError.invalidLocalHeaderDataOffset }
|
|
110
|
+
fseeko(archiveFile, off_t(entry.dataOffset), SEEK_SET)
|
|
111
|
+
progress?.totalUnitCount = totalUnitCountForReading(entry)
|
|
112
|
+
switch entry.type {
|
|
113
|
+
case .file:
|
|
114
|
+
guard let compressionMethod = CompressionMethod(rawValue: localFileHeader.compressionMethod) else {
|
|
115
|
+
throw ArchiveError.invalidCompressionMethod
|
|
116
|
+
}
|
|
117
|
+
switch compressionMethod {
|
|
118
|
+
case .none: checksum = try readUncompressed(
|
|
119
|
+
entry: entry,
|
|
120
|
+
bufferSize: bufferSize,
|
|
121
|
+
skipCRC32: skipCRC32,
|
|
122
|
+
progress: progress,
|
|
123
|
+
with: consumer)
|
|
124
|
+
case .deflate: checksum = try readCompressed(
|
|
125
|
+
entry: entry,
|
|
126
|
+
bufferSize: bufferSize,
|
|
127
|
+
skipCRC32: skipCRC32,
|
|
128
|
+
progress: progress,
|
|
129
|
+
with: consumer)
|
|
130
|
+
}
|
|
131
|
+
case .directory:
|
|
132
|
+
try consumer(Data())
|
|
133
|
+
progress?.completedUnitCount = totalUnitCountForReading(entry)
|
|
134
|
+
case .symlink:
|
|
135
|
+
let localFileHeader = entry.localFileHeader
|
|
136
|
+
let size = Int(localFileHeader.compressedSize)
|
|
137
|
+
let data = try Data.readChunk(of: size, from: archiveFile)
|
|
138
|
+
checksum = data.crc32(checksum: 0)
|
|
139
|
+
try consumer(data)
|
|
140
|
+
progress?.completedUnitCount = totalUnitCountForReading(entry)
|
|
141
|
+
}
|
|
142
|
+
return checksum
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Archive+ReadingDeprecated.swift
|
|
3
|
+
// ZIPFoundation
|
|
4
|
+
//
|
|
5
|
+
// Copyright © 2017-2021 Thomas Zoechling, https://www.peakstep.com and the ZIP Foundation project authors.
|
|
6
|
+
// Released under the MIT License.
|
|
7
|
+
//
|
|
8
|
+
// See https://github.com/weichsel/ZIPFoundation/blob/master/LICENSE for license information.
|
|
9
|
+
//
|
|
10
|
+
|
|
11
|
+
import Foundation
|
|
12
|
+
|
|
13
|
+
extension Archive {
|
|
14
|
+
|
|
15
|
+
@available(
|
|
16
|
+
*,
|
|
17
|
+
deprecated,
|
|
18
|
+
message: "Please use `Int` for `bufferSize`.")
|
|
19
|
+
func extract(
|
|
20
|
+
_ entry: Entry,
|
|
21
|
+
to url: URL,
|
|
22
|
+
bufferSize: UInt32,
|
|
23
|
+
skipCRC32: Bool = false,
|
|
24
|
+
progress: Progress? = nil)
|
|
25
|
+
throws -> CRC32
|
|
26
|
+
{
|
|
27
|
+
try extract(entry, to: url, bufferSize: Int(bufferSize), skipCRC32: skipCRC32, progress: progress)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@available(
|
|
31
|
+
*,
|
|
32
|
+
deprecated,
|
|
33
|
+
message: "Please use `Int` for `bufferSize`.")
|
|
34
|
+
func extract(
|
|
35
|
+
_ entry: Entry,
|
|
36
|
+
bufferSize: UInt32,
|
|
37
|
+
skipCRC32: Bool = false,
|
|
38
|
+
progress: Progress? = nil,
|
|
39
|
+
consumer: Consumer)
|
|
40
|
+
throws -> CRC32
|
|
41
|
+
{
|
|
42
|
+
try extract(
|
|
43
|
+
entry,
|
|
44
|
+
bufferSize: Int(bufferSize),
|
|
45
|
+
skipCRC32: skipCRC32,
|
|
46
|
+
progress: progress,
|
|
47
|
+
consumer: consumer)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Archive+Writing.swift
|
|
3
|
+
// ZIPFoundation
|
|
4
|
+
//
|
|
5
|
+
// Copyright © 2017-2021 Thomas Zoechling, https://www.peakstep.com and the ZIP Foundation project authors.
|
|
6
|
+
// Released under the MIT License.
|
|
7
|
+
//
|
|
8
|
+
// See https://github.com/weichsel/ZIPFoundation/blob/master/LICENSE for license information.
|
|
9
|
+
//
|
|
10
|
+
|
|
11
|
+
import Foundation
|
|
12
|
+
|
|
13
|
+
extension Archive {
|
|
14
|
+
enum ModifyOperation: Int {
|
|
15
|
+
case remove = -1
|
|
16
|
+
case add = 1
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
typealias EndOfCentralDirectoryStructure = (EndOfCentralDirectoryRecord, ZIP64EndOfCentralDirectory?)
|
|
20
|
+
|
|
21
|
+
/// Write files, directories or symlinks to the receiver.
|
|
22
|
+
///
|
|
23
|
+
/// - Parameters:
|
|
24
|
+
/// - path: The path that is used to identify an `Entry` within the `Archive` file.
|
|
25
|
+
/// - baseURL: The base URL of the resource to add.
|
|
26
|
+
/// The `baseURL` combined with `path` must form a fully qualified file URL.
|
|
27
|
+
/// - compressionMethod: Indicates the `CompressionMethod` that should be applied to `Entry`.
|
|
28
|
+
/// By default, no compression will be applied.
|
|
29
|
+
/// - bufferSize: The maximum size of the write buffer and the compression buffer (if needed).
|
|
30
|
+
/// - progress: A progress object that can be used to track or cancel the add operation.
|
|
31
|
+
/// - Throws: An error if the source file cannot be read or the receiver is not writable.
|
|
32
|
+
func addEntry(
|
|
33
|
+
with path: String,
|
|
34
|
+
relativeTo baseURL: URL,
|
|
35
|
+
compressionMethod: CompressionMethod = .none,
|
|
36
|
+
bufferSize: Int = defaultWriteChunkSize,
|
|
37
|
+
progress: Progress? = nil)
|
|
38
|
+
throws
|
|
39
|
+
{
|
|
40
|
+
let fileURL = baseURL.appendingPathComponent(path)
|
|
41
|
+
|
|
42
|
+
try addEntry(
|
|
43
|
+
with: path,
|
|
44
|
+
fileURL: fileURL,
|
|
45
|
+
compressionMethod: compressionMethod,
|
|
46
|
+
bufferSize: bufferSize,
|
|
47
|
+
progress: progress)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// Write files, directories or symlinks to the receiver.
|
|
51
|
+
///
|
|
52
|
+
/// - Parameters:
|
|
53
|
+
/// - path: The path that is used to identify an `Entry` within the `Archive` file.
|
|
54
|
+
/// - fileURL: An absolute file URL referring to the resource to add.
|
|
55
|
+
/// - compressionMethod: Indicates the `CompressionMethod` that should be applied to `Entry`.
|
|
56
|
+
/// By default, no compression will be applied.
|
|
57
|
+
/// - bufferSize: The maximum size of the write buffer and the compression buffer (if needed).
|
|
58
|
+
/// - progress: A progress object that can be used to track or cancel the add operation.
|
|
59
|
+
/// - Throws: An error if the source file cannot be read or the receiver is not writable.
|
|
60
|
+
func addEntry(
|
|
61
|
+
with path: String,
|
|
62
|
+
fileURL: URL,
|
|
63
|
+
compressionMethod: CompressionMethod = .none,
|
|
64
|
+
bufferSize: Int = defaultWriteChunkSize,
|
|
65
|
+
progress: Progress? = nil)
|
|
66
|
+
throws
|
|
67
|
+
{
|
|
68
|
+
let fileManager = FileManager()
|
|
69
|
+
guard fileManager.itemExists(at: fileURL) else {
|
|
70
|
+
throw CocoaError(.fileReadNoSuchFile, userInfo: [NSFilePathErrorKey: fileURL.path])
|
|
71
|
+
}
|
|
72
|
+
let type = try FileManager.typeForItem(at: fileURL)
|
|
73
|
+
// symlinks do not need to be readable
|
|
74
|
+
guard type == .symlink || fileManager.isReadableFile(atPath: fileURL.path) else {
|
|
75
|
+
throw CocoaError(.fileReadNoPermission, userInfo: [NSFilePathErrorKey: url.path])
|
|
76
|
+
}
|
|
77
|
+
let modDate = try FileManager.fileModificationDateTimeForItem(at: fileURL)
|
|
78
|
+
let uncompressedSize = type == .directory ? 0 : try FileManager.fileSizeForItem(at: fileURL)
|
|
79
|
+
let permissions = try FileManager.permissionsForItem(at: fileURL)
|
|
80
|
+
var provider: Provider
|
|
81
|
+
switch type {
|
|
82
|
+
case .file:
|
|
83
|
+
let entryFileSystemRepresentation = fileManager.fileSystemRepresentation(withPath: fileURL.path)
|
|
84
|
+
guard let entryFile: FILEPointer = fopen(entryFileSystemRepresentation, "rb") else {
|
|
85
|
+
throw CocoaError(.fileNoSuchFile)
|
|
86
|
+
}
|
|
87
|
+
defer { fclose(entryFile) }
|
|
88
|
+
provider = { _, _ in try Data.readChunk(of: bufferSize, from: entryFile) }
|
|
89
|
+
try addEntry(
|
|
90
|
+
with: path,
|
|
91
|
+
type: type,
|
|
92
|
+
uncompressedSize: uncompressedSize,
|
|
93
|
+
modificationDate: modDate,
|
|
94
|
+
permissions: permissions,
|
|
95
|
+
compressionMethod: compressionMethod,
|
|
96
|
+
bufferSize: bufferSize,
|
|
97
|
+
progress: progress,
|
|
98
|
+
provider: provider)
|
|
99
|
+
case .directory:
|
|
100
|
+
provider = { _, _ in Data() }
|
|
101
|
+
try addEntry(
|
|
102
|
+
with: path.hasSuffix("/") ? path : path + "/",
|
|
103
|
+
type: type,
|
|
104
|
+
uncompressedSize: uncompressedSize,
|
|
105
|
+
modificationDate: modDate,
|
|
106
|
+
permissions: permissions,
|
|
107
|
+
compressionMethod: compressionMethod,
|
|
108
|
+
bufferSize: bufferSize,
|
|
109
|
+
progress: progress,
|
|
110
|
+
provider: provider)
|
|
111
|
+
case .symlink:
|
|
112
|
+
provider = { _, _ -> Data in
|
|
113
|
+
let linkDestination = try fileManager.destinationOfSymbolicLink(atPath: fileURL.path)
|
|
114
|
+
let linkFileSystemRepresentation = fileManager.fileSystemRepresentation(withPath: linkDestination)
|
|
115
|
+
let linkLength = Int(strlen(linkFileSystemRepresentation))
|
|
116
|
+
let linkBuffer = UnsafeBufferPointer(start: linkFileSystemRepresentation, count: linkLength)
|
|
117
|
+
return Data(buffer: linkBuffer)
|
|
118
|
+
}
|
|
119
|
+
try addEntry(
|
|
120
|
+
with: path,
|
|
121
|
+
type: type,
|
|
122
|
+
uncompressedSize: uncompressedSize,
|
|
123
|
+
modificationDate: modDate,
|
|
124
|
+
permissions: permissions,
|
|
125
|
+
compressionMethod: compressionMethod,
|
|
126
|
+
bufferSize: bufferSize,
|
|
127
|
+
progress: progress,
|
|
128
|
+
provider: provider)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/// Write files, directories or symlinks to the receiver.
|
|
133
|
+
///
|
|
134
|
+
/// - Parameters:
|
|
135
|
+
/// - path: The path that is used to identify an `Entry` within the `Archive` file.
|
|
136
|
+
/// - type: Indicates the `Entry.EntryType` of the added content.
|
|
137
|
+
/// - uncompressedSize: The uncompressed size of the data that is going to be added with `provider`.
|
|
138
|
+
/// - modificationDate: A `Date` describing the file modification date of the `Entry`.
|
|
139
|
+
/// Default is the current `Date`.
|
|
140
|
+
/// - permissions: POSIX file permissions for the `Entry`.
|
|
141
|
+
/// Default is `0`o`644` for files and symlinks and `0`o`755` for directories.
|
|
142
|
+
/// - compressionMethod: Indicates the `CompressionMethod` that should be applied to `Entry`.
|
|
143
|
+
/// By default, no compression will be applied.
|
|
144
|
+
/// - bufferSize: The maximum size of the write buffer and the compression buffer (if needed).
|
|
145
|
+
/// - progress: A progress object that can be used to track or cancel the add operation.
|
|
146
|
+
/// - provider: A closure that accepts a position and a chunk size. Returns a `Data` chunk.
|
|
147
|
+
/// - Throws: An error if the source data is invalid or the receiver is not writable.
|
|
148
|
+
func addEntry(
|
|
149
|
+
with path: String,
|
|
150
|
+
type: Entry.EntryType,
|
|
151
|
+
uncompressedSize: Int64,
|
|
152
|
+
modificationDate: Date = Date(),
|
|
153
|
+
permissions: UInt16? = nil,
|
|
154
|
+
compressionMethod: CompressionMethod = .none,
|
|
155
|
+
bufferSize: Int = defaultWriteChunkSize,
|
|
156
|
+
progress: Progress? = nil,
|
|
157
|
+
provider: Provider)
|
|
158
|
+
throws
|
|
159
|
+
{
|
|
160
|
+
guard accessMode != .read else { throw ArchiveError.unwritableArchive }
|
|
161
|
+
// Directories and symlinks cannot be compressed
|
|
162
|
+
let compressionMethod = type == .file ? compressionMethod : .none
|
|
163
|
+
progress?.totalUnitCount = type == .directory ? defaultDirectoryUnitCount : uncompressedSize
|
|
164
|
+
let (eocdRecord, zip64EOCD) = (endOfCentralDirectoryRecord, zip64EndOfCentralDirectory)
|
|
165
|
+
guard offsetToStartOfCentralDirectory <= .max else { throw ArchiveError.invalidCentralDirectoryOffset }
|
|
166
|
+
var startOfCD = Int64(offsetToStartOfCentralDirectory)
|
|
167
|
+
fseeko(archiveFile, off_t(startOfCD), SEEK_SET)
|
|
168
|
+
let existingSize = sizeOfCentralDirectory
|
|
169
|
+
let existingData = try Data.readChunk(of: Int(existingSize), from: archiveFile)
|
|
170
|
+
fseeko(archiveFile, off_t(startOfCD), SEEK_SET)
|
|
171
|
+
let fileHeaderStart = Int64(ftello(archiveFile))
|
|
172
|
+
let modDateTime = modificationDate.fileModificationDateTime
|
|
173
|
+
defer { fflush(self.archiveFile) }
|
|
174
|
+
do {
|
|
175
|
+
// Local File Header
|
|
176
|
+
var localFileHeader = try writeLocalFileHeader(
|
|
177
|
+
path: path,
|
|
178
|
+
compressionMethod: compressionMethod,
|
|
179
|
+
size: (UInt64(uncompressedSize), 0),
|
|
180
|
+
checksum: 0,
|
|
181
|
+
modificationDateTime: modDateTime)
|
|
182
|
+
// File Data
|
|
183
|
+
let (written, checksum) = try writeEntry(
|
|
184
|
+
uncompressedSize: uncompressedSize,
|
|
185
|
+
type: type,
|
|
186
|
+
compressionMethod: compressionMethod,
|
|
187
|
+
bufferSize: bufferSize,
|
|
188
|
+
progress: progress,
|
|
189
|
+
provider: provider)
|
|
190
|
+
startOfCD = Int64(ftello(archiveFile))
|
|
191
|
+
// Write the local file header a second time. Now with compressedSize (if applicable) and a valid checksum.
|
|
192
|
+
fseeko(archiveFile, off_t(fileHeaderStart), SEEK_SET)
|
|
193
|
+
localFileHeader = try writeLocalFileHeader(
|
|
194
|
+
path: path,
|
|
195
|
+
compressionMethod: compressionMethod,
|
|
196
|
+
size: (UInt64(uncompressedSize), UInt64(written)),
|
|
197
|
+
checksum: checksum,
|
|
198
|
+
modificationDateTime: modDateTime)
|
|
199
|
+
// Central Directory
|
|
200
|
+
fseeko(archiveFile, off_t(startOfCD), SEEK_SET)
|
|
201
|
+
_ = try Data.writeLargeChunk(existingData, size: existingSize, bufferSize: bufferSize, to: archiveFile)
|
|
202
|
+
let permissions = permissions ?? (type == .directory ? defaultDirectoryPermissions : defaultFilePermissions)
|
|
203
|
+
let externalAttributes = FileManager.externalFileAttributesForEntry(of: type, permissions: permissions)
|
|
204
|
+
let centralDir = try writeCentralDirectoryStructure(
|
|
205
|
+
localFileHeader: localFileHeader,
|
|
206
|
+
relativeOffset: UInt64(fileHeaderStart),
|
|
207
|
+
externalFileAttributes: externalAttributes)
|
|
208
|
+
// End of Central Directory Record (including ZIP64 End of Central Directory Record/Locator)
|
|
209
|
+
let startOfEOCD = UInt64(ftello(archiveFile))
|
|
210
|
+
let eocd = try writeEndOfCentralDirectory(
|
|
211
|
+
centralDirectoryStructure: centralDir,
|
|
212
|
+
startOfCentralDirectory: UInt64(startOfCD),
|
|
213
|
+
startOfEndOfCentralDirectory: startOfEOCD,
|
|
214
|
+
operation: .add)
|
|
215
|
+
(endOfCentralDirectoryRecord, zip64EndOfCentralDirectory) = eocd
|
|
216
|
+
} catch ArchiveError.cancelledOperation {
|
|
217
|
+
try rollback(UInt64(fileHeaderStart), (existingData, existingSize), bufferSize, eocdRecord, zip64EOCD)
|
|
218
|
+
throw ArchiveError.cancelledOperation
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/// Remove a ZIP `Entry` from the receiver.
|
|
223
|
+
///
|
|
224
|
+
/// - Parameters:
|
|
225
|
+
/// - entry: The `Entry` to remove.
|
|
226
|
+
/// - bufferSize: The maximum size for the read and write buffers used during removal.
|
|
227
|
+
/// - progress: A progress object that can be used to track or cancel the remove operation.
|
|
228
|
+
/// - Throws: An error if the `Entry` is malformed or the receiver is not writable.
|
|
229
|
+
func remove(_ entry: Entry, bufferSize: Int = defaultReadChunkSize, progress: Progress? = nil) throws {
|
|
230
|
+
guard accessMode != .read else { throw ArchiveError.unwritableArchive }
|
|
231
|
+
let (tempArchive, tempDir) = try makeTempArchive()
|
|
232
|
+
defer { tempDir.map { try? FileManager().removeItem(at: $0) } }
|
|
233
|
+
progress?.totalUnitCount = totalUnitCountForRemoving(entry)
|
|
234
|
+
var centralDirectoryData = Data()
|
|
235
|
+
var offset: UInt64 = 0
|
|
236
|
+
for currentEntry in self {
|
|
237
|
+
let cds = currentEntry.centralDirectoryStructure
|
|
238
|
+
if currentEntry != entry {
|
|
239
|
+
let entryStart = cds.effectiveRelativeOffsetOfLocalHeader
|
|
240
|
+
fseeko(archiveFile, off_t(entryStart), SEEK_SET)
|
|
241
|
+
let provider: Provider = { _, chunkSize -> Data in
|
|
242
|
+
try Data.readChunk(of: chunkSize, from: self.archiveFile)
|
|
243
|
+
}
|
|
244
|
+
let consumer: Consumer = {
|
|
245
|
+
if progress?.isCancelled == true { throw ArchiveError.cancelledOperation }
|
|
246
|
+
_ = try Data.write(chunk: $0, to: tempArchive.archiveFile)
|
|
247
|
+
progress?.completedUnitCount += Int64($0.count)
|
|
248
|
+
}
|
|
249
|
+
guard currentEntry.localSize <= .max else { throw ArchiveError.invalidLocalHeaderSize }
|
|
250
|
+
_ = try Data.consumePart(
|
|
251
|
+
of: Int64(currentEntry.localSize),
|
|
252
|
+
chunkSize: bufferSize,
|
|
253
|
+
provider: provider,
|
|
254
|
+
consumer: consumer)
|
|
255
|
+
let updatedCentralDirectory = updateOffsetInCentralDirectory(
|
|
256
|
+
centralDirectoryStructure: cds,
|
|
257
|
+
updatedOffset: entryStart - offset)
|
|
258
|
+
centralDirectoryData.append(updatedCentralDirectory.data)
|
|
259
|
+
} else { offset = currentEntry.localSize }
|
|
260
|
+
}
|
|
261
|
+
let startOfCentralDirectory = UInt64(ftello(tempArchive.archiveFile))
|
|
262
|
+
_ = try Data.write(chunk: centralDirectoryData, to: tempArchive.archiveFile)
|
|
263
|
+
let startOfEndOfCentralDirectory = UInt64(ftello(tempArchive.archiveFile))
|
|
264
|
+
tempArchive.endOfCentralDirectoryRecord = endOfCentralDirectoryRecord
|
|
265
|
+
tempArchive.zip64EndOfCentralDirectory = zip64EndOfCentralDirectory
|
|
266
|
+
let ecodStructure = try
|
|
267
|
+
tempArchive.writeEndOfCentralDirectory(
|
|
268
|
+
centralDirectoryStructure: entry.centralDirectoryStructure,
|
|
269
|
+
startOfCentralDirectory: startOfCentralDirectory,
|
|
270
|
+
startOfEndOfCentralDirectory: startOfEndOfCentralDirectory,
|
|
271
|
+
operation: .remove)
|
|
272
|
+
(tempArchive.endOfCentralDirectoryRecord, tempArchive.zip64EndOfCentralDirectory) = ecodStructure
|
|
273
|
+
(endOfCentralDirectoryRecord, zip64EndOfCentralDirectory) = ecodStructure
|
|
274
|
+
fflush(tempArchive.archiveFile)
|
|
275
|
+
try replaceCurrentArchive(with: tempArchive)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
func replaceCurrentArchive(with archive: Archive) throws {
|
|
279
|
+
fclose(archiveFile)
|
|
280
|
+
if isMemoryArchive {
|
|
281
|
+
#if swift(>=5.0)
|
|
282
|
+
guard
|
|
283
|
+
let data = archive.data,
|
|
284
|
+
let config = Archive.makeBackingConfiguration(for: data, mode: .update) else
|
|
285
|
+
{
|
|
286
|
+
throw ArchiveError.unwritableArchive
|
|
287
|
+
}
|
|
288
|
+
archiveFile = config.file
|
|
289
|
+
memoryFile = config.memoryFile
|
|
290
|
+
endOfCentralDirectoryRecord = config.endOfCentralDirectoryRecord
|
|
291
|
+
zip64EndOfCentralDirectory = config.zip64EndOfCentralDirectory
|
|
292
|
+
#endif
|
|
293
|
+
} else {
|
|
294
|
+
let fileManager = FileManager()
|
|
295
|
+
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
|
|
296
|
+
do {
|
|
297
|
+
_ = try fileManager.replaceItemAt(url, withItemAt: archive.url)
|
|
298
|
+
} catch {
|
|
299
|
+
_ = try fileManager.removeItem(at: url)
|
|
300
|
+
_ = try fileManager.moveItem(at: archive.url, to: url)
|
|
301
|
+
}
|
|
302
|
+
#else
|
|
303
|
+
_ = try fileManager.removeItem(at: url)
|
|
304
|
+
_ = try fileManager.moveItem(at: archive.url, to: url)
|
|
305
|
+
#endif
|
|
306
|
+
let fileSystemRepresentation = fileManager.fileSystemRepresentation(withPath: url.path)
|
|
307
|
+
guard let file = fopen(fileSystemRepresentation, "rb+") else { throw ArchiveError.unreadableArchive }
|
|
308
|
+
archiveFile = file
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// MARK: - Private
|
|
314
|
+
|
|
315
|
+
extension Archive {
|
|
316
|
+
|
|
317
|
+
private func updateOffsetInCentralDirectory(
|
|
318
|
+
centralDirectoryStructure: CentralDirectoryStructure,
|
|
319
|
+
updatedOffset: UInt64)
|
|
320
|
+
-> CentralDirectoryStructure
|
|
321
|
+
{
|
|
322
|
+
let zip64ExtendedInformation = Entry.ZIP64ExtendedInformation(
|
|
323
|
+
zip64ExtendedInformation: centralDirectoryStructure.zip64ExtendedInformation, offset: updatedOffset)
|
|
324
|
+
let offsetInCD = updatedOffset < maxOffsetOfLocalFileHeader ? UInt32(updatedOffset) : UInt32.max
|
|
325
|
+
return CentralDirectoryStructure(
|
|
326
|
+
centralDirectoryStructure: centralDirectoryStructure,
|
|
327
|
+
zip64ExtendedInformation: zip64ExtendedInformation,
|
|
328
|
+
relativeOffset: offsetInCD)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
private func rollback(
|
|
332
|
+
_ localFileHeaderStart: UInt64,
|
|
333
|
+
_ existingCentralDirectory: (data: Data, size: UInt64),
|
|
334
|
+
_ bufferSize: Int,
|
|
335
|
+
_ endOfCentralDirRecord: EndOfCentralDirectoryRecord,
|
|
336
|
+
_ zip64EndOfCentralDirectory: ZIP64EndOfCentralDirectory?)
|
|
337
|
+
throws
|
|
338
|
+
{
|
|
339
|
+
fflush(archiveFile)
|
|
340
|
+
ftruncate(fileno(archiveFile), off_t(localFileHeaderStart))
|
|
341
|
+
fseeko(archiveFile, off_t(localFileHeaderStart), SEEK_SET)
|
|
342
|
+
_ = try Data.writeLargeChunk(
|
|
343
|
+
existingCentralDirectory.data,
|
|
344
|
+
size: existingCentralDirectory.size,
|
|
345
|
+
bufferSize: bufferSize,
|
|
346
|
+
to: archiveFile)
|
|
347
|
+
_ = try Data.write(chunk: existingCentralDirectory.data, to: archiveFile)
|
|
348
|
+
if let zip64EOCD = zip64EndOfCentralDirectory {
|
|
349
|
+
_ = try Data.write(chunk: zip64EOCD.data, to: archiveFile)
|
|
350
|
+
}
|
|
351
|
+
_ = try Data.write(chunk: endOfCentralDirRecord.data, to: archiveFile)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
private func makeTempArchive() throws -> (Archive, URL?) {
|
|
355
|
+
var archive: Archive
|
|
356
|
+
var url: URL?
|
|
357
|
+
if isMemoryArchive {
|
|
358
|
+
#if swift(>=5.0)
|
|
359
|
+
guard
|
|
360
|
+
let tempArchive = Archive(
|
|
361
|
+
data: Data(),
|
|
362
|
+
accessMode: .create,
|
|
363
|
+
preferredEncoding: preferredEncoding) else
|
|
364
|
+
{
|
|
365
|
+
throw ArchiveError.unwritableArchive
|
|
366
|
+
}
|
|
367
|
+
archive = tempArchive
|
|
368
|
+
#else
|
|
369
|
+
fatalError("Memory archives are unsupported.")
|
|
370
|
+
#endif
|
|
371
|
+
} else {
|
|
372
|
+
let manager = FileManager()
|
|
373
|
+
let tempDir = URL.temporaryReplacementDirectoryURL(for: self)
|
|
374
|
+
let uniqueString = ProcessInfo.processInfo.globallyUniqueString
|
|
375
|
+
let tempArchiveURL = tempDir.appendingPathComponent(uniqueString)
|
|
376
|
+
try manager.createParentDirectoryStructure(for: tempArchiveURL)
|
|
377
|
+
guard let tempArchive = Archive(url: tempArchiveURL, accessMode: .create) else {
|
|
378
|
+
throw ArchiveError.unwritableArchive
|
|
379
|
+
}
|
|
380
|
+
archive = tempArchive
|
|
381
|
+
url = tempDir
|
|
382
|
+
}
|
|
383
|
+
return (archive, url)
|
|
384
|
+
}
|
|
385
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Archive+WritingDeprecated.swift
|
|
3
|
+
// ZIPFoundation
|
|
4
|
+
//
|
|
5
|
+
// Copyright © 2017-2021 Thomas Zoechling, https://www.peakstep.com and the ZIP Foundation project authors.
|
|
6
|
+
// Released under the MIT License.
|
|
7
|
+
//
|
|
8
|
+
// See https://github.com/weichsel/ZIPFoundation/blob/master/LICENSE for license information.
|
|
9
|
+
//
|
|
10
|
+
|
|
11
|
+
import Foundation
|
|
12
|
+
|
|
13
|
+
extension Archive {
|
|
14
|
+
|
|
15
|
+
@available(
|
|
16
|
+
*,
|
|
17
|
+
deprecated,
|
|
18
|
+
message: "Please use `Int` for `bufferSize`.")
|
|
19
|
+
func addEntry(
|
|
20
|
+
with path: String,
|
|
21
|
+
relativeTo baseURL: URL,
|
|
22
|
+
compressionMethod: CompressionMethod = .none,
|
|
23
|
+
bufferSize: UInt32,
|
|
24
|
+
progress: Progress? = nil)
|
|
25
|
+
throws
|
|
26
|
+
{
|
|
27
|
+
try addEntry(
|
|
28
|
+
with: path,
|
|
29
|
+
relativeTo: baseURL,
|
|
30
|
+
compressionMethod: compressionMethod,
|
|
31
|
+
bufferSize: Int(bufferSize),
|
|
32
|
+
progress: progress)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@available(
|
|
36
|
+
*,
|
|
37
|
+
deprecated,
|
|
38
|
+
message: "Please use `Int` for `bufferSize`.")
|
|
39
|
+
func addEntry(
|
|
40
|
+
with path: String,
|
|
41
|
+
fileURL: URL,
|
|
42
|
+
compressionMethod: CompressionMethod = .none,
|
|
43
|
+
bufferSize: UInt32,
|
|
44
|
+
progress: Progress? = nil)
|
|
45
|
+
throws
|
|
46
|
+
{
|
|
47
|
+
try addEntry(
|
|
48
|
+
with: path,
|
|
49
|
+
fileURL: fileURL,
|
|
50
|
+
compressionMethod: compressionMethod,
|
|
51
|
+
bufferSize: Int(bufferSize),
|
|
52
|
+
progress: progress)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@available(
|
|
56
|
+
*,
|
|
57
|
+
deprecated,
|
|
58
|
+
message: "Please use `Int64` for `uncompressedSize` and provider `position`. `Int` for `bufferSize`.")
|
|
59
|
+
func addEntry(
|
|
60
|
+
with path: String,
|
|
61
|
+
type: Entry.EntryType,
|
|
62
|
+
uncompressedSize: UInt32,
|
|
63
|
+
modificationDate: Date = Date(),
|
|
64
|
+
permissions: UInt16? = nil,
|
|
65
|
+
compressionMethod: CompressionMethod = .none,
|
|
66
|
+
bufferSize: Int = defaultWriteChunkSize,
|
|
67
|
+
progress: Progress? = nil,
|
|
68
|
+
provider: (_ position: Int, _ size: Int) throws -> Data)
|
|
69
|
+
throws
|
|
70
|
+
{
|
|
71
|
+
let newProvider: Provider = { try provider(Int($0), $1) }
|
|
72
|
+
try addEntry(
|
|
73
|
+
with: path,
|
|
74
|
+
type: type,
|
|
75
|
+
uncompressedSize: Int64(uncompressedSize),
|
|
76
|
+
modificationDate: modificationDate,
|
|
77
|
+
permissions: permissions,
|
|
78
|
+
compressionMethod: compressionMethod,
|
|
79
|
+
bufferSize: bufferSize,
|
|
80
|
+
progress: progress,
|
|
81
|
+
provider: newProvider)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@available(
|
|
85
|
+
*,
|
|
86
|
+
deprecated,
|
|
87
|
+
message: "Please use `Int` for `bufferSize`.")
|
|
88
|
+
func remove(_ entry: Entry, bufferSize: UInt32, progress: Progress? = nil) throws {
|
|
89
|
+
try remove(entry, bufferSize: Int(bufferSize), progress: progress)
|
|
90
|
+
}
|
|
91
|
+
}
|