1 module zgrf.compression;
2 
3 import lzma;
4 
5 extern (C) private void* szAlloc(void* p, size_t size)
6 {
7     import core.stdc.stdlib : malloc;
8 
9     return malloc(size);
10 }
11 
12 extern (C) private void szFree(void* p, void* address)
13 {
14     import core.stdc.stdlib : free;
15 
16     free(address);
17 }
18 
19 private ISzAlloc szAllocLzma = {&szAlloc, &szFree};
20 
21 /**
22  * Uncompresses data. If the first byte of srcbuf is 0 then
23  * LZMA will be used to uncompress the data. Otherwise
24  * uses zlib.
25  *
26  * Params:
27  *  srcbuf = The compressed data
28  *  destlen = The uncompressed size
29  *
30  * Returns:
31  *  The uncompressed data
32  */
33 ubyte[] uncompress(const(ubyte)[] srcbuf, size_t destlen = 0u)
34 {
35     ubyte[] dst;
36     if (destlen == 0)
37     {
38         destlen = srcbuf.length * 2 + 1;
39     }
40 
41     if (srcbuf[0] == 0)
42     {
43         if (srcbuf.length < LZMA_PROPS_SIZE + 1)
44         {
45             return dst;
46         }
47         ELzmaStatus status;
48         SizeT destlen2 = destlen;
49         SizeT pack_size = srcbuf.length - LZMA_PROPS_SIZE - 1;
50         dst = new ubyte[destlen];
51         int ret = LzmaDecode(
52                 dst.ptr,
53                 &destlen2,
54                 cast(const(Byte*))(&srcbuf[LZMA_PROPS_SIZE + 1]),
55                 &pack_size,
56                 cast(const(Byte*))(&srcbuf[1]),
57                 LZMA_PROPS_SIZE,
58                 ELzmaFinishMode.LZMA_FINISH_END,
59                 &status,
60                 &szAllocLzma
61         );
62         if (ret != SZ_OK)
63         {
64         }
65     }
66     else
67     {
68         dst = cast(ubyte[]) zlib_uncompress(srcbuf, destlen);
69     }
70     return dst;
71 }
72 
73 // Taken directly from std/zlib.d
74 // This implementation does not treat Z_BUF_ERROR as an error that should be thrown
75 private void[] zlib_uncompress(const(void)[] srcbuf, size_t destlen = 0u, int winbits = 15)
76 {
77     import etc.c.zlib;
78     import std.zlib : ZlibException;
79     import std.conv : to;
80     int err;
81     ubyte[] destbuf;
82 
83     if (!destlen)
84         destlen = srcbuf.length * 2 + 1;
85 
86     etc.c.zlib.z_stream zs;
87     zs.next_in = cast(typeof(zs.next_in)) srcbuf.ptr;
88     zs.avail_in = to!uint(srcbuf.length);
89     err = etc.c.zlib.inflateInit2(&zs, winbits);
90     if (err)
91     {
92         throw new ZlibException(err);
93     }
94 
95     size_t olddestlen = 0u;
96 
97     loop:
98     while (true)
99     {
100         destbuf.length = destlen;
101         zs.next_out = cast(typeof(zs.next_out)) &destbuf[olddestlen];
102         zs.avail_out = to!uint(destlen - olddestlen);
103         olddestlen = destlen;
104 
105         err = etc.c.zlib.inflate(&zs, Z_NO_FLUSH);
106         switch (err)
107         {
108             case Z_OK:
109                 destlen = destbuf.length * 2;
110                 continue loop;
111 
112             case Z_BUF_ERROR:
113             case Z_STREAM_END:
114                 destbuf.length = zs.total_out;
115                 err = etc.c.zlib.inflateEnd(&zs);
116                 if (err != Z_OK)
117                     throw new ZlibException(err);
118                 return destbuf;
119 
120             default:
121                 etc.c.zlib.inflateEnd(&zs);
122                 throw new ZlibException(err);
123         }
124     }
125     assert(0, "Unreachable code");
126 }