From 82ff0dff047ea89e9671ffe4e81608b1da32fe27 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sat, 6 May 2017 20:26:58 +0300 Subject: [PATCH] Added first prototype of TAR parsing. And a test for it. --- .gitattributes | 1 + SWCompression.xcodeproj/project.pbxproj | 26 +++ Sources/LZMADecoder.swift | 2 +- Sources/TarContainer.swift | 214 ++++++++++++++++++++++++ Tests/TarTests.swift | 26 +++ Tests/Test Files/TAR/test.tar | 3 + 6 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 Sources/TarContainer.swift create mode 100644 Tests/TarTests.swift create mode 100644 Tests/Test Files/TAR/test.tar diff --git a/.gitattributes b/.gitattributes index 8f418ef2..ec56c56f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,3 +3,4 @@ *.answer filter=lfs diff=lfs merge=lfs -text *.xz filter=lfs diff=lfs merge=lfs -text *.zip filter=lfs diff=lfs merge=lfs -text +*.tar filter=lfs diff=lfs merge=lfs -text diff --git a/SWCompression.xcodeproj/project.pbxproj b/SWCompression.xcodeproj/project.pbxproj index ebdebc5d..25292138 100644 --- a/SWCompression.xcodeproj/project.pbxproj +++ b/SWCompression.xcodeproj/project.pbxproj @@ -45,6 +45,7 @@ 0681B3DF1E44F674001A9D79 /* BitToByteWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0681B3DD1E44F674001A9D79 /* BitToByteWriter.swift */; }; 0681B3E01E44F674001A9D79 /* BitToByteWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0681B3DD1E44F674001A9D79 /* BitToByteWriter.swift */; }; 0681B3E11E44F674001A9D79 /* BitToByteWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0681B3DD1E44F674001A9D79 /* BitToByteWriter.swift */; }; + 0691B9401EBE3E100037EE58 /* test.tar in Resources */ = {isa = PBXBuildFile; fileRef = 0691B93F1EBE3E100037EE58 /* test.tar */; }; 06991C701E05A9F70030FF07 /* test9.lzma in Resources */ = {isa = PBXBuildFile; fileRef = 06991C6D1E05A9F70030FF07 /* test9.lzma */; }; 06991C711E05A9F70030FF07 /* test10.lzma in Resources */ = {isa = PBXBuildFile; fileRef = 06991C6E1E05A9F70030FF07 /* test10.lzma */; }; 06991C721E05A9F70030FF07 /* test11.lzma in Resources */ = {isa = PBXBuildFile; fileRef = 06991C6F1E05A9F70030FF07 /* test11.lzma */; }; @@ -96,6 +97,11 @@ 06A393441DE0713300182E12 /* SWCompression.h in Headers */ = {isa = PBXBuildFile; fileRef = 06A393421DE070B500182E12 /* SWCompression.h */; settings = {ATTRIBUTES = (Public, ); }; }; 06A393451DE0713A00182E12 /* SWCompression.h in Headers */ = {isa = PBXBuildFile; fileRef = 06A393421DE070B500182E12 /* SWCompression.h */; settings = {ATTRIBUTES = (Public, ); }; }; 06A393461DE0714000182E12 /* SWCompression.h in Headers */ = {isa = PBXBuildFile; fileRef = 06A393421DE070B500182E12 /* SWCompression.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 06ADB2D91EBCEFA60053A188 /* TarContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06ADB2D81EBCEFA60053A188 /* TarContainer.swift */; }; + 06ADB2DA1EBCEFA60053A188 /* TarContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06ADB2D81EBCEFA60053A188 /* TarContainer.swift */; }; + 06ADB2DB1EBCEFA60053A188 /* TarContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06ADB2D81EBCEFA60053A188 /* TarContainer.swift */; }; + 06ADB2DC1EBCEFA60053A188 /* TarContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06ADB2D81EBCEFA60053A188 /* TarContainer.swift */; }; + 06ADB2E01EBCF33C0053A188 /* TarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06ADB2DF1EBCF33C0053A188 /* TarTests.swift */; }; 06B374CB1E0EECD500064853 /* random_file.zlib in Resources */ = {isa = PBXBuildFile; fileRef = 06B374CA1E0EECD500064853 /* random_file.zlib */; }; 06BB8F301E0C2C720079FB9E /* LZMA2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BB8F2F1E0C2C720079FB9E /* LZMA2.swift */; }; 06BB8F311E0C2C720079FB9E /* LZMA2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BB8F2F1E0C2C720079FB9E /* LZMA2.swift */; }; @@ -167,6 +173,7 @@ 0680E1B21DDA3D0A005C05EB /* GzipTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GzipTests.swift; sourceTree = ""; }; 0680E1B41DDA4075005C05EB /* Bzip2Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bzip2Tests.swift; sourceTree = ""; }; 0681B3DD1E44F674001A9D79 /* BitToByteWriter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BitToByteWriter.swift; path = Sources/BitToByteWriter.swift; sourceTree = SOURCE_ROOT; }; + 0691B93F1EBE3E100037EE58 /* test.tar */ = {isa = PBXFileReference; lastKnownFileType = archive.tar; path = test.tar; sourceTree = ""; }; 06991C6D1E05A9F70030FF07 /* test9.lzma */ = {isa = PBXFileReference; lastKnownFileType = file; path = test9.lzma; sourceTree = ""; }; 06991C6E1E05A9F70030FF07 /* test10.lzma */ = {isa = PBXFileReference; lastKnownFileType = file; path = test10.lzma; sourceTree = ""; }; 06991C6F1E05A9F70030FF07 /* test11.lzma */ = {isa = PBXFileReference; lastKnownFileType = file; path = test11.lzma; sourceTree = ""; }; @@ -200,6 +207,8 @@ 06A393401DE070B500182E12 /* Info-watchOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-watchOS.plist"; sourceTree = ""; }; 06A393411DE070B500182E12 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 06A393421DE070B500182E12 /* SWCompression.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SWCompression.h; sourceTree = ""; }; + 06ADB2D81EBCEFA60053A188 /* TarContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TarContainer.swift; sourceTree = ""; }; + 06ADB2DF1EBCF33C0053A188 /* TarTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TarTests.swift; sourceTree = ""; }; 06B374CA1E0EECD500064853 /* random_file.zlib */ = {isa = PBXFileReference; lastKnownFileType = file; path = random_file.zlib; sourceTree = ""; }; 06BB8F2F1E0C2C720079FB9E /* LZMA2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LZMA2.swift; sourceTree = ""; }; 06BD0D201E3D0C630054AA5C /* SWCompressionSourceCode.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = SWCompressionSourceCode.zip; sourceTree = ""; }; @@ -274,6 +283,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 0691B93E1EBE3E100037EE58 /* TAR */ = { + isa = PBXGroup; + children = ( + 0691B93F1EBE3E100037EE58 /* test.tar */, + ); + path = TAR; + sourceTree = ""; + }; 069AC2161E02DE490041AC13 /* LZMA */ = { isa = PBXGroup; children = ( @@ -397,6 +414,7 @@ 061FCE251DBCC4BE0052F7BE /* Deflate.swift */, 06FED40B1DD7717E0013DFB2 /* BZip2.swift */, 067518351E2A86F700D16354 /* ZipContainer.swift */, + 06ADB2D81EBCEFA60053A188 /* TarContainer.swift */, 06A3933D1DE070B500182E12 /* Service */, ); path = Sources; @@ -414,6 +432,7 @@ 0680E1B01DDA2D7C005C05EB /* ZlibTests.swift */, 061FCE2C1DBCC6A30052F7BE /* Constants.swift */, 0675183C1E2A883600D16354 /* ZipTests.swift */, + 06ADB2DF1EBCF33C0053A188 /* TarTests.swift */, 06BE1AD81DB410F100EE0F59 /* Info.plist */, ); path = Tests; @@ -428,6 +447,7 @@ 069DCE7F1EB64B1500ADC374 /* GZip */, 069AC2161E02DE490041AC13 /* LZMA */, 06BD0D1F1E3D0C630054AA5C /* ZIP */, + 0691B93E1EBE3E100037EE58 /* TAR */, 06CC185F1DE35269003532F5 /* test.zlib */, 06B374CA1E0EECD500064853 /* random_file.zlib */, ); @@ -689,6 +709,7 @@ 069DCE9D1EB64D0300ADC374 /* test2.answer in Resources */, 06D07AB01EB64EE800BC8C65 /* test5.xz in Resources */, 06BD0D241E3D0C630054AA5C /* TestDataDescriptor.zip in Resources */, + 0691B9401EBE3E100037EE58 /* test.tar in Resources */, 06D07A9E1EB64E7A00BC8C65 /* test6.bz2 in Resources */, 06D07A9C1EB64E7A00BC8C65 /* test4.bz2 in Resources */, 06D07AA01EB64E7A00BC8C65 /* test8.bz2 in Resources */, @@ -757,6 +778,7 @@ 06FED40D1DD7717E0013DFB2 /* BZip2.swift in Sources */, 069D0FF61E0D6CD000D8AA87 /* LZMABitTreeDecoder.swift in Sources */, 065569681DC65C2A00A47E66 /* GzipArchive.swift in Sources */, + 06ADB2DA1EBCEFA60053A188 /* TarContainer.swift in Sources */, 06E2346D1E068E9600F18798 /* XZArchive.swift in Sources */, 063DF10B1DE1F07800F38082 /* DataWithPointer.swift in Sources */, ); @@ -782,6 +804,7 @@ 065569711DC65C2B00A47E66 /* Deflate.swift in Sources */, 069D0FF71E0D6CD000D8AA87 /* LZMABitTreeDecoder.swift in Sources */, 06FED40E1DD7717E0013DFB2 /* BZip2.swift in Sources */, + 06ADB2DB1EBCEFA60053A188 /* TarContainer.swift in Sources */, 06E2346E1E068E9600F18798 /* XZArchive.swift in Sources */, 0655696F1DC65C2B00A47E66 /* GzipArchive.swift in Sources */, ); @@ -807,6 +830,7 @@ 06FED40F1DD7717E0013DFB2 /* BZip2.swift in Sources */, 069D0FF81E0D6CD000D8AA87 /* LZMABitTreeDecoder.swift in Sources */, 065569761DC65C2B00A47E66 /* GzipArchive.swift in Sources */, + 06ADB2DC1EBCEFA60053A188 /* TarContainer.swift in Sources */, 06E2346F1E068E9600F18798 /* XZArchive.swift in Sources */, 063DF10C1DE1F07800F38082 /* DataWithPointer.swift in Sources */, ); @@ -832,6 +856,7 @@ 06A3933B1DE0709300182E12 /* Deflate.swift in Sources */, 069D0FF51E0D6CD000D8AA87 /* LZMABitTreeDecoder.swift in Sources */, 06A3933C1DE0709300182E12 /* BZip2.swift in Sources */, + 06ADB2D91EBCEFA60053A188 /* TarContainer.swift in Sources */, 06E2346C1E068E9600F18798 /* XZArchive.swift in Sources */, 063DF10A1DE1F07800F38082 /* DataWithPointer.swift in Sources */, ); @@ -847,6 +872,7 @@ 069AC2121E02DDAF0041AC13 /* LzmaTests.swift in Sources */, 0680E1B11DDA2D7C005C05EB /* ZlibTests.swift in Sources */, 0680E1B31DDA3D0A005C05EB /* GzipTests.swift in Sources */, + 06ADB2E01EBCF33C0053A188 /* TarTests.swift in Sources */, 06E234801E069ABD00F18798 /* XzTests.swift in Sources */, 0675183D1E2A883600D16354 /* ZipTests.swift in Sources */, ); diff --git a/Sources/LZMADecoder.swift b/Sources/LZMADecoder.swift index c97bdd93..ae20d00d 100644 --- a/Sources/LZMADecoder.swift +++ b/Sources/LZMADecoder.swift @@ -192,7 +192,7 @@ final class LZMADecoder { } // MARK: Main LZMA 2 decoder function. - + func decodeLZMA2(_ lzma2DictionarySize: Int) throws { mainLoop: while true { let controlByte = pointerData.alignedByte() diff --git a/Sources/TarContainer.swift b/Sources/TarContainer.swift new file mode 100644 index 00000000..82941ee2 --- /dev/null +++ b/Sources/TarContainer.swift @@ -0,0 +1,214 @@ +// +// TarContainer.swift +// SWCompression +// +// Created by Timofey Solomko on 05.05.17. +// Copyright © 2017 Timofey Solomko. All rights reserved. +// + +import Foundation + +/** + Error happened during processing TAR archive (container). + It may indicate that either the data is damaged or it might not be TAR archive (container) at all. + + - `error`: error description. + */ +public enum TarError: Error { + case TooSmallFileIsPassed + case FieldIsNotNumber + case WrongHeaderChecksum + case WrongUstarVersion +} + +public class TarContainer { + + enum TarType { + case prePOSIX + case ustar // aka POSIX. + case pax // TODO: Implement + } + + public static func files(from data: Data) throws -> [Data] { + // First, if the TAR container contains only header, it should be at least 512 bytes long. + // So we have to check this. + guard data.count >= 512 else { throw TarError.TooSmallFileIsPassed } + + // Then, we need to find out, if the container is POSIX or not. + // POSIX containers have a 'ustar\x00' (or 'ustar\x20') indicator at offset 257. + let posixIndicator = String(data: data.subdata(in: 257..<263), encoding: .ascii) +// if posixIndicator == "ustar\u{00}" || posixIndicator == "ustar\u{20}" { +// return try parseUstar(data) +// } else { +// return try parsePrePosix(data) +// } + + var output = [Data]() + + var index = 0 + var blockStartIndex = 0 + while true { + // Container ends with two zero-filled records. + if data.subdata(in: index.. [Data] { +// return [] +// } + +// static func parseUstar(_ data: Data) throws -> [Data] { +// return [] +// } + +} + +fileprivate extension Data { + + fileprivate func nullEndedBuffer(_ startIndex: Int, _ cutoff: Int) -> [UInt8] { + var index = startIndex + var buffer = [UInt8]() + while true { + if self[index] == 0 || index - startIndex >= cutoff { + break + } + buffer.append(self[index]) + index += 1 + } + return buffer + } + + fileprivate func nullEndedAsciiString(_ startIndex: Int, _ cutoff: Int) -> String? { + return String(bytes: self.nullEndedBuffer(startIndex, cutoff), encoding: .ascii) + } + + fileprivate func nullSpaceEndedBuffer(_ startIndex: Int, _ cutoff: Int) -> [UInt8] { + var index = startIndex + var buffer = [UInt8]() + while true { + if self[index] == 0 || self[index] == 0x20 || index - startIndex >= cutoff { + break + } + buffer.append(self[index]) + index += 1 + } + return buffer + } + + fileprivate func nullSpaceEndedAsciiString(_ startIndex: Int, _ cutoff: Int) -> String? { + return String(bytes: self.nullSpaceEndedBuffer(startIndex, cutoff), encoding: .ascii) + } + +} + +fileprivate func octalToDecimal(_ number: Int) -> Int { + var octal = number + var decimal = 0, i = 0 + while octal != 0 { + let remainder = octal % 10 + octal /= 10 + decimal += remainder * Int(pow(8, Double(i))) + i += 1 + } + return decimal +} + +fileprivate func roundTo512(value: Int) -> Int { + let fractionNum = Double(value) / 512 + let roundedNum = Int(ceil(fractionNum)) + return roundedNum * 512 +} diff --git a/Tests/TarTests.swift b/Tests/TarTests.swift new file mode 100644 index 00000000..f92fef52 --- /dev/null +++ b/Tests/TarTests.swift @@ -0,0 +1,26 @@ +// +// TarTests.swift +// SWCompression +// +// Created by Timofey Solomko on 05.05.17. +// Copyright © 2017 Timofey Solomko. All rights reserved. +// + +import XCTest +import SWCompression + +class TarTests: XCTestCase { + + static let testType: String = "tar" + + func test() { + guard let testData = try? Data(contentsOf: Constants.url(forTest: "test", withType: TarTests.testType), + options: .mappedIfSafe) else { + XCTFail("Failed to load test archive") + return + } + + _ = try? TarContainer.files(from: testData) + } + +} diff --git a/Tests/Test Files/TAR/test.tar b/Tests/Test Files/TAR/test.tar new file mode 100644 index 00000000..2bc9f067 --- /dev/null +++ b/Tests/Test Files/TAR/test.tar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5a8af8043bfdecf913c25ac11e65acd592b94281010867a92b99d15a6ce4ffb4 +size 2560