Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prefer non-optional String->Data conversion #323

Merged
merged 33 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
a98cf2c
Prefer non-optional String->Data conversion
weichsel Jun 8, 2024
18eb7af
Disable erroneous swiftlint rule
weichsel Jun 8, 2024
cc2711d
Whitespace
weichsel Jun 8, 2024
aa0bf78
Add a merge step to convert raw code coverage profile data
weichsel Jun 8, 2024
d3dda5e
Whitespace
weichsel Jun 8, 2024
58fffc6
Remove custom codecov merge step
weichsel Jun 8, 2024
c008da1
Revert "Remove custom codecov merge step"
weichsel Jun 8, 2024
a6c23ca
Avoid optional FILE pointers when opening memory archives
weichsel Jun 9, 2024
f612665
Remove unreliable low-memory allocator test helper
weichsel Jun 9, 2024
ce0d914
Improve ZIP64 field extraction
weichsel Jun 9, 2024
4b0bad7
Add test resource
weichsel Jun 9, 2024
6a58e76
Swiftlint
weichsel Jun 9, 2024
7b3dd0f
Fix Linux build
weichsel Jun 9, 2024
8a0e219
Refactor memory file opening
weichsel Jun 9, 2024
f4d22b8
Fix Linux build
weichsel Jun 9, 2024
fbb037a
Cleanup
weichsel Jun 9, 2024
b0c350e
Merge workflows into one
weichsel Jun 9, 2024
ebf55de
Debug Linux test suite
weichsel Jun 9, 2024
53a1677
Debug Linux test
weichsel Jun 9, 2024
45cb00b
Debug Linux test
weichsel Jun 9, 2024
bc263d8
Refactor archive file handle closing
weichsel Jun 9, 2024
a077036
Swiftlint
weichsel Jun 9, 2024
ee780a5
Doc comment
weichsel Jun 9, 2024
e01c8dd
Improve coverage
weichsel Jun 9, 2024
f27f12a
Improve coverage
weichsel Jun 9, 2024
a5177fe
Increase coverage
weichsel Jun 10, 2024
4adaa2c
Swiftlint
weichsel Jun 10, 2024
c30176c
Change worfklow name
weichsel Jun 10, 2024
111ace5
Exclude synthesised source bundle accessor from coverage calculation
weichsel Jun 10, 2024
f0015e2
Update testing matrix
weichsel Jun 10, 2024
1dc5722
Codestyle
weichsel Jun 10, 2024
72af049
Rename var
weichsel Jun 10, 2024
dcea71a
Update Linux test matrix
weichsel Jun 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: CI

on:
pull_request:
paths:
- '.github/workflows/swiftlint.yml'
- '.swiftlint.yml'
- '**/*.swift'

jobs:
SwiftLint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: SwiftLint
uses: norio-nomura/action-swiftlint@3.2.1
with:
args: --strict

Xcode:
strategy:
matrix:
xcode_version: ['14.3.1', '15.0.1', '15.1', '15.2', '15.3', '15.4']
runs-on: macos-latest
env:
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode_version }}.app
steps:
- uses: actions/checkout@v2
- run: swift -version
- run: swift test -c release -Xswiftc -enable-testing

Linux:
strategy:
matrix:
tag: ['5.1', '5.2', '5.3', '5.4', '5.5', '5.6', '5.7', '5.8', '5.9', '5.10']
runs-on: ubuntu-latest
container:
image: swift:${{ matrix.tag }}
steps:
- uses: actions/checkout@v2
- run: swift test -c release -Xswiftc -enable-testing

Coverage:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- run: swift test -c release -Xswiftc -enable-testing --enable-code-coverage
- run: find .build/release/codecov/ -name "*.profraw" -print0 | xargs -0 xcrun llvm-profdata merge -sparse -o .build/release/codecov/default.profdata
- run: xcrun llvm-cov export -summary-only -ignore-filename-regex 'ZIPFoundationTests|resource_bundle_accessor.swift|.*Deprecated.*$' .build/release/ZIPFoundationPackageTests.xctest/Contents/MacOS/ZIPFoundationPackageTests -instr-profile .build/release/codecov/default.profdata > .build/coverage.json
- run: (cat .build/coverage.json|jq '.data[0]["totals"]["lines"]["percent"]' | grep -Eq "100") && { exit 0; } || { echo 'Please make sure that the test suite covers all framework code paths.'; exit 1; }
16 changes: 0 additions & 16 deletions .github/workflows/llvm-cov.yml

This file was deleted.

31 changes: 0 additions & 31 deletions .github/workflows/swift.yml

This file was deleted.

18 changes: 0 additions & 18 deletions .github/workflows/swiftlint.yml

This file was deleted.

11 changes: 1 addition & 10 deletions Sources/ZIPFoundation/Archive+BackingConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,8 @@ extension Archive {
#if swift(>=5.0)
static func makeBackingConfiguration(for data: Data, mode: AccessMode) throws
-> BackingConfiguration {
let posixMode: String
switch mode {
case .read: posixMode = "rb"
case .create: posixMode = "wb+"
case .update: posixMode = "rb+"
}
let memoryFile = MemoryFile(data: data)
guard let archiveFile = memoryFile.open(mode: posixMode) else {
throw ArchiveError.unreadableArchive
}

let archiveFile = memoryFile.open(mode: mode)
switch mode {
case .read:
guard let (eocdRecord, zip64EOCD) = Archive.scanForEndOfCentralDirectoryRecord(in: archiveFile) else {
Expand Down
3 changes: 1 addition & 2 deletions Sources/ZIPFoundation/Archive+Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,7 @@ extension Archive {
size: (uncompressed: UInt64, compressed: UInt64), checksum: CRC32,
modificationDateTime: (UInt16, UInt16)) throws -> LocalFileHeader {
// We always set Bit 11 in generalPurposeBitFlag, which indicates an UTF-8 encoded path.
guard let fileNameData = path.data(using: .utf8) else { throw ArchiveError.invalidEntryPath }

let fileNameData = Data(path.utf8)
var uncompressedSizeOfLFH = UInt32(0)
var compressedSizeOfLFH = UInt32(0)
var extraFieldLength = UInt16(0)
Expand Down
92 changes: 53 additions & 39 deletions Sources/ZIPFoundation/Archive+MemoryFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,60 +17,61 @@ extension Archive {
#if swift(>=5.0)

extension Archive {
/// Returns a `Data` object containing a representation of the receiver.
public var data: Data? { return self.memoryFile?.data }
}

class MemoryFile {
private(set) var data: Data
private var offset = 0
class MemoryFile {

init(data: Data = Data()) {
self.data = data
}
private(set) var data: Data
private var offset = 0

func open(mode: String) -> FILEPointer? {
let cookie = Unmanaged.passRetained(self)
let writable = mode.count > 0 && (mode.first! != "r" || mode.last! == "+")
let append = mode.count > 0 && mode.first! == "a"
#if os(macOS) || os(iOS) || os(tvOS) || os(visionOS) || os(watchOS) || os(Android)
let result = writable
? funopen(cookie.toOpaque(), readStub, writeStub, seekStub, closeStub)
: funopen(cookie.toOpaque(), readStub, nil, seekStub, closeStub)
#else
let stubs = cookie_io_functions_t(read: readStub, write: writeStub, seek: seekStub, close: closeStub)
let result = fopencookie(cookie.toOpaque(), mode, stubs)
#endif
if append {
fseeko(result, 0, SEEK_END)
init(data: Data = Data()) {
self.data = data
}

func open(mode: AccessMode) -> FILEPointer {
let cookie = Unmanaged.passRetained(self)
#if os(macOS) || os(iOS) || os(tvOS) || os(visionOS) || os(watchOS) || os(Android)
let result = mode.isWritable
? funopen(cookie.toOpaque(), readStub, writeStub, seekStub, closeStub)!
: funopen(cookie.toOpaque(), readStub, nil, seekStub, closeStub)!
#else
let stubs = cookie_io_functions_t(read: readStub, write: writeStub, seek: seekStub, close: closeStub)
let result = fopencookie(cookie.toOpaque(), mode.posixMode, stubs)!
#endif
return result
}
return result
}

/// Returns a `Data` object containing a representation of the receiver.
public var data: Data? { return self.memoryFile?.data }
}

private extension MemoryFile {
private extension Archive.MemoryFile {

func readData(buffer: UnsafeMutableRawBufferPointer) -> Int {
let size = min(buffer.count, data.count-offset)
let start = data.startIndex
data.copyBytes(to: buffer.bindMemory(to: UInt8.self), from: start+offset..<start+offset+size)
offset += size
self.data.copyBytes(to: buffer.bindMemory(to: UInt8.self), from: start+offset..<start+offset+size)
self.offset += size
return size
}

func writeData(buffer: UnsafeRawBufferPointer) -> Int {
let start = data.startIndex
if offset < data.count && offset+buffer.count > data.count {
data.removeSubrange(start+offset..<start+data.count)
let start = self.data.startIndex
if self.offset < self.data.count && self.offset+buffer.count > self.data.count {
self.data.removeSubrange(start+self.offset..<start+self.data.count)
} else if offset > data.count {
data.append(Data(count: offset-data.count))
self.data.append(Data(count: self.offset-self.data.count))
}
if offset == data.count {
data.append(buffer.bindMemory(to: UInt8.self))
if self.offset == self.data.count {
self.data.append(buffer.bindMemory(to: UInt8.self))
} else {
let start = data.startIndex // May have changed in earlier mutation
data.replaceSubrange(start+offset..<start+offset+buffer.count, with: buffer.bindMemory(to: UInt8.self))
let start = self.data.startIndex // May have changed in earlier mutation
self.data.replaceSubrange(
start+self.offset..<start+self.offset+buffer.count,
with: buffer.bindMemory(to: UInt8.self)
)
}
offset += buffer.count
self.offset += buffer.count
return buffer.count
}

Expand All @@ -88,18 +89,19 @@ private extension MemoryFile {
}
}

private func fileFromCookie(cookie: UnsafeRawPointer) -> MemoryFile {
return Unmanaged<MemoryFile>.fromOpaque(cookie).takeUnretainedValue()
private func fileFromCookie(cookie: UnsafeRawPointer) -> Archive.MemoryFile {
return Unmanaged<Archive.MemoryFile>.fromOpaque(cookie).takeUnretainedValue()
}

private func closeStub(_ cookie: UnsafeMutableRawPointer?) -> Int32 {
if let cookie = cookie {
Unmanaged<MemoryFile>.fromOpaque(cookie).release()
Unmanaged<Archive.MemoryFile>.fromOpaque(cookie).release()
}
return 0
}

#if os(macOS) || os(iOS) || os(tvOS) || os(visionOS) || os(watchOS) || os(Android)

private func readStub(_ cookie: UnsafeMutableRawPointer?,
_ bytePtr: UnsafeMutablePointer<Int8>?,
_ count: Int32) -> Int32 {
Expand All @@ -124,6 +126,18 @@ private func seekStub(_ cookie: UnsafeMutableRawPointer?,
}

#else

extension Archive.AccessMode {

var posixMode: String {
switch self {
case .read: return "rb"
case .create: return "wb+"
case .update: return "rb+"
}
}
}

private func readStub(_ cookie: UnsafeMutableRawPointer?,
_ bytePtr: UnsafeMutablePointer<Int8>?,
_ count: Int) -> Int {
Expand Down
1 change: 1 addition & 0 deletions Sources/ZIPFoundation/Archive+Reading.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ extension Archive {
throw CocoaError(.fileWriteFileExists, userInfo: [NSFilePathErrorKey: url.path])
}
let consumer = { (data: Data) in
// swiftlint:disable:next non_optional_string_data_conversion
guard let linkPath = String(data: data, encoding: .utf8) else { throw ArchiveError.invalidEntryPath }

let parentURL = url.deletingLastPathComponent()
Expand Down
2 changes: 1 addition & 1 deletion Sources/ZIPFoundation/Archive+Writing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,6 @@ extension Archive {
}

func replaceCurrentArchive(with archive: Archive) throws {
fclose(self.archiveFile)
if self.isMemoryArchive {
#if swift(>=5.0)
guard let data = archive.data else {
Expand Down Expand Up @@ -246,6 +245,7 @@ extension Archive {
#endif
let fileSystemRepresentation = fileManager.fileSystemRepresentation(withPath: self.url.path)
guard let file = fopen(fileSystemRepresentation, "rb+") else { throw ArchiveError.unreadableArchive }

self.archiveFile = file
}
}
Expand Down
27 changes: 14 additions & 13 deletions Sources/ZIPFoundation/Archive.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ public final class Archive: Sequence {
case read
/// Indicates that a newly instantiated `Archive` should update an existing backing file.
case update

/// Indicates that the archive can be written to.
var isWritable: Bool { self != .read }
}

/// The version of an `Archive`
Expand Down Expand Up @@ -129,7 +132,11 @@ public final class Archive: Sequence {
public let url: URL
/// Access mode for an archive file.
public let accessMode: AccessMode
var archiveFile: FILEPointer
var archiveFile: FILEPointer {
willSet {
fclose(self.archiveFile)
}
}
var endOfCentralDirectoryRecord: EndOfCentralDirectoryRecord
var zip64EndOfCentralDirectory: ZIP64EndOfCentralDirectory?
var pathEncoding: String.Encoding?
Expand Down Expand Up @@ -174,7 +181,7 @@ public final class Archive: Sequence {
setvbuf(self.archiveFile, nil, _IOFBF, Int(defaultPOSIXBufferSize))
}

#if swift(>=5.0)
#if swift(>=5.0)
var memoryFile: MemoryFile?

/// Initializes a new in-memory ZIP `Archive`.
Expand All @@ -192,10 +199,7 @@ public final class Archive: Sequence {
/// - The backing `data` _must_ contain a valid ZIP archive for `AccessMode.read` and `AccessMode.update`.
/// - The backing `data` _must_ be empty (or omitted) for `AccessMode.create`.
public init(data: Data = Data(), accessMode mode: AccessMode, pathEncoding: String.Encoding? = nil) throws {
guard let url = URL(string: "\(memoryURLScheme)://") else {
throw ArchiveError.unreadableArchive
}

let url = URL(string: "\(memoryURLScheme)://")!
self.url = url
self.accessMode = mode
self.pathEncoding = pathEncoding
Expand All @@ -205,7 +209,7 @@ public final class Archive: Sequence {
self.endOfCentralDirectoryRecord = config.endOfCentralDirectoryRecord
self.zip64EndOfCentralDirectory = config.zip64EndOfCentralDirectory
}
#endif
#endif

deinit {
fclose(self.archiveFile)
Expand Down Expand Up @@ -293,14 +297,11 @@ public final class Archive: Sequence {

private static func scanForZIP64EndOfCentralDirectory(in file: FILEPointer, eocdOffset: UInt64)
-> ZIP64EndOfCentralDirectory? {
guard UInt64(ZIP64EndOfCentralDirectoryLocator.size) < eocdOffset else {
return nil
}
guard UInt64(ZIP64EndOfCentralDirectoryLocator.size) < eocdOffset else { return nil }

let locatorOffset = eocdOffset - UInt64(ZIP64EndOfCentralDirectoryLocator.size)
guard UInt64(ZIP64EndOfCentralDirectoryRecord.size) < locatorOffset else { return nil }

guard UInt64(ZIP64EndOfCentralDirectoryRecord.size) < locatorOffset else {
return nil
}
let recordOffset = locatorOffset - UInt64(ZIP64EndOfCentralDirectoryRecord.size)
guard let locator: ZIP64EndOfCentralDirectoryLocator = Data.readStruct(from: file, at: locatorOffset),
let record: ZIP64EndOfCentralDirectoryRecord = Data.readStruct(from: file, at: recordOffset) else {
Expand Down
Loading
Loading