From 6c54ca61b896713a31acc3f500046778fa7d1b35 Mon Sep 17 00:00:00 2001 From: John Corner Date: Sun, 28 May 2023 16:49:37 +0800 Subject: [PATCH 1/9] 1. Fix Xcode 14 unit tests read resources failed. 2. Fix zip file contains non-utf8 encding directory name may failed to unzip. --- Package.swift | 5 +++-- Package@swift-5.8.swift | 30 ++++++++++++++++++++++++++++++ Zip/Zip.swift | 11 +++++++++-- Zip/ZipUtilities.swift | 17 +++++++++++++++++ ZipTests/Resources/gb2312.zip | Bin 0 -> 794 bytes ZipTests/ZipTests.swift | 11 ++++++++++- 6 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 Package@swift-5.8.swift create mode 100644 ZipTests/Resources/gb2312.zip diff --git a/Package.swift b/Package.swift index fd33704c..b89977ca 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.1 +// swift-tools-version:5.3 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -24,6 +24,7 @@ let package = Package( .testTarget( name: "ZipTests", dependencies: ["Zip"], - path: "ZipTests"), + path: "ZipTests", + resources: [.process("Resources")]), ] ) diff --git a/Package@swift-5.8.swift b/Package@swift-5.8.swift new file mode 100644 index 00000000..b89977ca --- /dev/null +++ b/Package@swift-5.8.swift @@ -0,0 +1,30 @@ +// swift-tools-version:5.3 +// The swift-tools-version declares the minimum version of Swift required to build this package. +import PackageDescription + +let package = Package( + name: "Zip", + products: [ + .library(name: "Zip", targets: ["Zip"]) + ], + targets: [ + .target( + name: "Minizip", + dependencies: [], + path: "Zip/minizip", + exclude: ["module"], + linkerSettings: [ + .linkedLibrary("z") + ]), + .target( + name: "Zip", + dependencies: ["Minizip"], + path: "Zip", + exclude: ["minizip", "zlib"]), + .testTarget( + name: "ZipTests", + dependencies: ["Zip"], + path: "ZipTests", + resources: [.process("Resources")]), + ] +) diff --git a/Zip/Zip.swift b/Zip/Zip.swift index af2d453e..2ee7f437 100644 --- a/Zip/Zip.swift +++ b/Zip/Zip.swift @@ -160,8 +160,15 @@ public class Zip { unzGetCurrentFileInfo64(zip, &fileInfo, fileName, UInt(fileNameSize), nil, 0, nil, 0) fileName[Int(fileInfo.size_filename)] = 0 - var pathString = String(cString: fileName) - + let encodingPathString = ZipUtilities.AutoEncodingString(data: .init(bytes: fileName, count: fileNameSize)).text() + var pathString = encodingPathString.isEmpty ? String(cString: fileName):encodingPathString +#if DEBUG + if encodingPathString.isEmpty { + print("encoding string failed, use cString \(pathString)") + } else { + print("encoding string \(pathString)") + } +#endif guard pathString.count > 0 else { throw ZipError.unzipFail } diff --git a/Zip/ZipUtilities.swift b/Zip/ZipUtilities.swift index 0bcec54e..15f0da0b 100644 --- a/Zip/ZipUtilities.swift +++ b/Zip/ZipUtilities.swift @@ -105,4 +105,21 @@ internal class ZipUtilities { return processedFilePaths } + internal struct AutoEncodingString { + /// Raw sting data + let data: Data + + /// Auto detect string encode + /// - Returns: string with valid encoding, it will be empty string if encoding failed. + func text() -> String { + var lossy = ObjCBool(false) + let encodingValue = NSString.stringEncoding(for: data, encodingOptions: nil, convertedString: nil, usedLossyConversion: &lossy) + let encoding = String.Encoding(rawValue: encodingValue) + #if DEBUG + print("[\(type(of: self))] detect string encoding \(encoding), usedLossyConversion \(lossy.boolValue)") + #endif + let string = String(data: data, encoding: encoding) + return string ?? "" + } + } } diff --git a/ZipTests/Resources/gb2312.zip b/ZipTests/Resources/gb2312.zip new file mode 100644 index 0000000000000000000000000000000000000000..f5daad751c8f2bfa380f66441b4c95d7d73d2e2b GIT binary patch literal 794 zcmWIWW@Zs#0Dwe8=g{oAjd zeYN)?NZ8Q8C``f7$ka4IzqG)Kfl+i*#Iwf@6P|bOeA==4*}}z7Cv?1=-S)U)B3SqH zHEW*i>Ur6`;%P_Q({(-1cTa}MBbgEabP*TW4a=JMgo(9y=cLlokXrFa}3Ye?^h&p6uv;v3Kv&IUAnNZ2*~idi|Eq`@Zg2y#Ms+od?!# zJ+x=v;$4Ytf1RT{jt_2Ei$6#Rte2 yxMHLVYMTO(o&?0C`2sn9kbUG}a;Va7A~qk<*4^QX- literal 0 HcmV?d00001 diff --git a/ZipTests/ZipTests.swift b/ZipTests/ZipTests.swift index 70f689ff..aad9052b 100644 --- a/ZipTests/ZipTests.swift +++ b/ZipTests/ZipTests.swift @@ -33,7 +33,7 @@ class ZipTests: XCTestCase { private func url(forResource resource: String, withExtension ext: String? = nil) -> URL? { #if Xcode - return Bundle(for: ZipTests.self).url(forResource: resource, withExtension: ext) + return Bundle.module.url(forResource: resource, withExtension: ext) #else let testDirPath = URL(fileURLWithPath: String(#file)).deletingLastPathComponent() let resourcePath = testDirPath.appendingPathComponent("Resources").appendingPathComponent(resource) @@ -271,4 +271,13 @@ class ZipTests: XCTestCase { XCTAssertTrue(Zip.isValidFileExtension("zip")) XCTAssertTrue(Zip.isValidFileExtension("cbz")) } + + func testGB2312File() throws { + let filePath = url(forResource: "gb2312", withExtension: "zip")! + let destinationURL = try Zip.quickUnzipFile(filePath) + addTeardownBlock { + try? FileManager.default.removeItem(at: destinationURL) + } + XCTAssertTrue(FileManager.default.fileExists(atPath: destinationURL.path)) + } } From 13778e29626dc254b9f7570c24bcac42370f740c Mon Sep 17 00:00:00 2001 From: John Corner Date: Sun, 28 May 2023 17:26:00 +0800 Subject: [PATCH 2/9] 2.1.3 --- Zip.podspec | 2 +- Zip/Info-tvOS.plist | 2 +- Zip/Info.plist | 2 +- ZipTests/Info.plist | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Zip.podspec b/Zip.podspec index 907757f9..5842cd03 100644 --- a/Zip.podspec +++ b/Zip.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = "Zip" - s.version = "2.1.2" + s.version = "2.1.3" s.summary = "Zip and unzip files in Swift." s.swift_version = "5.3" s.swift_versions = ["4.2", "5.0", "5.1", "5.3"] diff --git a/Zip/Info-tvOS.plist b/Zip/Info-tvOS.plist index bc21553c..41c0d687 100644 --- a/Zip/Info-tvOS.plist +++ b/Zip/Info-tvOS.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.1.2 + 2.1.3 CFBundleSignature ???? CFBundleVersion diff --git a/Zip/Info.plist b/Zip/Info.plist index bc21553c..41c0d687 100644 --- a/Zip/Info.plist +++ b/Zip/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.1.2 + 2.1.3 CFBundleSignature ???? CFBundleVersion diff --git a/ZipTests/Info.plist b/ZipTests/Info.plist index db36cdf0..c8548fd8 100644 --- a/ZipTests/Info.plist +++ b/ZipTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 2.1.2 + 2.1.3 CFBundleSignature ???? CFBundleVersion From 9c04362cd3a067e7f848b6dfe6d0c61376f90a4a Mon Sep 17 00:00:00 2001 From: John Corner Date: Fri, 18 Aug 2023 00:00:21 +0800 Subject: [PATCH 3/9] add Linux auto detect string enconding and convert to utf8 support --- Package@swift-5.8.swift | 23 +++++++++++-- Zip/ZipUtilities.swift | 72 ++++++++++++++++++++++++++++++----------- 2 files changed, 74 insertions(+), 21 deletions(-) diff --git a/Package@swift-5.8.swift b/Package@swift-5.8.swift index b89977ca..49f01df2 100644 --- a/Package@swift-5.8.swift +++ b/Package@swift-5.8.swift @@ -1,12 +1,31 @@ -// swift-tools-version:5.3 +// swift-tools-version:5.8 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription +let defines: [SwiftSetting] = [ + .define("SYSTEM_ICONV", .when(platforms: [.linux])) +] + +#if os(Linux) +let dependencies: [PackageDescription.Package.Dependency] = [ + .package(url: "https://github.com/0xfeedface1993/iconv.git", branch: "main"), + .package(url: "https://github.com/0xfeedface1993/CEnca.git", from: "0.1.3"), +] +let products: [Target.Dependency] = [ + .product(name: "iconv", package: "iconv"), + .product(name: "EncodingWrapper", package: "CEnca"), +] +#else +let dependencies: [PackageDescription.Package.Dependency] = [] +let products: [Target.Dependency] = [] +#endif + let package = Package( name: "Zip", products: [ .library(name: "Zip", targets: ["Zip"]) ], + dependencies: dependencies, targets: [ .target( name: "Minizip", @@ -18,7 +37,7 @@ let package = Package( ]), .target( name: "Zip", - dependencies: ["Minizip"], + dependencies: ["Minizip"] + products, path: "Zip", exclude: ["minizip", "zlib"]), .testTarget( diff --git a/Zip/ZipUtilities.swift b/Zip/ZipUtilities.swift index 15f0da0b..da9390ee 100644 --- a/Zip/ZipUtilities.swift +++ b/Zip/ZipUtilities.swift @@ -7,6 +7,14 @@ // import Foundation +import Logging + +#if SYSTEM_ICONV && canImport(iconv) && canImport(EncodingWrapper) +import iconv +import EncodingWrapper +#endif + +fileprivate let logger = Logger(label: "com.zip.utilties") internal class ZipUtilities { @@ -19,21 +27,21 @@ internal class ZipUtilities { As true: $ zip -r Test.zip Test/ $ unzip -l Test.zip - Test/ - Test/A.txt - Test/B.txt + Test/ + Test/A.txt + Test/B.txt As false: $ zip -r Test.zip Test/ $ unzip -l Test.zip - A.txt - B.txt - */ + A.txt + B.txt + */ let includeRootDirectory = true - + // File manager let fileManager = FileManager.default - + /** * ProcessedFilePath struct */ @@ -49,12 +57,12 @@ internal class ZipUtilities { //MARK: Path processing /** - Process zip paths - - - parameter paths: Paths as NSURL. - - - returns: Array of ProcessedFilePath structs. - */ + Process zip paths + + - parameter paths: Paths as NSURL. + + - returns: Array of ProcessedFilePath structs. + */ internal func processZipPaths(_ paths: [URL]) -> [ProcessedFilePath]{ var processedFilePaths = [ProcessedFilePath]() for path in paths { @@ -88,7 +96,7 @@ internal class ZipUtilities { while let filePathComponent = enumerator.nextObject() as? String { let path = directory.appendingPathComponent(filePathComponent) let filePath = path.path - + var isDirectory: ObjCBool = false _ = fileManager.fileExists(atPath: filePath, isDirectory: &isDirectory) if !isDirectory.boolValue { @@ -104,22 +112,48 @@ internal class ZipUtilities { } return processedFilePaths } - + internal struct AutoEncodingString { /// Raw sting data let data: Data +#if SYSTEM_ICONV && canImport(iconv) && canImport(EncodingWrapper) + /// Auto detect string encode + /// - Returns: string with valid encoding, it will be empty string if encoding failed. + func text() -> String { + do { + let encodeName = try EncodingWrapper(data).style(.iconv).guessAllLanguageFoEncodingString() + let sourceCodePage = try Iconv.CodePage(name: encodeName) + let buffer = data.withUnsafeBytes(Array.init(_:)) + let unicodeString = try Iconv(from: sourceCodePage, to: .UTF8).utf8(buf: buffer) + return unicodeString ?? "" + } catch { + logger.error("string encoding process failed, \(error)") + return "" + } + } +#else /// Auto detect string encode /// - Returns: string with valid encoding, it will be empty string if encoding failed. func text() -> String { var lossy = ObjCBool(false) let encodingValue = NSString.stringEncoding(for: data, encodingOptions: nil, convertedString: nil, usedLossyConversion: &lossy) let encoding = String.Encoding(rawValue: encodingValue) - #if DEBUG - print("[\(type(of: self))] detect string encoding \(encoding), usedLossyConversion \(lossy.boolValue)") - #endif + logger.info("detect string encoding \(encoding), usedLossyConversion \(lossy.boolValue)") let string = String(data: data, encoding: encoding) return string ?? "" } +#endif + } +} + +#if SYSTEM_ICONV && canImport(iconv) && canImport(EncodingWrapper) +extension Iconv.CodePage { + init(name: String) throws { + guard let sourceCodePage = Iconv.CodePage(rawValue: name) else { + throw CocoaError(.fileReadUnknownStringEncoding) + } + self = sourceCodePage } } +#endif From f7014e33a6fac8566ae8cb245b46947638273fb1 Mon Sep 17 00:00:00 2001 From: John Corner Date: Fri, 18 Aug 2023 00:38:57 +0800 Subject: [PATCH 4/9] update Package.swift file --- Package.swift | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index b89977ca..49f01df2 100644 --- a/Package.swift +++ b/Package.swift @@ -1,12 +1,31 @@ -// swift-tools-version:5.3 +// swift-tools-version:5.8 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription +let defines: [SwiftSetting] = [ + .define("SYSTEM_ICONV", .when(platforms: [.linux])) +] + +#if os(Linux) +let dependencies: [PackageDescription.Package.Dependency] = [ + .package(url: "https://github.com/0xfeedface1993/iconv.git", branch: "main"), + .package(url: "https://github.com/0xfeedface1993/CEnca.git", from: "0.1.3"), +] +let products: [Target.Dependency] = [ + .product(name: "iconv", package: "iconv"), + .product(name: "EncodingWrapper", package: "CEnca"), +] +#else +let dependencies: [PackageDescription.Package.Dependency] = [] +let products: [Target.Dependency] = [] +#endif + let package = Package( name: "Zip", products: [ .library(name: "Zip", targets: ["Zip"]) ], + dependencies: dependencies, targets: [ .target( name: "Minizip", @@ -18,7 +37,7 @@ let package = Package( ]), .target( name: "Zip", - dependencies: ["Minizip"], + dependencies: ["Minizip"] + products, path: "Zip", exclude: ["minizip", "zlib"]), .testTarget( From 50e303311b4048cf754ec9b269f82214542edc4a Mon Sep 17 00:00:00 2001 From: John Corner Date: Fri, 18 Aug 2023 00:43:22 +0800 Subject: [PATCH 5/9] fix swiftSettings parameter not passs to SPM --- Package.swift | 11 ++++++++--- Package@swift-5.8.swift | 11 ++++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Package.swift b/Package.swift index 49f01df2..e601c2fe 100644 --- a/Package.swift +++ b/Package.swift @@ -16,8 +16,12 @@ let products: [Target.Dependency] = [ .product(name: "EncodingWrapper", package: "CEnca"), ] #else -let dependencies: [PackageDescription.Package.Dependency] = [] -let products: [Target.Dependency] = [] +let dependencies: [PackageDescription.Package.Dependency] = [ + .package(url: "https://github.com/apple/swift-log.git", from: "1.5.2"), +] +let products: [Target.Dependency] = [ + .product(name: "Logging", package: "swift-log"), +] #endif let package = Package( @@ -39,7 +43,8 @@ let package = Package( name: "Zip", dependencies: ["Minizip"] + products, path: "Zip", - exclude: ["minizip", "zlib"]), + exclude: ["minizip", "zlib"], + swiftSettings: defines), .testTarget( name: "ZipTests", dependencies: ["Zip"], diff --git a/Package@swift-5.8.swift b/Package@swift-5.8.swift index 49f01df2..e601c2fe 100644 --- a/Package@swift-5.8.swift +++ b/Package@swift-5.8.swift @@ -16,8 +16,12 @@ let products: [Target.Dependency] = [ .product(name: "EncodingWrapper", package: "CEnca"), ] #else -let dependencies: [PackageDescription.Package.Dependency] = [] -let products: [Target.Dependency] = [] +let dependencies: [PackageDescription.Package.Dependency] = [ + .package(url: "https://github.com/apple/swift-log.git", from: "1.5.2"), +] +let products: [Target.Dependency] = [ + .product(name: "Logging", package: "swift-log"), +] #endif let package = Package( @@ -39,7 +43,8 @@ let package = Package( name: "Zip", dependencies: ["Minizip"] + products, path: "Zip", - exclude: ["minizip", "zlib"]), + exclude: ["minizip", "zlib"], + swiftSettings: defines), .testTarget( name: "ZipTests", dependencies: ["Zip"], From 5f856e4a37b6f80b634f6299d6f7a1f13a68f327 Mon Sep 17 00:00:00 2001 From: John Corner Date: Fri, 18 Aug 2023 01:10:54 +0800 Subject: [PATCH 6/9] expose AutoEncodingString module --- Zip/Zip.swift | 2 +- Zip/ZipUtilities.swift | 66 +++++++++++++++++++++--------------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/Zip/Zip.swift b/Zip/Zip.swift index 2ee7f437..ac98281a 100644 --- a/Zip/Zip.swift +++ b/Zip/Zip.swift @@ -160,7 +160,7 @@ public class Zip { unzGetCurrentFileInfo64(zip, &fileInfo, fileName, UInt(fileNameSize), nil, 0, nil, 0) fileName[Int(fileInfo.size_filename)] = 0 - let encodingPathString = ZipUtilities.AutoEncodingString(data: .init(bytes: fileName, count: fileNameSize)).text() + let encodingPathString = AutoEncodingString(data: .init(bytes: fileName, count: fileNameSize)).text() var pathString = encodingPathString.isEmpty ? String(cString: fileName):encodingPathString #if DEBUG if encodingPathString.isEmpty { diff --git a/Zip/ZipUtilities.swift b/Zip/ZipUtilities.swift index da9390ee..4d912a42 100644 --- a/Zip/ZipUtilities.swift +++ b/Zip/ZipUtilities.swift @@ -112,39 +112,6 @@ internal class ZipUtilities { } return processedFilePaths } - - internal struct AutoEncodingString { - /// Raw sting data - let data: Data - -#if SYSTEM_ICONV && canImport(iconv) && canImport(EncodingWrapper) - /// Auto detect string encode - /// - Returns: string with valid encoding, it will be empty string if encoding failed. - func text() -> String { - do { - let encodeName = try EncodingWrapper(data).style(.iconv).guessAllLanguageFoEncodingString() - let sourceCodePage = try Iconv.CodePage(name: encodeName) - let buffer = data.withUnsafeBytes(Array.init(_:)) - let unicodeString = try Iconv(from: sourceCodePage, to: .UTF8).utf8(buf: buffer) - return unicodeString ?? "" - } catch { - logger.error("string encoding process failed, \(error)") - return "" - } - } -#else - /// Auto detect string encode - /// - Returns: string with valid encoding, it will be empty string if encoding failed. - func text() -> String { - var lossy = ObjCBool(false) - let encodingValue = NSString.stringEncoding(for: data, encodingOptions: nil, convertedString: nil, usedLossyConversion: &lossy) - let encoding = String.Encoding(rawValue: encodingValue) - logger.info("detect string encoding \(encoding), usedLossyConversion \(lossy.boolValue)") - let string = String(data: data, encoding: encoding) - return string ?? "" - } -#endif - } } #if SYSTEM_ICONV && canImport(iconv) && canImport(EncodingWrapper) @@ -157,3 +124,36 @@ extension Iconv.CodePage { } } #endif + +public struct AutoEncodingString { + /// Raw sting data + public let data: Data + +#if SYSTEM_ICONV && canImport(iconv) && canImport(EncodingWrapper) + /// Auto detect string encode + /// - Returns: string with valid encoding, it will be empty string if encoding failed. + public func text() -> String { + do { + let encodeName = try EncodingWrapper(data).style(.iconv).guessAllLanguageFoEncodingString() + let sourceCodePage = try Iconv.CodePage(name: encodeName) + let buffer = data.withUnsafeBytes(Array.init(_:)) + let unicodeString = try Iconv(from: sourceCodePage, to: .UTF8).utf8(buf: buffer) + return unicodeString ?? "" + } catch { + logger.error("string encoding process failed, \(error)") + return "" + } + } +#else + /// Auto detect string encode + /// - Returns: string with valid encoding, it will be empty string if encoding failed. + public func text() -> String { + var lossy = ObjCBool(false) + let encodingValue = NSString.stringEncoding(for: data, encodingOptions: nil, convertedString: nil, usedLossyConversion: &lossy) + let encoding = String.Encoding(rawValue: encodingValue) + logger.info("detect string encoding \(encoding), usedLossyConversion \(lossy.boolValue)") + let string = String(data: data, encoding: encoding) + return string ?? "" + } +#endif +} From 27e05c6e3e2aa76ae6548d40d6cec13f1fd8b129 Mon Sep 17 00:00:00 2001 From: John Corner Date: Fri, 18 Aug 2023 01:13:57 +0800 Subject: [PATCH 7/9] 'AutoEncodingString' add public initializer --- Zip/ZipUtilities.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Zip/ZipUtilities.swift b/Zip/ZipUtilities.swift index 4d912a42..39acbcf4 100644 --- a/Zip/ZipUtilities.swift +++ b/Zip/ZipUtilities.swift @@ -129,6 +129,10 @@ public struct AutoEncodingString { /// Raw sting data public let data: Data + init(data: Data) { + self.data = data + } + #if SYSTEM_ICONV && canImport(iconv) && canImport(EncodingWrapper) /// Auto detect string encode /// - Returns: string with valid encoding, it will be empty string if encoding failed. From 4855dba33cded1b9243b75fc03b50cde7e8328dc Mon Sep 17 00:00:00 2001 From: John Corner Date: Fri, 18 Aug 2023 01:16:52 +0800 Subject: [PATCH 8/9] fix missing public keyword --- Zip/ZipUtilities.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zip/ZipUtilities.swift b/Zip/ZipUtilities.swift index 39acbcf4..bedac909 100644 --- a/Zip/ZipUtilities.swift +++ b/Zip/ZipUtilities.swift @@ -129,7 +129,7 @@ public struct AutoEncodingString { /// Raw sting data public let data: Data - init(data: Data) { + public init(data: Data) { self.data = data } From 7f4ac81da9ecd7c3ee703e514b026656de6df0e0 Mon Sep 17 00:00:00 2001 From: John Corner Date: Fri, 18 Aug 2023 03:34:26 +0800 Subject: [PATCH 9/9] fix non-nil end string data convert failed. --- Zip/ZipUtilities.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Zip/ZipUtilities.swift b/Zip/ZipUtilities.swift index bedac909..b3369f57 100644 --- a/Zip/ZipUtilities.swift +++ b/Zip/ZipUtilities.swift @@ -140,7 +140,10 @@ public struct AutoEncodingString { do { let encodeName = try EncodingWrapper(data).style(.iconv).guessAllLanguageFoEncodingString() let sourceCodePage = try Iconv.CodePage(name: encodeName) - let buffer = data.withUnsafeBytes(Array.init(_:)) + var buffer = data.withUnsafeBytes(Array.init(_:)) + if buffer.last != 0 { + buffer.append(0) + } let unicodeString = try Iconv(from: sourceCodePage, to: .UTF8).utf8(buf: buffer) return unicodeString ?? "" } catch {