66import Foundation
77import BitByteData
88
9+ /**
10+ A type that allows to iteratively read TAR entries from a container provided by a `FileHandle`.
11+
12+ The `TarReader` may be helpful in reducing the peak memory usage on certain platforms. However, to achieve this either
13+ the `TarReader.process(_:)` function should be used or both the call to `TarReader.read()` and the processing of the
14+ returned entry should be wrapped inside the `autoreleasepool`. Since the `autoreleasepool` is available only on Darwin
15+ platforms, the memory reducing effect may be not as significant on non-Darwin platforms (such as Linux or Windows).
16+
17+ The following code demonstrates an example usage of the `TarReader`:
18+ ```swift
19+ let handle: FileHandle = ...
20+ let reader = TarReader(fileHandle: handle)
21+ try reader.process { ... }
22+ ...
23+ try handle.close()
24+ ```
25+ Note that closing the `FileHandle` remains the responsibility of the caller.
26+
27+ - Important: Due to the API availability limitations of Foundation's `FileHandle`, on certain platforms errors in
28+ `FileHandle` operations may result in unrecoverable runtime failures. As such, it is not recommended to use `TarWriter`
29+ on those platforms. The following platforms are _unaffected_ by this issue: macOS 10.15.4+, iOS 13.4+, watchOS 6.2+,
30+ tvOS 13.4+.
31+ */
932public struct TarReader {
1033
1134 private let handle : FileHandle
@@ -14,6 +37,12 @@ public struct TarReader {
1437 private var longLinkName : String ?
1538 private var longName : String ?
1639
40+ /**
41+ Creates a new instance for reading TAR entries from the provided `fileHandle`.
42+
43+ - Parameter fileHandle: A handle from which the entries will be read. Note that the `TarReader` does not close the
44+ `fileHandle` and this remains the responsibility of the caller.
45+ */
1746 public init ( fileHandle: FileHandle ) {
1847 self . handle = fileHandle
1948 self . lastGlobalExtendedHeader = nil
@@ -22,13 +51,38 @@ public struct TarReader {
2251 self . longName = nil
2352 }
2453
54+ /**
55+ Processes the next TAR entry by reading it from the container and calling the provided closure on the result.
56+
57+ On Darwin platforms both the reading and the call to the closure are performed inside the `autoreleasepool` which
58+ allows to reduce the peak memory usage.
59+
60+ If the argument supplied to the closure is `nil` this indicates that the end of the input was reached. After that
61+ the repeated `TarReader.process(_:)` or `TarReader.read()` calls will result in the `DataError.truncated` being
62+ thrown.
63+
64+ - Throws: `DataError.truncated` if the input is truncated. `TarError` is thrown in case of malformed input. Errors
65+ thrown by `FileHandle` operations are also propagated.
66+ */
2567 public mutating func process< T> ( _ transform: ( TarEntry ? ) throws -> T ) throws -> T {
2668 return try autoreleasepool {
2769 let entry = try read ( )
2870 return try transform ( entry)
2971 }
3072 }
3173
74+ /**
75+ Reads the next TAR entry from the container.
76+
77+ On Darwin platforms it is recommended to wrap both the call to this function and the follow-up processing inside
78+ the `autoreleasepool` in order to reduce the peak memory usage.
79+
80+ - Throws: `DataError.truncated` if the input is truncated. `TarError` is thrown in case of malformed input. Errors
81+ thrown by `FileHandle` operations are also propagated.
82+
83+ - Returns: The next entry from the container or `nil` if the end of the input has been reached. After that the
84+ repeated `TarReader.process(_:)` or `TarReader.read()` calls will result in the `DataError.truncated` being thrown.
85+ */
3286 public mutating func read( ) throws -> TarEntry ? {
3387 let headerData = try getData ( size: 512 )
3488 if headerData. count == 0 {
0 commit comments