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.
Files changed (96) hide show
  1. package/.github/workflows/main.yml +27 -9
  2. package/Lottie.xcodeproj/project.pbxproj +158 -70
  3. package/Lottie.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +2 -2
  4. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/IDEFindNavigatorScopes.plist +5 -0
  5. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  6. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +258 -0
  7. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Expressions.xcexplist +13 -2
  8. package/Package.swift +2 -1
  9. package/README.md +3 -3
  10. package/Rakefile +8 -4
  11. package/Sources/Private/CoreAnimation/Animations/CALayer+addAnimation.swift +16 -2
  12. package/Sources/Private/CoreAnimation/Animations/CombinedShapeAnimation.swift +1 -1
  13. package/Sources/Private/CoreAnimation/Animations/CustomPathAnimation.swift +1 -1
  14. package/Sources/Private/CoreAnimation/Animations/EllipseAnimation.swift +1 -1
  15. package/Sources/Private/CoreAnimation/Animations/GradientAnimations.swift +6 -6
  16. package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +76 -7
  17. package/Sources/Private/CoreAnimation/Animations/OpacityAnimation.swift +1 -1
  18. package/Sources/Private/CoreAnimation/Animations/RectangleAnimation.swift +1 -1
  19. package/Sources/Private/CoreAnimation/Animations/ShapeAnimation.swift +66 -102
  20. package/Sources/Private/CoreAnimation/Animations/StarAnimation.swift +2 -2
  21. package/Sources/Private/CoreAnimation/Animations/StrokeAnimation.swift +3 -3
  22. package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +66 -17
  23. package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +55 -32
  24. package/Sources/Private/CoreAnimation/Extensions/Keyframes+combined.swift +16 -12
  25. package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +3 -3
  26. package/Sources/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +24 -11
  27. package/Sources/Private/CoreAnimation/Layers/ImageLayer.swift +2 -2
  28. package/Sources/Private/CoreAnimation/Layers/PreCompLayer.swift +1 -1
  29. package/Sources/Private/CoreAnimation/Layers/RepeaterLayer.swift +13 -2
  30. package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +9 -1
  31. package/Sources/Private/CoreAnimation/ValueProviderStore.swift +22 -11
  32. package/Sources/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.swift +1 -1
  33. package/Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift +13 -2
  34. package/Sources/Private/MainThread/LayerContainers/Utility/LayerTransformNode.swift +16 -7
  35. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift +1 -1
  36. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift +2 -2
  37. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/RectNode.swift +1 -1
  38. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/StarNode.swift +2 -2
  39. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift +20 -8
  40. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift +1 -1
  41. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift +1 -1
  42. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +1 -1
  43. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +1 -1
  44. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +28 -9
  45. package/Sources/Private/Model/Assets/ImageAsset.swift +4 -3
  46. package/Sources/Private/Model/DotLottie/DotLottieAnimation.swift +2 -8
  47. package/Sources/Private/Model/DotLottie/DotLottieManifest.swift +3 -14
  48. package/Sources/Private/Model/DotLottie/DotLottieUtils.swift +11 -1
  49. package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+BackingConfiguration.swift +147 -0
  50. package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Helpers.swift +351 -0
  51. package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+MemoryFile.swift +183 -0
  52. package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Progress.swift +66 -0
  53. package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Reading.swift +144 -0
  54. package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+ReadingDeprecated.swift +49 -0
  55. package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Writing.swift +385 -0
  56. package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+WritingDeprecated.swift +91 -0
  57. package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+ZIP64.swift +170 -0
  58. package/Sources/Private/Model/DotLottie/{Zip/ZipArchive.swift → ZipFoundation/Archive.swift} +150 -227
  59. package/Sources/Private/Model/DotLottie/ZipFoundation/Data+Compression.swift +403 -0
  60. package/Sources/Private/Model/DotLottie/ZipFoundation/Data+CompressionDeprecated.swift +44 -0
  61. package/Sources/Private/Model/DotLottie/{Zip → ZipFoundation}/Data+Serialization.swift +62 -0
  62. package/Sources/Private/Model/DotLottie/{Zip/ZipEntry+Serialization.swift → ZipFoundation/Entry+Serialization.swift} +7 -7
  63. package/Sources/Private/Model/DotLottie/{Zip/ZipEntry+ZIP64.swift → ZipFoundation/Entry+ZIP64.swift} +13 -19
  64. package/Sources/Private/Model/DotLottie/{Zip/ZipEntry.swift → ZipFoundation/Entry.swift} +141 -10
  65. package/Sources/Private/Model/DotLottie/ZipFoundation/FileManager+ZIP.swift +368 -0
  66. package/Sources/Private/Model/DotLottie/ZipFoundation/README.md +24 -0
  67. package/Sources/Private/Model/DotLottie/ZipFoundation/URL+ZIP.swift +32 -0
  68. package/Sources/Private/Model/Extensions/Bundle.swift +5 -14
  69. package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +31 -8
  70. package/Sources/Private/Model/Objects/Transform.swift +58 -17
  71. package/Sources/Private/Model/ShapeItems/Repeater.swift +41 -7
  72. package/Sources/Private/Model/ShapeItems/ShapeTransform.swift +61 -7
  73. package/Sources/Private/Model/Text/TextAnimator.swift +37 -5
  74. package/Sources/Private/RootAnimationLayer.swift +3 -1
  75. package/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift +12 -4
  76. package/Sources/Private/Utility/Extensions/DataExtension.swift +14 -4
  77. package/Sources/Private/Utility/Primitives/BezierPathRoundExtension.swift +11 -0
  78. package/Sources/Private/Utility/Primitives/ColorExtension.swift +10 -13
  79. package/Sources/Private/Utility/Primitives/VectorsExtensions.swift +28 -6
  80. package/Sources/Public/Animation/LottieAnimationHelpers.swift +12 -10
  81. package/Sources/Public/Animation/LottieAnimationView.swift +213 -186
  82. package/Sources/Public/DotLottie/DotLottieFile.swift +11 -34
  83. package/Sources/Public/DotLottie/DotLottieFileHelpers.swift +101 -74
  84. package/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift +90 -0
  85. package/Sources/Public/iOS/LottieAnimationViewBase.swift +1 -1
  86. package/Sources/Public/macOS/LottieAnimationViewBase.macOS.swift +1 -1
  87. package/lottie-ios.podspec +1 -1
  88. package/package.json +1 -1
  89. package/LottieAnimation/LottieAnimation.xcodeproj/project.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  90. package/LottieAnimation/LottieAnimation.xcodeproj/project.xcworkspace/xcuserdata/valentinperignon.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  91. package/LottieAnimation/LottieAnimation.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
  92. package/LottieAnimation/LottieAnimation.xcodeproj/xcuserdata/valentinperignon.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -6
  93. package/LottieAnimation/LottieAnimation.xcodeproj/xcuserdata/valentinperignon.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
  94. package/Sources/Private/Model/DotLottie/Zip/Data+Compression.swift +0 -134
  95. package/Sources/Private/Model/DotLottie/Zip/FileManager+ZIP.swift +0 -130
  96. package/Sources/Private/Utility/Interpolatable/KeyframeGroup+Extensions.swift +0 -59
@@ -0,0 +1,170 @@
1
+ //
2
+ // Archive+ZIP64.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
+ let zip64EOCDRecordStructSignature = 0x06064b50
14
+ let zip64EOCDLocatorStructSignature = 0x07064b50
15
+
16
+ // MARK: - ExtraFieldHeaderID
17
+
18
+ enum ExtraFieldHeaderID: UInt16 {
19
+ case zip64ExtendedInformation = 0x0001
20
+ }
21
+
22
+ extension Archive {
23
+ struct ZIP64EndOfCentralDirectory {
24
+ let record: ZIP64EndOfCentralDirectoryRecord
25
+ let locator: ZIP64EndOfCentralDirectoryLocator
26
+ }
27
+
28
+ struct ZIP64EndOfCentralDirectoryRecord: DataSerializable {
29
+ let zip64EOCDRecordSignature = UInt32(zip64EOCDRecordStructSignature)
30
+ let sizeOfZIP64EndOfCentralDirectoryRecord: UInt64
31
+ let versionMadeBy: UInt16
32
+ let versionNeededToExtract: UInt16
33
+ let numberOfDisk: UInt32
34
+ let numberOfDiskStart: UInt32
35
+ let totalNumberOfEntriesOnDisk: UInt64
36
+ let totalNumberOfEntriesInCentralDirectory: UInt64
37
+ let sizeOfCentralDirectory: UInt64
38
+ let offsetToStartOfCentralDirectory: UInt64
39
+ let zip64ExtensibleDataSector: Data
40
+ static let size = 56
41
+ }
42
+
43
+ struct ZIP64EndOfCentralDirectoryLocator: DataSerializable {
44
+ let zip64EOCDLocatorSignature = UInt32(zip64EOCDLocatorStructSignature)
45
+ let numberOfDiskWithZIP64EOCDRecordStart: UInt32
46
+ let relativeOffsetOfZIP64EOCDRecord: UInt64
47
+ let totalNumberOfDisk: UInt32
48
+ static let size = 20
49
+ }
50
+ }
51
+
52
+ extension Archive.ZIP64EndOfCentralDirectoryRecord {
53
+
54
+ // MARK: Lifecycle
55
+
56
+ init?(data: Data, additionalDataProvider _: (Int) throws -> Data) {
57
+ guard data.count == Archive.ZIP64EndOfCentralDirectoryRecord.size else { return nil }
58
+ guard data.scanValue(start: 0) == zip64EOCDRecordSignature else { return nil }
59
+ sizeOfZIP64EndOfCentralDirectoryRecord = data.scanValue(start: 4)
60
+ versionMadeBy = data.scanValue(start: 12)
61
+ versionNeededToExtract = data.scanValue(start: 14)
62
+ // Version Needed to Extract: 4.5 - File uses ZIP64 format extensions
63
+ guard versionNeededToExtract >= Archive.Version.v45.rawValue else { return nil }
64
+ numberOfDisk = data.scanValue(start: 16)
65
+ numberOfDiskStart = data.scanValue(start: 20)
66
+ totalNumberOfEntriesOnDisk = data.scanValue(start: 24)
67
+ totalNumberOfEntriesInCentralDirectory = data.scanValue(start: 32)
68
+ sizeOfCentralDirectory = data.scanValue(start: 40)
69
+ offsetToStartOfCentralDirectory = data.scanValue(start: 48)
70
+ zip64ExtensibleDataSector = Data()
71
+ }
72
+
73
+ init(
74
+ record: Archive.ZIP64EndOfCentralDirectoryRecord,
75
+ numberOfEntriesOnDisk: UInt64,
76
+ numberOfEntriesInCD: UInt64,
77
+ sizeOfCentralDirectory: UInt64,
78
+ offsetToStartOfCD: UInt64)
79
+ {
80
+ sizeOfZIP64EndOfCentralDirectoryRecord = record.sizeOfZIP64EndOfCentralDirectoryRecord
81
+ versionMadeBy = record.versionMadeBy
82
+ versionNeededToExtract = record.versionNeededToExtract
83
+ numberOfDisk = record.numberOfDisk
84
+ numberOfDiskStart = record.numberOfDiskStart
85
+ totalNumberOfEntriesOnDisk = numberOfEntriesOnDisk
86
+ totalNumberOfEntriesInCentralDirectory = numberOfEntriesInCD
87
+ self.sizeOfCentralDirectory = sizeOfCentralDirectory
88
+ offsetToStartOfCentralDirectory = offsetToStartOfCD
89
+ zip64ExtensibleDataSector = record.zip64ExtensibleDataSector
90
+ }
91
+
92
+ // MARK: Internal
93
+
94
+ var data: Data {
95
+ var zip64EOCDRecordSignature = zip64EOCDRecordSignature
96
+ var sizeOfZIP64EOCDRecord = sizeOfZIP64EndOfCentralDirectoryRecord
97
+ var versionMadeBy = versionMadeBy
98
+ var versionNeededToExtract = versionNeededToExtract
99
+ var numberOfDisk = numberOfDisk
100
+ var numberOfDiskStart = numberOfDiskStart
101
+ var totalNumberOfEntriesOnDisk = totalNumberOfEntriesOnDisk
102
+ var totalNumberOfEntriesInCD = totalNumberOfEntriesInCentralDirectory
103
+ var sizeOfCD = sizeOfCentralDirectory
104
+ var offsetToStartOfCD = offsetToStartOfCentralDirectory
105
+ var data = Data()
106
+ withUnsafePointer(to: &zip64EOCDRecordSignature) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
107
+ withUnsafePointer(to: &sizeOfZIP64EOCDRecord) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
108
+ withUnsafePointer(to: &versionMadeBy) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
109
+ withUnsafePointer(to: &versionNeededToExtract) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
110
+ withUnsafePointer(to: &numberOfDisk) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
111
+ withUnsafePointer(to: &numberOfDiskStart) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
112
+ withUnsafePointer(to: &totalNumberOfEntriesOnDisk) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
113
+ withUnsafePointer(to: &totalNumberOfEntriesInCD) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
114
+ withUnsafePointer(to: &sizeOfCD) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
115
+ withUnsafePointer(to: &offsetToStartOfCD) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
116
+ data.append(zip64ExtensibleDataSector)
117
+ return data
118
+ }
119
+
120
+ }
121
+
122
+ extension Archive.ZIP64EndOfCentralDirectoryLocator {
123
+
124
+ // MARK: Lifecycle
125
+
126
+ init?(data: Data, additionalDataProvider _: (Int) throws -> Data) {
127
+ guard data.count == Archive.ZIP64EndOfCentralDirectoryLocator.size else { return nil }
128
+ guard data.scanValue(start: 0) == zip64EOCDLocatorSignature else { return nil }
129
+ numberOfDiskWithZIP64EOCDRecordStart = data.scanValue(start: 4)
130
+ relativeOffsetOfZIP64EOCDRecord = data.scanValue(start: 8)
131
+ totalNumberOfDisk = data.scanValue(start: 16)
132
+ }
133
+
134
+ init(locator: Archive.ZIP64EndOfCentralDirectoryLocator, offsetOfZIP64EOCDRecord: UInt64) {
135
+ numberOfDiskWithZIP64EOCDRecordStart = locator.numberOfDiskWithZIP64EOCDRecordStart
136
+ relativeOffsetOfZIP64EOCDRecord = offsetOfZIP64EOCDRecord
137
+ totalNumberOfDisk = locator.totalNumberOfDisk
138
+ }
139
+
140
+ // MARK: Internal
141
+
142
+ var data: Data {
143
+ var zip64EOCDLocatorSignature = zip64EOCDLocatorSignature
144
+ var numberOfDiskWithZIP64EOCD = numberOfDiskWithZIP64EOCDRecordStart
145
+ var offsetOfZIP64EOCDRecord = relativeOffsetOfZIP64EOCDRecord
146
+ var totalNumberOfDisk = totalNumberOfDisk
147
+ var data = Data()
148
+ withUnsafePointer(to: &zip64EOCDLocatorSignature) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
149
+ withUnsafePointer(to: &numberOfDiskWithZIP64EOCD) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
150
+ withUnsafePointer(to: &offsetOfZIP64EOCDRecord) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
151
+ withUnsafePointer(to: &totalNumberOfDisk) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
152
+ return data
153
+ }
154
+
155
+ }
156
+
157
+ extension Archive.ZIP64EndOfCentralDirectory {
158
+ var data: Data { record.data + locator.data }
159
+ }
160
+
161
+ /// Properties that represent the maximum value of each field
162
+ var maxUInt32 = UInt32.max
163
+ var maxUInt16 = UInt16.max
164
+
165
+ var maxCompressedSize: UInt32 { maxUInt32 }
166
+ var maxUncompressedSize: UInt32 { maxUInt32 }
167
+ var maxOffsetOfLocalFileHeader: UInt32 { maxUInt32 }
168
+ var maxOffsetOfCentralDirectory: UInt32 { maxUInt32 }
169
+ var maxSizeOfCentralDirectory: UInt32 { maxUInt32 }
170
+ var maxTotalNumberOfEntries: UInt16 { maxUInt16 }
@@ -10,26 +10,82 @@
10
10
 
11
11
  import Foundation
12
12
 
13
- // MARK: - ZipArchive
14
-
15
- final class ZipArchive: Sequence {
13
+ /// The default chunk size when reading entry data from an archive.
14
+ let defaultReadChunkSize = Int(16 * 1024)
15
+ /// The default chunk size when writing entry data to an archive.
16
+ let defaultWriteChunkSize = defaultReadChunkSize
17
+ /// The default permissions for newly added entries.
18
+ let defaultFilePermissions = UInt16(0o644)
19
+ /// The default permissions for newly added directories.
20
+ let defaultDirectoryPermissions = UInt16(0o755)
21
+ let defaultPOSIXBufferSize = defaultReadChunkSize
22
+ let defaultDirectoryUnitCount = Int64(1)
23
+ let minEndOfCentralDirectoryOffset = Int64(22)
24
+ let endOfCentralDirectoryStructSignature = 0x06054b50
25
+ let localFileHeaderStructSignature = 0x04034b50
26
+ let dataDescriptorStructSignature = 0x08074b50
27
+ let centralDirectoryStructSignature = 0x02014b50
28
+ let memoryURLScheme = "memory"
29
+
30
+ // MARK: - Archive
31
+
32
+ /// A sequence of uncompressed or compressed ZIP entries.
33
+ ///
34
+ /// You use an `Archive` to create, read or update ZIP files.
35
+ /// To read an existing ZIP file, you have to pass in an existing file `URL` and `AccessMode.read`:
36
+ ///
37
+ /// var archiveURL = URL(fileURLWithPath: "/path/file.zip")
38
+ /// var archive = Archive(url: archiveURL, accessMode: .read)
39
+ ///
40
+ /// An `Archive` is a sequence of entries. You can
41
+ /// iterate over an archive using a `for`-`in` loop to get access to individual `Entry` objects:
42
+ ///
43
+ /// for entry in archive {
44
+ /// print(entry.path)
45
+ /// }
46
+ ///
47
+ /// Each `Entry` in an `Archive` is represented by its `path`. You can
48
+ /// use `path` to retrieve the corresponding `Entry` from an `Archive` via subscripting:
49
+ ///
50
+ /// let entry = archive['/path/file.txt']
51
+ ///
52
+ /// To create a new `Archive`, pass in a non-existing file URL and `AccessMode.create`. To modify an
53
+ /// existing `Archive` use `AccessMode.update`:
54
+ ///
55
+ /// var archiveURL = URL(fileURLWithPath: "/path/file.zip")
56
+ /// var archive = Archive(url: archiveURL, accessMode: .update)
57
+ /// try archive?.addEntry("test.txt", relativeTo: baseURL, compressionMethod: .deflate)
58
+ final class Archive: Sequence {
16
59
 
17
60
  // MARK: Lifecycle
18
61
 
19
62
  /// Initializes a new ZIP `Archive`.
20
63
  ///
21
64
  /// You can use this initalizer to create new archive files or to read and update existing ones.
65
+ /// The `mode` parameter indicates the intended usage of the archive: `.read`, `.create` or `.update`.
22
66
  /// - Parameters:
23
67
  /// - url: File URL to the receivers backing file.
68
+ /// - mode: Access mode of the receiver.
69
+ /// - preferredEncoding: Encoding for entry paths. Overrides the encoding specified in the archive.
70
+ /// This encoding is only used when _decoding_ paths from the receiver.
71
+ /// Paths of entries added with `addEntry` are always UTF-8 encoded.
24
72
  /// - Returns: An archive initialized with a backing file at the passed in file URL and the given access mode
25
73
  /// or `nil` if the following criteria are not met:
26
- init?(url: URL) {
74
+ /// - Note:
75
+ /// - The file URL _must_ point to an existing file for `AccessMode.read`.
76
+ /// - The file URL _must_ point to a non-existing file for `AccessMode.create`.
77
+ /// - The file URL _must_ point to an existing file for `AccessMode.update`.
78
+ init?(url: URL, accessMode mode: AccessMode, preferredEncoding: String.Encoding? = nil) {
27
79
  self.url = url
28
- guard let config = ZipArchive.makeBackingConfiguration(for: url) else { return nil }
80
+ accessMode = mode
81
+ self.preferredEncoding = preferredEncoding
82
+ guard let config = Archive.makeBackingConfiguration(for: url, mode: mode) else {
83
+ return nil
84
+ }
29
85
  archiveFile = config.file
30
86
  endOfCentralDirectoryRecord = config.endOfCentralDirectoryRecord
31
87
  zip64EndOfCentralDirectory = config.zip64EndOfCentralDirectory
32
- setvbuf(archiveFile, nil, _IOFBF, Int(Self.defaultPOSIXBufferSize))
88
+ setvbuf(archiveFile, nil, _IOFBF, Int(defaultPOSIXBufferSize))
33
89
  }
34
90
 
35
91
  deinit {
@@ -38,10 +94,10 @@ final class ZipArchive: Sequence {
38
94
 
39
95
  // MARK: Internal
40
96
 
41
- typealias LocalFileHeader = ZipEntry.LocalFileHeader
42
- typealias DataDescriptor = ZipEntry.DefaultDataDescriptor
43
- typealias ZIP64DataDescriptor = ZipEntry.ZIP64DataDescriptor
44
- typealias CentralDirectoryStructure = ZipEntry.CentralDirectoryStructure
97
+ typealias LocalFileHeader = Entry.LocalFileHeader
98
+ typealias DataDescriptor = Entry.DefaultDataDescriptor
99
+ typealias ZIP64DataDescriptor = Entry.ZIP64DataDescriptor
100
+ typealias CentralDirectoryStructure = Entry.CentralDirectoryStructure
45
101
 
46
102
  /// An error that occurs during reading, creating or updating a ZIP file.
47
103
  enum ArchiveError: Error {
@@ -75,6 +131,24 @@ final class ZipArchive: Sequence {
75
131
  case missingEndOfCentralDirectoryRecord
76
132
  }
77
133
 
134
+ /// The access mode for an `Archive`.
135
+ enum AccessMode: UInt {
136
+ /// Indicates that a newly instantiated `Archive` should create its backing file.
137
+ case create
138
+ /// Indicates that a newly instantiated `Archive` should read from an existing backing file.
139
+ case read
140
+ /// Indicates that a newly instantiated `Archive` should update an existing backing file.
141
+ case update
142
+ }
143
+
144
+ /// The version of an `Archive`
145
+ enum Version: UInt16 {
146
+ /// The minimum version for deflate compressed archives
147
+ case v20 = 20
148
+ /// The minimum version for archives making use of ZIP64 extensions
149
+ case v45 = 45
150
+ }
151
+
78
152
  struct EndOfCentralDirectoryRecord: DataSerializable {
79
153
  let endOfCentralDirectorySignature = UInt32(endOfCentralDirectoryStructSignature)
80
154
  let numberOfDisk: UInt16
@@ -88,38 +162,14 @@ final class ZipArchive: Sequence {
88
162
  static let size = 22
89
163
  }
90
164
 
91
- // MARK: - Helpers
92
-
93
- typealias EndOfCentralDirectoryStructure = (EndOfCentralDirectoryRecord, ZIP64EndOfCentralDirectory?)
94
-
95
- struct BackingConfiguration {
96
- let file: FILEPointer
97
- let endOfCentralDirectoryRecord: EndOfCentralDirectoryRecord
98
- let zip64EndOfCentralDirectory: ZIP64EndOfCentralDirectory?
99
-
100
- init(
101
- file: FILEPointer,
102
- endOfCentralDirectoryRecord: EndOfCentralDirectoryRecord,
103
- zip64EndOfCentralDirectory: ZIP64EndOfCentralDirectory?)
104
- {
105
- self.file = file
106
- self.endOfCentralDirectoryRecord = endOfCentralDirectoryRecord
107
- self.zip64EndOfCentralDirectory = zip64EndOfCentralDirectory
108
- }
109
- }
110
-
111
- static let defaultPOSIXBufferSize = Int(16 * 1024)
112
- static let minEndOfCentralDirectoryOffset = Int64(22)
113
- static let endOfCentralDirectoryStructSignature = 0x06054b50
114
-
115
- /// The default chunk size when reading entry data from an archive.
116
- static let defaultReadChunkSize = Int(16 * 1024)
117
-
118
165
  /// URL of an Archive's backing file.
119
166
  let url: URL
167
+ /// Access mode for an archive file.
168
+ let accessMode: AccessMode
120
169
  var archiveFile: FILEPointer
121
170
  var endOfCentralDirectoryRecord: EndOfCentralDirectoryRecord
122
171
  var zip64EndOfCentralDirectory: ZIP64EndOfCentralDirectory?
172
+ var preferredEncoding: String.Encoding?
123
173
 
124
174
  var totalNumberOfEntriesInCentralDirectory: UInt64 {
125
175
  zip64EndOfCentralDirectory?.record.totalNumberOfEntriesInCentralDirectory
@@ -136,6 +186,43 @@ final class ZipArchive: Sequence {
136
186
  ?? UInt64(endOfCentralDirectoryRecord.offsetToStartOfCentralDirectory)
137
187
  }
138
188
 
189
+ #if swift(>=5.0)
190
+ var memoryFile: MemoryFile?
191
+
192
+ /// Initializes a new in-memory ZIP `Archive`.
193
+ ///
194
+ /// You can use this initalizer to create new in-memory archive files or to read and update existing ones.
195
+ ///
196
+ /// - Parameters:
197
+ /// - data: `Data` object used as backing for in-memory archives.
198
+ /// - mode: Access mode of the receiver.
199
+ /// - preferredEncoding: Encoding for entry paths. Overrides the encoding specified in the archive.
200
+ /// This encoding is only used when _decoding_ paths from the receiver.
201
+ /// Paths of entries added with `addEntry` are always UTF-8 encoded.
202
+ /// - Returns: An in-memory archive initialized with passed in backing data.
203
+ /// - Note:
204
+ /// - The backing `data` _must_ contain a valid ZIP archive for `AccessMode.read` and `AccessMode.update`.
205
+ /// - The backing `data` _must_ be empty (or omitted) for `AccessMode.create`.
206
+ init?(data: Data = Data(), accessMode mode: AccessMode, preferredEncoding: String.Encoding? = nil) {
207
+ guard
208
+ let url = URL(string: "\(memoryURLScheme)://"),
209
+ let config = Archive.makeBackingConfiguration(for: data, mode: mode) else
210
+ {
211
+ return nil
212
+ }
213
+
214
+ self.url = url
215
+ accessMode = mode
216
+ self.preferredEncoding = preferredEncoding
217
+ archiveFile = config.file
218
+ memoryFile = config.memoryFile
219
+ endOfCentralDirectoryRecord = config.endOfCentralDirectoryRecord
220
+ zip64EndOfCentralDirectory = config.zip64EndOfCentralDirectory
221
+ }
222
+ #endif
223
+
224
+ // MARK: - Helpers
225
+
139
226
  static func scanForEndOfCentralDirectoryRecord(in file: FILEPointer)
140
227
  -> EndOfCentralDirectoryStructure?
141
228
  {
@@ -160,22 +247,7 @@ final class ZipArchive: Sequence {
160
247
  return nil
161
248
  }
162
249
 
163
- static func makeBackingConfiguration(for url: URL) -> BackingConfiguration? {
164
- let fileManager = FileManager()
165
- let fileSystemRepresentation = fileManager.fileSystemRepresentation(withPath: url.path)
166
- guard
167
- let archiveFile = fopen(fileSystemRepresentation, "rb"),
168
- let (eocdRecord, zip64EOCD) = ZipArchive.scanForEndOfCentralDirectoryRecord(in: archiveFile) else
169
- {
170
- return nil
171
- }
172
- return BackingConfiguration(
173
- file: archiveFile,
174
- endOfCentralDirectoryRecord: eocdRecord,
175
- zip64EndOfCentralDirectory: zip64EOCD)
176
- }
177
-
178
- func makeIterator() -> AnyIterator<ZipEntry> {
250
+ func makeIterator() -> AnyIterator<Entry> {
179
251
  let totalNumberOfEntriesInCD = totalNumberOfEntriesInCentralDirectory
180
252
  var directoryIndex = offsetToStartOfCentralDirectory
181
253
  var index = 0
@@ -197,7 +269,7 @@ final class ZipArchive: Sequence {
197
269
  var zip64DataDescriptor: ZIP64DataDescriptor?
198
270
  if centralDirStruct.usesDataDescriptor {
199
271
  let additionalSize = UInt64(localFileHeader.fileNameLength) + UInt64(localFileHeader.extraFieldLength)
200
- let isCompressed = centralDirStruct.compressionMethod != 0
272
+ let isCompressed = centralDirStruct.compressionMethod != CompressionMethod.none.rawValue
201
273
  let dataSize = isCompressed
202
274
  ? centralDirStruct.effectiveCompressedSize
203
275
  : centralDirStruct.effectiveUncompressedSize
@@ -215,7 +287,7 @@ final class ZipArchive: Sequence {
215
287
  directoryIndex += UInt64(centralDirStruct.fileCommentLength)
216
288
  index += 1
217
289
  }
218
- return ZipEntry(
290
+ return Entry(
219
291
  centralDirectoryStructure: centralDirStruct,
220
292
  localFileHeader: localFileHeader,
221
293
  dataDescriptor: dataDescriptor,
@@ -231,42 +303,11 @@ final class ZipArchive: Sequence {
231
303
  ///
232
304
  /// - Parameter path: A relative file path identifying the corresponding `Entry`.
233
305
  /// - Returns: An `Entry` with the given `path`. Otherwise, `nil`.
234
- subscript(path: String) -> ZipEntry? {
235
- first { $0.path == path }
236
- }
237
-
238
- /// Read a ZIP `Entry` from the receiver and write it to `url`.
239
- ///
240
- /// - Parameters:
241
- /// - entry: The ZIP `Entry` to read.
242
- /// - url: The destination file URL.
243
- /// - bufferSize: The maximum size of the read buffer and the decompression buffer (if needed).
244
- /// - Returns: The checksum of the processed content or 0 if the `skipCRC32` flag was set to `true`.
245
- /// - Throws: An error if the destination file cannot be written or the entry contains malformed content.
246
- func extract(_ entry: ZipEntry, to url: URL, bufferSize: Int = defaultReadChunkSize) throws -> UInt32 {
247
- guard bufferSize > 0 else { throw ArchiveError.invalidBufferSize }
248
- let fileManager = FileManager()
249
- try fileManager.createParentDirectoryStructure(for: url)
250
- let destinationRepresentation = fileManager.fileSystemRepresentation(withPath: url.path)
251
- guard let destinationFile: FILEPointer = fopen(destinationRepresentation, "wb+") else {
252
- throw CocoaError(.fileNoSuchFile)
306
+ subscript(path: String) -> Entry? {
307
+ if let encoding = preferredEncoding {
308
+ return first { $0.path(using: encoding) == path }
253
309
  }
254
- defer { fclose(destinationFile) }
255
-
256
- guard bufferSize > 0 else { throw ArchiveError.invalidBufferSize }
257
- guard entry.dataOffset <= .max else { throw ArchiveError.invalidLocalHeaderDataOffset }
258
- fseeko(archiveFile, off_t(entry.dataOffset), SEEK_SET)
259
-
260
- let attributes = FileManager.attributes(from: entry)
261
- try fileManager.setAttributes(attributes, ofItemAtPath: url.path)
262
-
263
- let size = entry.centralDirectoryStructure.effectiveCompressedSize
264
- guard size <= .max else { throw ArchiveError.invalidEntrySize }
265
- return try Data.decompress(size: Int64(size), bufferSize: bufferSize, provider: { _, chunkSize -> Data in
266
- try Data.readChunk(of: chunkSize, from: self.archiveFile)
267
- }, consumer: { data in
268
- _ = try Data.write(chunk: data, to: destinationFile)
269
- })
310
+ return first { $0.path == path }
270
311
  }
271
312
 
272
313
  // MARK: Private
@@ -293,147 +334,12 @@ final class ZipArchive: Sequence {
293
334
  }
294
335
  }
295
336
 
296
- // MARK: - Zip64
297
-
298
- extension ZipArchive {
299
-
300
- struct ZIP64EndOfCentralDirectory {
301
- let record: ZIP64EndOfCentralDirectoryRecord
302
- let locator: ZIP64EndOfCentralDirectoryLocator
303
- }
304
-
305
- struct ZIP64EndOfCentralDirectoryRecord: DataSerializable {
306
- let zip64EOCDRecordSignature = UInt32(zip64EOCDRecordStructSignature)
307
- let sizeOfZIP64EndOfCentralDirectoryRecord: UInt64
308
- let versionMadeBy: UInt16
309
- let versionNeededToExtract: UInt16
310
- let numberOfDisk: UInt32
311
- let numberOfDiskStart: UInt32
312
- let totalNumberOfEntriesOnDisk: UInt64
313
- let totalNumberOfEntriesInCentralDirectory: UInt64
314
- let sizeOfCentralDirectory: UInt64
315
- let offsetToStartOfCentralDirectory: UInt64
316
- let zip64ExtensibleDataSector: Data
317
- static let size = 56
318
- }
319
-
320
- struct ZIP64EndOfCentralDirectoryLocator: DataSerializable {
321
- let zip64EOCDLocatorSignature = UInt32(zip64EOCDLocatorStructSignature)
322
- let numberOfDiskWithZIP64EOCDRecordStart: UInt32
323
- let relativeOffsetOfZIP64EOCDRecord: UInt64
324
- let totalNumberOfDisk: UInt32
325
- static let size = 20
326
- }
327
-
328
- static let zip64EOCDRecordStructSignature = 0x06064b50
329
- static let zip64EOCDLocatorStructSignature = 0x07064b50
330
- }
331
-
332
- extension ZipArchive.ZIP64EndOfCentralDirectoryRecord {
333
-
334
- // MARK: Lifecycle
335
-
336
- init?(data: Data, additionalDataProvider _: (Int) throws -> Data) {
337
- guard data.count == ZipArchive.ZIP64EndOfCentralDirectoryRecord.size else { return nil }
338
- guard data.scanValue(start: 0) == zip64EOCDRecordSignature else { return nil }
339
- sizeOfZIP64EndOfCentralDirectoryRecord = data.scanValue(start: 4)
340
- versionMadeBy = data.scanValue(start: 12)
341
- versionNeededToExtract = data.scanValue(start: 14)
342
- // Version Needed to Extract: 4.5 - File uses ZIP64 format extensions
343
- guard versionNeededToExtract >= 45 else { return nil }
344
- numberOfDisk = data.scanValue(start: 16)
345
- numberOfDiskStart = data.scanValue(start: 20)
346
- totalNumberOfEntriesOnDisk = data.scanValue(start: 24)
347
- totalNumberOfEntriesInCentralDirectory = data.scanValue(start: 32)
348
- sizeOfCentralDirectory = data.scanValue(start: 40)
349
- offsetToStartOfCentralDirectory = data.scanValue(start: 48)
350
- zip64ExtensibleDataSector = Data()
351
- }
352
-
353
- init(
354
- record: ZipArchive.ZIP64EndOfCentralDirectoryRecord,
355
- numberOfEntriesOnDisk: UInt64,
356
- numberOfEntriesInCD: UInt64,
357
- sizeOfCentralDirectory: UInt64,
358
- offsetToStartOfCD: UInt64)
359
- {
360
- sizeOfZIP64EndOfCentralDirectoryRecord = record.sizeOfZIP64EndOfCentralDirectoryRecord
361
- versionMadeBy = record.versionMadeBy
362
- versionNeededToExtract = record.versionNeededToExtract
363
- numberOfDisk = record.numberOfDisk
364
- numberOfDiskStart = record.numberOfDiskStart
365
- totalNumberOfEntriesOnDisk = numberOfEntriesOnDisk
366
- totalNumberOfEntriesInCentralDirectory = numberOfEntriesInCD
367
- self.sizeOfCentralDirectory = sizeOfCentralDirectory
368
- offsetToStartOfCentralDirectory = offsetToStartOfCD
369
- zip64ExtensibleDataSector = record.zip64ExtensibleDataSector
370
- }
371
-
372
- // MARK: Internal
373
-
374
- var data: Data {
375
- var zip64EOCDRecordSignature = zip64EOCDRecordSignature
376
- var sizeOfZIP64EOCDRecord = sizeOfZIP64EndOfCentralDirectoryRecord
377
- var versionMadeBy = versionMadeBy
378
- var versionNeededToExtract = versionNeededToExtract
379
- var numberOfDisk = numberOfDisk
380
- var numberOfDiskStart = numberOfDiskStart
381
- var totalNumberOfEntriesOnDisk = totalNumberOfEntriesOnDisk
382
- var totalNumberOfEntriesInCD = totalNumberOfEntriesInCentralDirectory
383
- var sizeOfCD = sizeOfCentralDirectory
384
- var offsetToStartOfCD = offsetToStartOfCentralDirectory
385
- var data = Data()
386
- withUnsafePointer(to: &zip64EOCDRecordSignature) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
387
- withUnsafePointer(to: &sizeOfZIP64EOCDRecord) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
388
- withUnsafePointer(to: &versionMadeBy) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
389
- withUnsafePointer(to: &versionNeededToExtract) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
390
- withUnsafePointer(to: &numberOfDisk) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
391
- withUnsafePointer(to: &numberOfDiskStart) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
392
- withUnsafePointer(to: &totalNumberOfEntriesOnDisk) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
393
- withUnsafePointer(to: &totalNumberOfEntriesInCD) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
394
- withUnsafePointer(to: &sizeOfCD) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
395
- withUnsafePointer(to: &offsetToStartOfCD) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
396
- data.append(zip64ExtensibleDataSector)
397
- return data
398
- }
399
-
400
- }
401
-
402
- extension ZipArchive.ZIP64EndOfCentralDirectoryLocator {
403
-
404
- // MARK: Lifecycle
405
-
406
- init?(data: Data, additionalDataProvider _: (Int) throws -> Data) {
407
- guard data.count == ZipArchive.ZIP64EndOfCentralDirectoryLocator.size else { return nil }
408
- guard data.scanValue(start: 0) == zip64EOCDLocatorSignature else { return nil }
409
- numberOfDiskWithZIP64EOCDRecordStart = data.scanValue(start: 4)
410
- relativeOffsetOfZIP64EOCDRecord = data.scanValue(start: 8)
411
- totalNumberOfDisk = data.scanValue(start: 16)
412
- }
413
-
414
- // MARK: Internal
415
-
416
- var data: Data {
417
- var zip64EOCDLocatorSignature = zip64EOCDLocatorSignature
418
- var numberOfDiskWithZIP64EOCD = numberOfDiskWithZIP64EOCDRecordStart
419
- var offsetOfZIP64EOCDRecord = relativeOffsetOfZIP64EOCDRecord
420
- var totalNumberOfDisk = totalNumberOfDisk
421
- var data = Data()
422
- withUnsafePointer(to: &zip64EOCDLocatorSignature) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
423
- withUnsafePointer(to: &numberOfDiskWithZIP64EOCD) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
424
- withUnsafePointer(to: &offsetOfZIP64EOCDRecord) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
425
- withUnsafePointer(to: &totalNumberOfDisk) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
426
- return data
427
- }
428
-
429
- }
430
-
431
- extension ZipArchive.EndOfCentralDirectoryRecord {
337
+ extension Archive.EndOfCentralDirectoryRecord {
432
338
 
433
339
  // MARK: Lifecycle
434
340
 
435
341
  init?(data: Data, additionalDataProvider provider: (Int) throws -> Data) {
436
- guard data.count == ZipArchive.EndOfCentralDirectoryRecord.size else { return nil }
342
+ guard data.count == Archive.EndOfCentralDirectoryRecord.size else { return nil }
437
343
  guard data.scanValue(start: 0) == endOfCentralDirectorySignature else { return nil }
438
344
  numberOfDisk = data.scanValue(start: 4)
439
345
  numberOfDiskStart = data.scanValue(start: 6)
@@ -447,6 +353,23 @@ extension ZipArchive.EndOfCentralDirectoryRecord {
447
353
  zipFileCommentData = commentData
448
354
  }
449
355
 
356
+ init(
357
+ record: Archive.EndOfCentralDirectoryRecord,
358
+ numberOfEntriesOnDisk: UInt16,
359
+ numberOfEntriesInCentralDirectory: UInt16,
360
+ updatedSizeOfCentralDirectory: UInt32,
361
+ startOfCentralDirectory: UInt32)
362
+ {
363
+ numberOfDisk = record.numberOfDisk
364
+ numberOfDiskStart = record.numberOfDiskStart
365
+ totalNumberOfEntriesOnDisk = numberOfEntriesOnDisk
366
+ totalNumberOfEntriesInCentralDirectory = numberOfEntriesInCentralDirectory
367
+ sizeOfCentralDirectory = updatedSizeOfCentralDirectory
368
+ offsetToStartOfCentralDirectory = startOfCentralDirectory
369
+ zipFileCommentLength = record.zipFileCommentLength
370
+ zipFileCommentData = record.zipFileCommentData
371
+ }
372
+
450
373
  // MARK: Internal
451
374
 
452
375
  var data: Data {