diff --git a/SWCompression.xcodeproj/project.pbxproj b/SWCompression.xcodeproj/project.pbxproj index bf2bc9e1..fc17ae97 100644 --- a/SWCompression.xcodeproj/project.pbxproj +++ b/SWCompression.xcodeproj/project.pbxproj @@ -36,7 +36,6 @@ 062C1D7C1F8FCA9300917968 /* TarExtendedHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 062C1D7B1F8FCA9300917968 /* TarExtendedHeader.swift */; }; 0631CB061F81109E00A2E0AB /* Deflate+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0631CB051F81109E00A2E0AB /* Deflate+Constants.swift */; }; 0634D9831F5207C30002B675 /* SuffixArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0634D9821F5207C30002B675 /* SuffixArray.swift */; }; - 06393DF51F1A7F8E00A647A0 /* ZipString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06393DF41F1A7F8E00A647A0 /* ZipString.swift */; }; 063DF10D1DE1F07800F38082 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063DF1061DE1F07800F38082 /* Extensions.swift */; }; 063DF1131DE1F07800F38082 /* DecodingHuffmanTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063DF1081DE1F07800F38082 /* DecodingHuffmanTree.swift */; }; 0642998B206017590044BA0C /* ZipExtraField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0642998A206017590044BA0C /* ZipExtraField.swift */; }; @@ -248,7 +247,6 @@ 0631CB051F81109E00A2E0AB /* Deflate+Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Deflate+Constants.swift"; sourceTree = ""; }; 063364E21DC52979007E313F /* GzipArchive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GzipArchive.swift; sourceTree = ""; }; 0634D9821F5207C30002B675 /* SuffixArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SuffixArray.swift; sourceTree = ""; }; - 06393DF41F1A7F8E00A647A0 /* ZipString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZipString.swift; sourceTree = ""; }; 063DF1061DE1F07800F38082 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Extensions.swift; path = Sources/Common/Extensions.swift; sourceTree = SOURCE_ROOT; }; 063DF1081DE1F07800F38082 /* DecodingHuffmanTree.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DecodingHuffmanTree.swift; path = Sources/Common/DecodingHuffmanTree.swift; sourceTree = SOURCE_ROOT; }; 0642998A206017590044BA0C /* ZipExtraField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZipExtraField.swift; sourceTree = ""; }; @@ -588,7 +586,6 @@ 0642998A206017590044BA0C /* ZipExtraField.swift */, 0606CF8020641209002B6EE9 /* StandardExtraFields.swift */, 061C063C1F0E8AB700832F0C /* ZipError.swift */, - 06393DF41F1A7F8E00A647A0 /* ZipString.swift */, 061C064C1F0E901C00832F0C /* ZipLocalHeader.swift */, 061C06471F0E8FF500832F0C /* ZipCentralDirectoryEntry.swift */, 061C06511F0E904600832F0C /* ZipEndOfCentralDirectory.swift */, @@ -1110,7 +1107,6 @@ 0620F4C81F8E24BF001C9B7A /* DecompressionAlgorithm.swift in Sources */, 0686A6451FA4C47300E89C9E /* FileSystemType+Zip.swift in Sources */, 06CDFCA31F111D6C00292758 /* BZip2Error.swift in Sources */, - 06393DF51F1A7F8E00A647A0 /* ZipString.swift in Sources */, 06CFF6B22078C5A5003B8375 /* TarEntryInfoProvider.swift in Sources */, 06CC3FE21F8AAF3100BD576D /* ByteReader+XZ.swift in Sources */, 065D0ADC1F2B9D9100CE2DA8 /* 7zEntryInfo.swift in Sources */, diff --git a/Sources/ZIP/ByteReader+ZipString.swift b/Sources/ZIP/ByteReader+ZipString.swift index cb05d33c..d0ab3edf 100644 --- a/Sources/ZIP/ByteReader+ZipString.swift +++ b/Sources/ZIP/ByteReader+ZipString.swift @@ -20,12 +20,91 @@ extension ByteReader { if useUtf8 { return String(data: stringData, encoding: .utf8) } - if ZipString.cp437Available && !ZipString.needsUtf8(stringData) { + if String.cp437Available && !stringData.needsUtf8() { return String(data: stringData, encoding: String.Encoding(rawValue: - CFStringConvertEncodingToNSStringEncoding(ZipString.cp437Encoding))) + CFStringConvertEncodingToNSStringEncoding(String.cp437Encoding))) } else { return String(data: stringData, encoding: .utf8) } } } + +fileprivate extension String { + + #if os(Linux) + static let cp437Encoding: CFStringEncoding = UInt32(truncatingIfNeeded: UInt(kCFStringEncodingDOSLatinUS)) + static let cp437Available: Bool = CFStringIsEncodingAvailable(cp437Encoding) + #else + static let cp437Encoding = CFStringEncoding(CFStringEncodings.dosLatinUS.rawValue) + static let cp437Available = CFStringIsEncodingAvailable(cp437Encoding) + #endif + +} + +fileprivate extension Data { + + func needsUtf8() -> Bool { + // UTF-8 can have BOM. + if self.count >= 3 { + if self[self.startIndex] == 0xEF && self[self.startIndex + 1] == 0xBB && self[self.startIndex + 2] == 0xBF { + return true + } + } + + var index = self.startIndex + while index < self.endIndex { + let byte = self[index] + if byte <= 0x7F { // This simple byte can exist both in CP437 and UTF-8. + index += 1 + continue + } + + // Otherwise, it has to be correct code sequence in case of UTF-8. + // If code sequence is incorrect, then it is CP437. + let codeLength: Int + if byte >= 0xC2 && byte <= 0xDF { + codeLength = 2 + } else if byte >= 0xE0 && byte <= 0xEF { + codeLength = 3 + } else if byte >= 0xF0 && byte <= 0xF4 { + codeLength = 4 + } else { + return false + } + + if index + codeLength - 1 >= self.endIndex { + return false + } + + for i in 1..> 11 == 0x1B { + return false + } + } else if codeLength == 4 { + let ch = (UInt32(truncatingIfNeeded: self[index]) & 0x07) << 18 + + (UInt32(truncatingIfNeeded: self[index + 1]) & 0x3F) << 12 + + (UInt32(truncatingIfNeeded: self[index + 2]) & 0x3F) << 6 + + UInt32(truncatingIfNeeded: self[index + 3]) & 0x3F + if ch < 0x10000 || ch > 0x10FFFF { + return false + } + } + return true + } + // All bytes were in range 0...0x7F, which can be both in CP437 and UTF-8. + // We solve this ambiguity in favor of CP437. + return false + } + +} + diff --git a/Sources/ZIP/ZipString.swift b/Sources/ZIP/ZipString.swift deleted file mode 100644 index 523ebdae..00000000 --- a/Sources/ZIP/ZipString.swift +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2018 Timofey Solomko -// Licensed under MIT License -// -// See LICENSE for license information - -import Foundation - -#if os(Linux) - import CoreFoundation -#endif - -class ZipString { - - #if os(Linux) - static let cp437Encoding: CFStringEncoding = UInt32(truncatingIfNeeded: UInt(kCFStringEncodingDOSLatinUS)) - static let cp437Available: Bool = CFStringIsEncodingAvailable(cp437Encoding) - #else - static let cp437Encoding = CFStringEncoding(CFStringEncodings.dosLatinUS.rawValue) - static let cp437Available = CFStringIsEncodingAvailable(cp437Encoding) - #endif - - static func needsUtf8(_ data: Data) -> Bool { - // UTF-8 can have BOM. - if data.count >= 3 { - if data[data.startIndex] == 0xEF && data[data.startIndex + 1] == 0xBB && data[data.startIndex + 2] == 0xBF { - return true - } - } - - var index = data.startIndex - while index < data.endIndex { - let byte = data[index] - if byte <= 0x7F { // This simple byte can exist both in CP437 and UTF-8. - index += 1 - continue - } - - // Otherwise, it has to be correct code sequence in case of UTF-8. - // If code sequence is incorrect, then it is CP437. - let codeLength: Int - if byte >= 0xC2 && byte <= 0xDF { - codeLength = 2 - } else if byte >= 0xE0 && byte <= 0xEF { - codeLength = 3 - } else if byte >= 0xF0 && byte <= 0xF4 { - codeLength = 4 - } else { - return false - } - - if index + codeLength - 1 >= data.endIndex { - return false - } - - for i in 1..> 11 == 0x1B { - return false - } - } else if codeLength == 4 { - let ch = (UInt32(truncatingIfNeeded: data[index]) & 0x07) << 18 + - (UInt32(truncatingIfNeeded: data[index + 1]) & 0x3F) << 12 + - (UInt32(truncatingIfNeeded: data[index + 2]) & 0x3F) << 6 + - UInt32(truncatingIfNeeded: data[index + 3]) & 0x3F - if ch < 0x10000 || ch > 0x10FFFF { - return false - } - } - return true - } - // All bytes were in range 0...0x7F, which can be both in CP437 and UTF-8. - // We solve this ambiguity in favor of CP437. - return false - } - -}