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,351 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Archive+Helpers.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
|
+
// MARK: - Reading
|
|
16
|
+
|
|
17
|
+
func readUncompressed(
|
|
18
|
+
entry: Entry,
|
|
19
|
+
bufferSize: Int,
|
|
20
|
+
skipCRC32: Bool,
|
|
21
|
+
progress: Progress? = nil,
|
|
22
|
+
with consumer: Consumer)
|
|
23
|
+
throws -> CRC32
|
|
24
|
+
{
|
|
25
|
+
let size = entry.centralDirectoryStructure.effectiveUncompressedSize
|
|
26
|
+
guard size <= .max else { throw ArchiveError.invalidEntrySize }
|
|
27
|
+
return try Data.consumePart(
|
|
28
|
+
of: Int64(size),
|
|
29
|
+
chunkSize: bufferSize,
|
|
30
|
+
skipCRC32: skipCRC32,
|
|
31
|
+
provider: { _, chunkSize -> Data in
|
|
32
|
+
try Data.readChunk(of: chunkSize, from: self.archiveFile)
|
|
33
|
+
},
|
|
34
|
+
consumer: { data in
|
|
35
|
+
if progress?.isCancelled == true { throw ArchiveError.cancelledOperation }
|
|
36
|
+
try consumer(data)
|
|
37
|
+
progress?.completedUnitCount += Int64(data.count)
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
func readCompressed(
|
|
42
|
+
entry: Entry,
|
|
43
|
+
bufferSize: Int,
|
|
44
|
+
skipCRC32: Bool,
|
|
45
|
+
progress: Progress? = nil,
|
|
46
|
+
with consumer: Consumer)
|
|
47
|
+
throws -> CRC32
|
|
48
|
+
{
|
|
49
|
+
let size = entry.centralDirectoryStructure.effectiveCompressedSize
|
|
50
|
+
guard size <= .max else { throw ArchiveError.invalidEntrySize }
|
|
51
|
+
return try Data.decompress(
|
|
52
|
+
size: Int64(size),
|
|
53
|
+
bufferSize: bufferSize,
|
|
54
|
+
skipCRC32: skipCRC32,
|
|
55
|
+
provider: { _, chunkSize -> Data in
|
|
56
|
+
try Data.readChunk(of: chunkSize, from: self.archiveFile)
|
|
57
|
+
},
|
|
58
|
+
consumer: { data in
|
|
59
|
+
if progress?.isCancelled == true { throw ArchiveError.cancelledOperation }
|
|
60
|
+
try consumer(data)
|
|
61
|
+
progress?.completedUnitCount += Int64(data.count)
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// MARK: - Writing
|
|
66
|
+
|
|
67
|
+
func writeEntry(
|
|
68
|
+
uncompressedSize: Int64,
|
|
69
|
+
type: Entry.EntryType,
|
|
70
|
+
compressionMethod: CompressionMethod,
|
|
71
|
+
bufferSize: Int,
|
|
72
|
+
progress: Progress? = nil,
|
|
73
|
+
provider: Provider) throws -> (sizeWritten: Int64, crc32: CRC32)
|
|
74
|
+
{
|
|
75
|
+
var checksum = CRC32(0)
|
|
76
|
+
var sizeWritten = Int64(0)
|
|
77
|
+
switch type {
|
|
78
|
+
case .file:
|
|
79
|
+
switch compressionMethod {
|
|
80
|
+
case .none:
|
|
81
|
+
(sizeWritten, checksum) = try writeUncompressed(
|
|
82
|
+
size: uncompressedSize,
|
|
83
|
+
bufferSize: bufferSize,
|
|
84
|
+
progress: progress,
|
|
85
|
+
provider: provider)
|
|
86
|
+
case .deflate:
|
|
87
|
+
(sizeWritten, checksum) = try writeCompressed(
|
|
88
|
+
size: uncompressedSize,
|
|
89
|
+
bufferSize: bufferSize,
|
|
90
|
+
progress: progress,
|
|
91
|
+
provider: provider)
|
|
92
|
+
}
|
|
93
|
+
case .directory:
|
|
94
|
+
_ = try provider(0, 0)
|
|
95
|
+
if let progress = progress { progress.completedUnitCount = progress.totalUnitCount }
|
|
96
|
+
case .symlink:
|
|
97
|
+
let (linkSizeWritten, linkChecksum) = try writeSymbolicLink(
|
|
98
|
+
size: Int(uncompressedSize),
|
|
99
|
+
provider: provider)
|
|
100
|
+
(sizeWritten, checksum) = (Int64(linkSizeWritten), linkChecksum)
|
|
101
|
+
if let progress = progress { progress.completedUnitCount = progress.totalUnitCount }
|
|
102
|
+
}
|
|
103
|
+
return (sizeWritten, checksum)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
func writeLocalFileHeader(
|
|
107
|
+
path: String,
|
|
108
|
+
compressionMethod: CompressionMethod,
|
|
109
|
+
size: (uncompressed: UInt64, compressed: UInt64),
|
|
110
|
+
checksum: CRC32,
|
|
111
|
+
modificationDateTime: (UInt16, UInt16))
|
|
112
|
+
throws -> LocalFileHeader
|
|
113
|
+
{
|
|
114
|
+
// We always set Bit 11 in generalPurposeBitFlag, which indicates an UTF-8 encoded path.
|
|
115
|
+
guard let fileNameData = path.data(using: .utf8) else { throw ArchiveError.invalidEntryPath }
|
|
116
|
+
|
|
117
|
+
var uncompressedSizeOfLFH = UInt32(0)
|
|
118
|
+
var compressedSizeOfLFH = UInt32(0)
|
|
119
|
+
var extraFieldLength = UInt16(0)
|
|
120
|
+
var zip64ExtendedInformation: Entry.ZIP64ExtendedInformation?
|
|
121
|
+
var versionNeededToExtract = Version.v20.rawValue
|
|
122
|
+
// ZIP64 Extended Information in the Local header MUST include BOTH original and compressed file size fields.
|
|
123
|
+
if size.uncompressed >= maxUncompressedSize || size.compressed >= maxCompressedSize {
|
|
124
|
+
uncompressedSizeOfLFH = .max
|
|
125
|
+
compressedSizeOfLFH = .max
|
|
126
|
+
extraFieldLength = UInt16(20) // 2 + 2 + 8 + 8
|
|
127
|
+
versionNeededToExtract = Version.v45.rawValue
|
|
128
|
+
zip64ExtendedInformation = Entry.ZIP64ExtendedInformation(
|
|
129
|
+
dataSize: extraFieldLength - 4,
|
|
130
|
+
uncompressedSize: size.uncompressed,
|
|
131
|
+
compressedSize: size.compressed,
|
|
132
|
+
relativeOffsetOfLocalHeader: 0,
|
|
133
|
+
diskNumberStart: 0)
|
|
134
|
+
} else {
|
|
135
|
+
uncompressedSizeOfLFH = UInt32(size.uncompressed)
|
|
136
|
+
compressedSizeOfLFH = UInt32(size.compressed)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let localFileHeader = LocalFileHeader(
|
|
140
|
+
versionNeededToExtract: versionNeededToExtract,
|
|
141
|
+
generalPurposeBitFlag: UInt16(2048),
|
|
142
|
+
compressionMethod: compressionMethod.rawValue,
|
|
143
|
+
lastModFileTime: modificationDateTime.1,
|
|
144
|
+
lastModFileDate: modificationDateTime.0,
|
|
145
|
+
crc32: checksum,
|
|
146
|
+
compressedSize: compressedSizeOfLFH,
|
|
147
|
+
uncompressedSize: uncompressedSizeOfLFH,
|
|
148
|
+
fileNameLength: UInt16(fileNameData.count),
|
|
149
|
+
extraFieldLength: extraFieldLength,
|
|
150
|
+
fileNameData: fileNameData,
|
|
151
|
+
extraFieldData: zip64ExtendedInformation?.data ?? Data())
|
|
152
|
+
_ = try Data.write(chunk: localFileHeader.data, to: archiveFile)
|
|
153
|
+
return localFileHeader
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
func writeCentralDirectoryStructure(
|
|
157
|
+
localFileHeader: LocalFileHeader,
|
|
158
|
+
relativeOffset: UInt64,
|
|
159
|
+
externalFileAttributes: UInt32)
|
|
160
|
+
throws -> CentralDirectoryStructure
|
|
161
|
+
{
|
|
162
|
+
var extraUncompressedSize: UInt64?
|
|
163
|
+
var extraCompressedSize: UInt64?
|
|
164
|
+
var extraOffset: UInt64?
|
|
165
|
+
var relativeOffsetOfCD = UInt32(0)
|
|
166
|
+
var extraFieldLength = UInt16(0)
|
|
167
|
+
var zip64ExtendedInformation: Entry.ZIP64ExtendedInformation?
|
|
168
|
+
if localFileHeader.uncompressedSize == .max || localFileHeader.compressedSize == .max {
|
|
169
|
+
let zip64Field = Entry.ZIP64ExtendedInformation
|
|
170
|
+
.scanForZIP64Field(in: localFileHeader.extraFieldData, fields: [.uncompressedSize, .compressedSize])
|
|
171
|
+
extraUncompressedSize = zip64Field?.uncompressedSize
|
|
172
|
+
extraCompressedSize = zip64Field?.compressedSize
|
|
173
|
+
}
|
|
174
|
+
if relativeOffset >= maxOffsetOfLocalFileHeader {
|
|
175
|
+
extraOffset = relativeOffset
|
|
176
|
+
relativeOffsetOfCD = .max
|
|
177
|
+
} else {
|
|
178
|
+
relativeOffsetOfCD = UInt32(relativeOffset)
|
|
179
|
+
}
|
|
180
|
+
extraFieldLength = [extraUncompressedSize, extraCompressedSize, extraOffset]
|
|
181
|
+
.compactMap { $0 }
|
|
182
|
+
.reduce(UInt16(0)) { $0 + UInt16(MemoryLayout.size(ofValue: $1)) }
|
|
183
|
+
if extraFieldLength > 0 {
|
|
184
|
+
// Size of extra fields, shouldn't include the leading 4 bytes
|
|
185
|
+
zip64ExtendedInformation = Entry.ZIP64ExtendedInformation(
|
|
186
|
+
dataSize: extraFieldLength,
|
|
187
|
+
uncompressedSize: extraUncompressedSize ?? 0,
|
|
188
|
+
compressedSize: extraCompressedSize ?? 0,
|
|
189
|
+
relativeOffsetOfLocalHeader: extraOffset ?? 0,
|
|
190
|
+
diskNumberStart: 0)
|
|
191
|
+
extraFieldLength += Entry.ZIP64ExtendedInformation.headerSize
|
|
192
|
+
}
|
|
193
|
+
let centralDirectory = CentralDirectoryStructure(
|
|
194
|
+
localFileHeader: localFileHeader,
|
|
195
|
+
fileAttributes: externalFileAttributes,
|
|
196
|
+
relativeOffset: relativeOffsetOfCD,
|
|
197
|
+
extraField: (
|
|
198
|
+
extraFieldLength,
|
|
199
|
+
zip64ExtendedInformation?.data ?? Data()))
|
|
200
|
+
_ = try Data.write(chunk: centralDirectory.data, to: archiveFile)
|
|
201
|
+
return centralDirectory
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
func writeEndOfCentralDirectory(
|
|
205
|
+
centralDirectoryStructure: CentralDirectoryStructure,
|
|
206
|
+
startOfCentralDirectory: UInt64,
|
|
207
|
+
startOfEndOfCentralDirectory: UInt64,
|
|
208
|
+
operation: ModifyOperation)
|
|
209
|
+
throws -> EndOfCentralDirectoryStructure
|
|
210
|
+
{
|
|
211
|
+
var record = endOfCentralDirectoryRecord
|
|
212
|
+
let sizeOfCD = sizeOfCentralDirectory
|
|
213
|
+
let numberOfTotalEntries = totalNumberOfEntriesInCentralDirectory
|
|
214
|
+
let countChange = operation.rawValue
|
|
215
|
+
var dataLength = centralDirectoryStructure.extraFieldLength
|
|
216
|
+
dataLength += centralDirectoryStructure.fileNameLength
|
|
217
|
+
dataLength += centralDirectoryStructure.fileCommentLength
|
|
218
|
+
let cdDataLengthChange = countChange * (Int(dataLength) + CentralDirectoryStructure.size)
|
|
219
|
+
let (updatedSizeOfCD, updatedNumberOfEntries): (UInt64, UInt64) = try {
|
|
220
|
+
switch operation {
|
|
221
|
+
case .add:
|
|
222
|
+
guard .max - sizeOfCD >= cdDataLengthChange else {
|
|
223
|
+
throw ArchiveError.invalidCentralDirectorySize
|
|
224
|
+
}
|
|
225
|
+
guard .max - numberOfTotalEntries >= countChange else {
|
|
226
|
+
throw ArchiveError.invalidCentralDirectoryEntryCount
|
|
227
|
+
}
|
|
228
|
+
return (sizeOfCD + UInt64(cdDataLengthChange), numberOfTotalEntries + UInt64(countChange))
|
|
229
|
+
case .remove:
|
|
230
|
+
return (sizeOfCD - UInt64(-cdDataLengthChange), numberOfTotalEntries - UInt64(-countChange))
|
|
231
|
+
}
|
|
232
|
+
}()
|
|
233
|
+
let sizeOfCDForEOCD = updatedSizeOfCD >= maxSizeOfCentralDirectory
|
|
234
|
+
? UInt32.max
|
|
235
|
+
: UInt32(updatedSizeOfCD)
|
|
236
|
+
let numberOfTotalEntriesForEOCD = updatedNumberOfEntries >= maxTotalNumberOfEntries
|
|
237
|
+
? UInt16.max
|
|
238
|
+
: UInt16(updatedNumberOfEntries)
|
|
239
|
+
let offsetOfCDForEOCD = startOfCentralDirectory >= maxOffsetOfCentralDirectory
|
|
240
|
+
? UInt32.max
|
|
241
|
+
: UInt32(startOfCentralDirectory)
|
|
242
|
+
// ZIP64 End of Central Directory
|
|
243
|
+
var zip64EOCD: ZIP64EndOfCentralDirectory?
|
|
244
|
+
if numberOfTotalEntriesForEOCD == .max || offsetOfCDForEOCD == .max || sizeOfCDForEOCD == .max {
|
|
245
|
+
zip64EOCD = try writeZIP64EOCD(
|
|
246
|
+
totalNumberOfEntries: updatedNumberOfEntries,
|
|
247
|
+
sizeOfCentralDirectory: updatedSizeOfCD,
|
|
248
|
+
offsetOfCentralDirectory: startOfCentralDirectory,
|
|
249
|
+
offsetOfEndOfCentralDirectory: startOfEndOfCentralDirectory)
|
|
250
|
+
}
|
|
251
|
+
record = EndOfCentralDirectoryRecord(
|
|
252
|
+
record: record,
|
|
253
|
+
numberOfEntriesOnDisk: numberOfTotalEntriesForEOCD,
|
|
254
|
+
numberOfEntriesInCentralDirectory: numberOfTotalEntriesForEOCD,
|
|
255
|
+
updatedSizeOfCentralDirectory: sizeOfCDForEOCD,
|
|
256
|
+
startOfCentralDirectory: offsetOfCDForEOCD)
|
|
257
|
+
_ = try Data.write(chunk: record.data, to: archiveFile)
|
|
258
|
+
return (record, zip64EOCD)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
func writeUncompressed(
|
|
262
|
+
size: Int64,
|
|
263
|
+
bufferSize: Int,
|
|
264
|
+
progress: Progress? = nil,
|
|
265
|
+
provider: Provider) throws -> (sizeWritten: Int64, checksum: CRC32)
|
|
266
|
+
{
|
|
267
|
+
var position: Int64 = 0
|
|
268
|
+
var sizeWritten: Int64 = 0
|
|
269
|
+
var checksum = CRC32(0)
|
|
270
|
+
while position < size {
|
|
271
|
+
if progress?.isCancelled == true { throw ArchiveError.cancelledOperation }
|
|
272
|
+
let readSize = (size - position) >= bufferSize ? bufferSize : Int(size - position)
|
|
273
|
+
let entryChunk = try provider(position, readSize)
|
|
274
|
+
checksum = entryChunk.crc32(checksum: checksum)
|
|
275
|
+
sizeWritten += Int64(try Data.write(chunk: entryChunk, to: archiveFile))
|
|
276
|
+
position += Int64(bufferSize)
|
|
277
|
+
progress?.completedUnitCount = sizeWritten
|
|
278
|
+
}
|
|
279
|
+
return (sizeWritten, checksum)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
func writeCompressed(
|
|
283
|
+
size: Int64,
|
|
284
|
+
bufferSize: Int,
|
|
285
|
+
progress: Progress? = nil,
|
|
286
|
+
provider: Provider) throws -> (sizeWritten: Int64, checksum: CRC32)
|
|
287
|
+
{
|
|
288
|
+
var sizeWritten: Int64 = 0
|
|
289
|
+
let consumer: Consumer = { data in sizeWritten += Int64(try Data.write(chunk: data, to: self.archiveFile)) }
|
|
290
|
+
let checksum = try Data.compress(
|
|
291
|
+
size: size,
|
|
292
|
+
bufferSize: bufferSize,
|
|
293
|
+
provider: { position, size -> Data in
|
|
294
|
+
if progress?.isCancelled == true { throw ArchiveError.cancelledOperation }
|
|
295
|
+
let data = try provider(position, size)
|
|
296
|
+
progress?.completedUnitCount += Int64(data.count)
|
|
297
|
+
return data
|
|
298
|
+
},
|
|
299
|
+
consumer: consumer)
|
|
300
|
+
return (sizeWritten, checksum)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
func writeSymbolicLink(size: Int, provider: Provider) throws -> (sizeWritten: Int, checksum: CRC32) {
|
|
304
|
+
// The reported size of a symlink is the number of characters in the path it points to.
|
|
305
|
+
let linkData = try provider(0, size)
|
|
306
|
+
let checksum = linkData.crc32(checksum: 0)
|
|
307
|
+
let sizeWritten = try Data.write(chunk: linkData, to: archiveFile)
|
|
308
|
+
return (sizeWritten, checksum)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
func writeZIP64EOCD(
|
|
312
|
+
totalNumberOfEntries: UInt64,
|
|
313
|
+
sizeOfCentralDirectory: UInt64,
|
|
314
|
+
offsetOfCentralDirectory: UInt64,
|
|
315
|
+
offsetOfEndOfCentralDirectory: UInt64)
|
|
316
|
+
throws -> ZIP64EndOfCentralDirectory
|
|
317
|
+
{
|
|
318
|
+
var zip64EOCD: ZIP64EndOfCentralDirectory = zip64EndOfCentralDirectory ?? {
|
|
319
|
+
// Shouldn't include the leading 12 bytes: (size - 12 = 44)
|
|
320
|
+
let record = ZIP64EndOfCentralDirectoryRecord(
|
|
321
|
+
sizeOfZIP64EndOfCentralDirectoryRecord: UInt64(44),
|
|
322
|
+
versionMadeBy: UInt16(789),
|
|
323
|
+
versionNeededToExtract: Version.v45.rawValue,
|
|
324
|
+
numberOfDisk: 0,
|
|
325
|
+
numberOfDiskStart: 0,
|
|
326
|
+
totalNumberOfEntriesOnDisk: 0,
|
|
327
|
+
totalNumberOfEntriesInCentralDirectory: 0,
|
|
328
|
+
sizeOfCentralDirectory: 0,
|
|
329
|
+
offsetToStartOfCentralDirectory: 0,
|
|
330
|
+
zip64ExtensibleDataSector: Data())
|
|
331
|
+
let locator = ZIP64EndOfCentralDirectoryLocator(
|
|
332
|
+
numberOfDiskWithZIP64EOCDRecordStart: 0,
|
|
333
|
+
relativeOffsetOfZIP64EOCDRecord: 0,
|
|
334
|
+
totalNumberOfDisk: 1)
|
|
335
|
+
return ZIP64EndOfCentralDirectory(record: record, locator: locator)
|
|
336
|
+
}()
|
|
337
|
+
|
|
338
|
+
let updatedRecord = ZIP64EndOfCentralDirectoryRecord(
|
|
339
|
+
record: zip64EOCD.record,
|
|
340
|
+
numberOfEntriesOnDisk: totalNumberOfEntries,
|
|
341
|
+
numberOfEntriesInCD: totalNumberOfEntries,
|
|
342
|
+
sizeOfCentralDirectory: sizeOfCentralDirectory,
|
|
343
|
+
offsetToStartOfCD: offsetOfCentralDirectory)
|
|
344
|
+
let updatedLocator = ZIP64EndOfCentralDirectoryLocator(
|
|
345
|
+
locator: zip64EOCD.locator,
|
|
346
|
+
offsetOfZIP64EOCDRecord: offsetOfEndOfCentralDirectory)
|
|
347
|
+
zip64EOCD = ZIP64EndOfCentralDirectory(record: updatedRecord, locator: updatedLocator)
|
|
348
|
+
_ = try Data.write(chunk: zip64EOCD.data, to: archiveFile)
|
|
349
|
+
return zip64EOCD
|
|
350
|
+
}
|
|
351
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Archive+MemoryFile.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
|
+
var isMemoryArchive: Bool { url.scheme == memoryURLScheme }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
#if swift(>=5.0)
|
|
18
|
+
|
|
19
|
+
extension Archive {
|
|
20
|
+
/// Returns a `Data` object containing a representation of the receiver.
|
|
21
|
+
var data: Data? { memoryFile?.data }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class MemoryFile {
|
|
25
|
+
|
|
26
|
+
// MARK: Lifecycle
|
|
27
|
+
|
|
28
|
+
init(data: Data = Data()) {
|
|
29
|
+
self.data = data
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// MARK: Internal
|
|
33
|
+
|
|
34
|
+
private(set) var data: Data
|
|
35
|
+
|
|
36
|
+
func open(mode: String) -> FILEPointer? {
|
|
37
|
+
let cookie = Unmanaged.passRetained(self)
|
|
38
|
+
let writable = mode.count > 0 && (mode.first! != "r" || mode.last! == "+")
|
|
39
|
+
let append = mode.count > 0 && mode.first! == "a"
|
|
40
|
+
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) || os(Android)
|
|
41
|
+
let result = writable
|
|
42
|
+
? funopen(cookie.toOpaque(), readStub, writeStub, seekStub, closeStub)
|
|
43
|
+
: funopen(cookie.toOpaque(), readStub, nil, seekStub, closeStub)
|
|
44
|
+
#else
|
|
45
|
+
let stubs = cookie_io_functions_t(read: readStub, write: writeStub, seek: seekStub, close: closeStub)
|
|
46
|
+
let result = fopencookie(cookie.toOpaque(), mode, stubs)
|
|
47
|
+
#endif
|
|
48
|
+
if append {
|
|
49
|
+
fseeko(result, 0, SEEK_END)
|
|
50
|
+
}
|
|
51
|
+
return result
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// MARK: Private
|
|
55
|
+
|
|
56
|
+
private var offset = 0
|
|
57
|
+
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
extension MemoryFile {
|
|
61
|
+
fileprivate func readData(buffer: UnsafeMutableRawBufferPointer) -> Int {
|
|
62
|
+
let size = min(buffer.count, data.count - offset)
|
|
63
|
+
let start = data.startIndex
|
|
64
|
+
data.copyBytes(to: buffer.bindMemory(to: UInt8.self), from: start + offset..<start + offset + size)
|
|
65
|
+
offset += size
|
|
66
|
+
return size
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
fileprivate func writeData(buffer: UnsafeRawBufferPointer) -> Int {
|
|
70
|
+
let start = data.startIndex
|
|
71
|
+
if offset < data.count, offset + buffer.count > data.count {
|
|
72
|
+
data.removeSubrange(start + offset..<start + data.count)
|
|
73
|
+
} else if offset > data.count {
|
|
74
|
+
data.append(Data(count: offset - data.count))
|
|
75
|
+
}
|
|
76
|
+
if offset == data.count {
|
|
77
|
+
data.append(buffer.bindMemory(to: UInt8.self))
|
|
78
|
+
} else {
|
|
79
|
+
let start = data.startIndex // May have changed in earlier mutation
|
|
80
|
+
data.replaceSubrange(start + offset..<start + offset + buffer.count, with: buffer.bindMemory(to: UInt8.self))
|
|
81
|
+
}
|
|
82
|
+
offset += buffer.count
|
|
83
|
+
return buffer.count
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
fileprivate func seek(offset: Int, whence: Int32) -> Int {
|
|
87
|
+
var result = -1
|
|
88
|
+
if whence == SEEK_SET {
|
|
89
|
+
result = offset
|
|
90
|
+
} else if whence == SEEK_CUR {
|
|
91
|
+
result = self.offset + offset
|
|
92
|
+
} else if whence == SEEK_END {
|
|
93
|
+
result = data.count + offset
|
|
94
|
+
}
|
|
95
|
+
self.offset = result
|
|
96
|
+
return self.offset
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private func fileFromCookie(cookie: UnsafeRawPointer) -> MemoryFile {
|
|
101
|
+
Unmanaged<MemoryFile>.fromOpaque(cookie).takeUnretainedValue()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private func closeStub(_ cookie: UnsafeMutableRawPointer?) -> Int32 {
|
|
105
|
+
if let cookie = cookie {
|
|
106
|
+
Unmanaged<MemoryFile>.fromOpaque(cookie).release()
|
|
107
|
+
}
|
|
108
|
+
return 0
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) || os(Android)
|
|
112
|
+
private func readStub(
|
|
113
|
+
_ cookie: UnsafeMutableRawPointer?,
|
|
114
|
+
_ bytePtr: UnsafeMutablePointer<Int8>?,
|
|
115
|
+
_ count: Int32)
|
|
116
|
+
-> Int32
|
|
117
|
+
{
|
|
118
|
+
guard let cookie = cookie, let bytePtr = bytePtr else { return 0 }
|
|
119
|
+
return Int32(fileFromCookie(cookie: cookie).readData(
|
|
120
|
+
buffer: UnsafeMutableRawBufferPointer(start: bytePtr, count: Int(count))))
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private func writeStub(
|
|
124
|
+
_ cookie: UnsafeMutableRawPointer?,
|
|
125
|
+
_ bytePtr: UnsafePointer<Int8>?,
|
|
126
|
+
_ count: Int32)
|
|
127
|
+
-> Int32
|
|
128
|
+
{
|
|
129
|
+
guard let cookie = cookie, let bytePtr = bytePtr else { return 0 }
|
|
130
|
+
return Int32(fileFromCookie(cookie: cookie).writeData(
|
|
131
|
+
buffer: UnsafeRawBufferPointer(start: bytePtr, count: Int(count))))
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private func seekStub(
|
|
135
|
+
_ cookie: UnsafeMutableRawPointer?,
|
|
136
|
+
_ offset: fpos_t,
|
|
137
|
+
_ whence: Int32)
|
|
138
|
+
-> fpos_t
|
|
139
|
+
{
|
|
140
|
+
guard let cookie = cookie else { return 0 }
|
|
141
|
+
return fpos_t(fileFromCookie(cookie: cookie).seek(offset: Int(offset), whence: whence))
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
#else
|
|
145
|
+
private func readStub(
|
|
146
|
+
_ cookie: UnsafeMutableRawPointer?,
|
|
147
|
+
_ bytePtr: UnsafeMutablePointer<Int8>?,
|
|
148
|
+
_ count: Int)
|
|
149
|
+
-> Int
|
|
150
|
+
{
|
|
151
|
+
guard let cookie = cookie, let bytePtr = bytePtr else { return 0 }
|
|
152
|
+
return fileFromCookie(cookie: cookie).readData(
|
|
153
|
+
buffer: UnsafeMutableRawBufferPointer(start: bytePtr, count: count))
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private func writeStub(
|
|
157
|
+
_ cookie: UnsafeMutableRawPointer?,
|
|
158
|
+
_ bytePtr: UnsafePointer<Int8>?,
|
|
159
|
+
_ count: Int)
|
|
160
|
+
-> Int
|
|
161
|
+
{
|
|
162
|
+
guard let cookie = cookie, let bytePtr = bytePtr else { return 0 }
|
|
163
|
+
return fileFromCookie(cookie: cookie).writeData(
|
|
164
|
+
buffer: UnsafeRawBufferPointer(start: bytePtr, count: count))
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private func seekStub(
|
|
168
|
+
_ cookie: UnsafeMutableRawPointer?,
|
|
169
|
+
_ offset: UnsafeMutablePointer<Int>?,
|
|
170
|
+
_ whence: Int32)
|
|
171
|
+
-> Int32
|
|
172
|
+
{
|
|
173
|
+
guard let cookie = cookie, let offset = offset else { return 0 }
|
|
174
|
+
let result = fileFromCookie(cookie: cookie).seek(offset: Int(offset.pointee), whence: whence)
|
|
175
|
+
if result >= 0 {
|
|
176
|
+
offset.pointee = result
|
|
177
|
+
return 0
|
|
178
|
+
} else {
|
|
179
|
+
return -1
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
#endif
|
|
183
|
+
#endif
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Archive+Progress.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
|
+
/// The number of the work units that have to be performed when
|
|
15
|
+
/// removing `entry` from the receiver.
|
|
16
|
+
///
|
|
17
|
+
/// - Parameter entry: The entry that will be removed.
|
|
18
|
+
/// - Returns: The number of the work units.
|
|
19
|
+
func totalUnitCountForRemoving(_ entry: Entry) -> Int64 {
|
|
20
|
+
Int64(offsetToStartOfCentralDirectory - entry.localSize)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
func makeProgressForRemoving(_ entry: Entry) -> Progress {
|
|
24
|
+
Progress(totalUnitCount: totalUnitCountForRemoving(entry))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// The number of the work units that have to be performed when
|
|
28
|
+
/// reading `entry` from the receiver.
|
|
29
|
+
///
|
|
30
|
+
/// - Parameter entry: The entry that will be read.
|
|
31
|
+
/// - Returns: The number of the work units.
|
|
32
|
+
func totalUnitCountForReading(_ entry: Entry) -> Int64 {
|
|
33
|
+
switch entry.type {
|
|
34
|
+
case .file, .symlink:
|
|
35
|
+
return Int64(entry.uncompressedSize)
|
|
36
|
+
case .directory:
|
|
37
|
+
return defaultDirectoryUnitCount
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
func makeProgressForReading(_ entry: Entry) -> Progress {
|
|
42
|
+
Progress(totalUnitCount: totalUnitCountForReading(entry))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// The number of the work units that have to be performed when
|
|
46
|
+
/// adding the file at `url` to the receiver.
|
|
47
|
+
/// - Parameter entry: The entry that will be removed.
|
|
48
|
+
/// - Returns: The number of the work units.
|
|
49
|
+
func totalUnitCountForAddingItem(at url: URL) -> Int64 {
|
|
50
|
+
var count = Int64(0)
|
|
51
|
+
do {
|
|
52
|
+
let type = try FileManager.typeForItem(at: url)
|
|
53
|
+
switch type {
|
|
54
|
+
case .file, .symlink:
|
|
55
|
+
count = Int64(try FileManager.fileSizeForItem(at: url))
|
|
56
|
+
case .directory:
|
|
57
|
+
count = defaultDirectoryUnitCount
|
|
58
|
+
}
|
|
59
|
+
} catch { count = -1 }
|
|
60
|
+
return count
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
func makeProgressForAddingItem(at url: URL) -> Progress {
|
|
64
|
+
Progress(totalUnitCount: totalUnitCountForAddingItem(at: url))
|
|
65
|
+
}
|
|
66
|
+
}
|