Skip to content

Commit

Permalink
Add support for embedded platforms
Browse files Browse the repository at this point in the history
  • Loading branch information
saagarjha committed Sep 13, 2023
1 parent 2a87017 commit f6a177b
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 18 deletions.
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import PackageDescription
let package = Package(
name: "unxip",
platforms: [
.macOS(.v11)
.macOS(.v11), .iOS(.v14), .watchOS(.v7),
],
products: [
.executable(name: "unxip", targets: ["unxip"])
Expand Down
77 changes: 60 additions & 17 deletions unxip.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

#if os(macOS)
#if canImport(Compression)
import Compression
#else
import FoundationXML
Expand All @@ -10,6 +10,10 @@ import Foundation
import zlib
#endif

#if canImport(UIKit) // Embedded, in other words
import libxml2
#endif

#if PROFILING
import os

Expand All @@ -22,7 +26,7 @@ import Foundation
enum DefaultCompressor {
static func zlibDecompress(data: [UInt8], decompressedSize: Int) -> [UInt8] {
return [UInt8](unsafeUninitializedCapacity: decompressedSize) { buffer, count in
#if os(macOS)
#if canImport(Compression)
let zlibSkip = 2 // Apple's decoder doesn't want to see CMF/FLG (see RFC 1950)
data[data.index(data.startIndex, offsetBy: zlibSkip)...].withUnsafeBufferPointer {
precondition(compression_decode_buffer(buffer.baseAddress!, decompressedSize, $0.baseAddress!, $0.count, nil, COMPRESSION_ZLIB) == decompressedSize)
Expand All @@ -40,7 +44,7 @@ enum DefaultCompressor {
let magic = [0xfd] + "7zX".utf8
precondition(data.prefix(magic.count).elementsEqual(magic))
return [UInt8](unsafeUninitializedCapacity: decompressedSize) { buffer, count in
#if os(macOS)
#if canImport(Compression)
precondition(compression_decode_buffer(buffer.baseAddress!, decompressedSize, data, data.count, nil, COMPRESSION_LZMA) == decompressedSize)
#else
var memlimit = UInt64.max
Expand Down Expand Up @@ -522,7 +526,7 @@ struct File {
mode & Int(C_ISVTX) != 0
}

#if os(macOS)
#if canImport(Darwin)
static let blocksize = {
var buffer = stat()
// FIXME: This relies on a previous chdir to the output directory
Expand Down Expand Up @@ -690,7 +694,7 @@ actor Statistics {

// There seems to be a compiler bug where this needs to be outside of init
static func start() -> Any? {
if #available(macOS 13.0, *) {
if #available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) {
return ContinuousClock.now
} else {
return nil
Expand All @@ -711,7 +715,7 @@ actor Statistics {
start = Self.start()

let watchedSignal: CInt
#if os(macOS)
#if canImport(Darwin)
watchedSignal = SIGINFO
#else
watchedSignal = SIGUSR1
Expand Down Expand Up @@ -757,10 +761,10 @@ actor Statistics {
if let total = total {
print(" (out of \(Self.byteCountFormatter.string(fromByteCount: Int64(total))))", terminator: "")
}
if #available(macOS 13.0, *),
if #available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *),
let start = start as? ContinuousClock.Instant
{
#if os(macOS)
#if canImport(Darwin)
let duration = (ContinuousClock.now - start).formatted(.units(allowed: [.seconds], fractionalPart: .show(length: 2)))
#else
let duration = ContinuousClock.now - start
Expand Down Expand Up @@ -886,9 +890,11 @@ struct Main {
}
#if os(macOS)
let readSize = Int(PIPE_SIZE) * 16
#else
#elseif canImport(Glibc)
let pipeSize = fcntl(descriptor, F_GETPIPE_SZ)
let readSize = (pipeSize > 0 ? Int(pipeSize) : sysconf(CInt(_SC_PAGESIZE))) * 16
#else
let readSize = sysconf(CInt(_SC_PAGESIZE)) * 16
#endif

Task {
Expand Down Expand Up @@ -1173,7 +1179,7 @@ struct Main {
await taskStream.addTask {
try await task?.value

#if os(macOS)
#if canImport(Darwin)
let compressedData =
options.compress
? try! await compressionStream.addTask {
Expand All @@ -1199,7 +1205,7 @@ struct Main {
setStickyBit(on: file)
}

#if os(macOS)
#if canImport(Darwin)
if let compressedData = compressedData,
file.write(compressedData: compressedData, toDescriptor: fd)
{
Expand Down Expand Up @@ -1254,12 +1260,49 @@ struct Main {
let compressedTOC = try await file.read(Int(tocCompressedSize))
let toc = DefaultCompressor.zlibDecompress(data: compressedTOC, decompressedSize: Int(tocDecompressedSize))

let document = try! XMLDocument(data: Data(toc))
let content = try! document.nodes(forXPath: "/xar/toc/file").first {
try! $0.nodes(forXPath: "name").first!.stringValue! == "Content"
}!
let contentOffset = Int(try! content.nodes(forXPath: "data/offset").first!.stringValue!)!
let contentSize = Int(try! content.nodes(forXPath: "data/length").first!.stringValue!)!
#if canImport(UIKit)
let document = xmlReadMemory(toc, CInt(toc.count), "", nil, 0)
defer {
xmlFreeDoc(document)
}
let context = xmlXPathNewContext(document)
defer {
xmlXPathFreeContext(context)
}

func evaluateXPath(node: xmlNodePtr!, xpath: String) -> String {
let result = xmlXPathNodeEval(node, xpath, context)!
defer {
xmlXPathFreeObject(result)
}
precondition(result.pointee.type == XPATH_NODESET && result.pointee.nodesetval.pointee.nodeNr == 1)
let string = xmlNodeListGetString(document, result.pointee.nodesetval.pointee.nodeTab.pointee!.pointee.children, 1)!
defer {
xmlFree(string)
}
return String(cString: string)
}

let result = xmlXPathEvalExpression("/xar/toc/file", context)!
defer {
xmlXPathFreeObject(result)
}
precondition(result.pointee.type == XPATH_NODESET)
let content = result.pointee.nodesetval.pointee.nodeTab[
(0..<Int(result.pointee.nodesetval.pointee.nodeNr)).first {
evaluateXPath(node: result.pointee.nodesetval.pointee.nodeTab[$0], xpath: "name") == "Content"
}!]

let contentOffset = Int(evaluateXPath(node: content, xpath: "data/offset"))!
let contentSize = Int(evaluateXPath(node: content, xpath: "data/length"))!
#else
let document = try! XMLDocument(data: Data(toc))
let content = try! document.nodes(forXPath: "/xar/toc/file").first {
try! $0.nodes(forXPath: "name").first!.stringValue! == "Content"
}!
let contentOffset = Int(try! content.nodes(forXPath: "data/offset").first!.stringValue!)!
let contentSize = Int(try! content.nodes(forXPath: "data/length").first!.stringValue!)!
#endif

_ = try await file.read(fileStart + Int(headerSize) + Int(tocCompressedSize) + contentOffset - file.position)
file.cap = file.position + contentSize
Expand Down

0 comments on commit f6a177b

Please sign in to comment.