1 /** 2 Type that implements the TarGz Filter used with the Archive template. 3 4 Copyright: Copyright Richard W Laughlin Jr. 2014—2016 5 6 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 7 8 Authors: Richard W Laughlin Jr. 9 10 Source: http://github.com/rcythr/archive 11 12 Filter class which can be used by the Archive class to compress/decompress files into a .gz format. 13 14 The only current available alias which uses this filter is the TarGzArchive. 15 16 Reading Usage: 17 --- 18 import archive.targz; 19 import std.stdio; 20 21 auto archive = new TarGzArchive(std.file.read("my.tar.gz"); 22 23 foreach(file; archive.files) 24 { 25 writeln("Filename: ", file.path); 26 writeln("Data: ", file.data); 27 } 28 29 --- 30 31 Writing Usage: 32 --- 33 import archive.targz; 34 35 auto archive = new TarGzArchive(); 36 37 auto file = new TarGzArchive.File("languages/awesome.txt"); 38 file.data = "D\n"; // can also set to immutable(ubyte)[] 39 archive.addFile(file); 40 41 std.file.write("lang.tar.gz", cast(ubyte[])archive.serialize()); 42 43 --- 44 45 */ 46 47 module archive.targz; 48 49 import archive.core; 50 import archive.tar; 51 52 private import etc.c.zlib; 53 private import std.algorithm; 54 private import std.array; 55 private import std.zlib; 56 57 /** 58 * Filter class which can be used by the Archive class to compress/decompress 59 * files into a .gz format. 60 */ 61 public class GzFilter(int L) 62 { 63 /** 64 * Input data is wrapped with gzip and returned. 65 */ 66 public static void[] compress(void[] data) 67 { 68 auto result = appender!(ubyte[])(); 69 70 Compress compressor = new Compress(L, HeaderFormat.gzip); 71 for(uint i=0; i < data.length; i += 1024) 72 { 73 result.put(cast(ubyte[])compressor.compress(data[i .. min(i+1024, data.length)])); 74 } 75 result.put(cast(ubyte[])compressor.flush()); 76 77 return result.data; 78 } 79 80 /** 81 * Input data is processed to extract from the gzip format. 82 */ 83 public static void[] decompress(void[] data) 84 { 85 auto result = appender!(ubyte[])(); 86 87 UnCompress uncompressor = new UnCompress(); 88 for(uint i=0; i < data.length; i += 1024) 89 { 90 result.put(cast(ubyte[])uncompressor.uncompress(data[i .. min(i+1024, data.length)])); 91 } 92 result.put(cast(ubyte[])uncompressor.flush()); 93 94 return result.data; 95 } 96 } 97 98 /** 99 * Convenience alias that simplifies the interface for users 100 */ 101 alias TarGzArchive = Archive!(TarPolicy, GzFilter!6); 102 103 unittest 104 { 105 string data1 = "HELLO\nI AM A FILE WITH SOME DATA\n1234567890\nABCDEFGHIJKLMOP"; 106 immutable(ubyte)[] data2 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 107 108 TarGzArchive output = new TarGzArchive(); 109 110 // Add file into the top level directory. 111 TarGzArchive.File file1 = new TarGzArchive.File(); 112 file1.path = "apple.txt"; 113 file1.data = data1; 114 output.addFile(file1); 115 116 // Add a file into a non top level directory. 117 TarGzArchive.File file2 = new TarGzArchive.File("directory/directory/directory/apple.txt"); 118 file2.data = data2; 119 output.addFile(file2); 120 121 // Add a directory that already exists. 122 output.addDirectory("directory/"); 123 124 // Add a directory that does not exist. 125 output.addDirectory("newdirectory/"); 126 127 // Remove unused directories 128 output.removeEmptyDirectories(); 129 130 // Ensure the only unused directory was removed. 131 assert(output.getDirectory("newdirectory") is null); 132 133 // Re-add a directory that does not exist so we can test its output later. 134 output.addDirectory("newdirectory/"); 135 136 // Serialize the zip archive and construct a new zip with it 137 TarGzArchive input = new TarGzArchive(output.serialize()); 138 139 // Make sure that there is a file named apple.txt and a file named directory/directory/directory/apple.txt 140 assert(input.getFile("apple.txt") !is null); 141 assert(input.getFile("directory/directory/directory/apple.txt") !is null); 142 143 // Make sure there are no extra directories or files 144 assert(input.numFiles() == 2); 145 assert(input.numDirectories() == 4); 146 assert(input.numMembers() == 6); 147 }