1 module zstd.compress;
2 
3 import zstd.c.zstd;
4 import zstd.common;
5 
6 enum Level : int
7 {
8     base = 3,
9     mix = 1,
10     max = 22,
11     speed = 1,
12     size = 22,
13 }
14 
15 ubyte[] compress(const(void)[] src, int level = Level.base)
16 {
17     auto destCap = ZSTD_compressBound(src.length);
18     auto destBuf = new ubyte[destCap];
19     auto result = ZSTD_compress(destBuf.ptr, destCap, src.ptr, src.length, level);
20     if (ZSTD_isError(result)) {
21         destBuf = null;
22         throw new ZstdException(result);
23     }
24 
25     return destBuf[0..result];
26 }
27 
28 class Compressor
29 {
30   private:
31     ZSTD_CStream* cstream;
32     ubyte[] buffer;
33 
34   public:
35     @property @trusted static
36     {
37         size_t recommendedInSize()
38         {
39             return ZSTD_CStreamInSize();
40         }
41 
42         size_t recommendedOutSize()
43         {
44             return ZSTD_CStreamOutSize();
45         }
46     }
47 
48     this(int level = Level.base)
49     in
50     {
51         assert(Level.min <= level && level <= Level.max);
52     }
53     body
54     {
55         cstream = ZSTD_createCStream();
56         buffer = new ubyte[](recommendedOutSize);
57         size_t result = ZSTD_initCStream(cstream, level);
58         if (ZSTD_isError(result))
59             throw new ZstdException(result);
60     }
61 
62     ~this()
63     {
64         closeStream();
65     }
66 
67     ubyte[] compress(const(void)[] src)
68     {
69         ubyte[] result;
70         ZSTD_inBuffer input = {src.ptr, src.length, 0};
71         ZSTD_outBuffer output = {buffer.ptr, buffer.length, 0};
72 
73         while (input.pos < input.size) {
74             output.pos = 0;
75             size_t code = ZSTD_compressStream(cstream, &output, &input);
76             if (ZSTD_isError(code))
77                 throw new ZstdException(code);
78             result ~= buffer[0..output.pos];
79         }
80 
81         return result;
82     }
83 
84     ubyte[] flush()
85     {
86         ZSTD_outBuffer output = {buffer.ptr, buffer.length, 0};
87 
88         size_t code = ZSTD_flushStream(cstream, &output);
89         if (ZSTD_isError(code))
90             throw new ZstdException(code);
91 
92         return buffer[0..output.pos];
93     }
94 
95     ubyte[] finish()
96     {
97         ZSTD_outBuffer output = {buffer.ptr, buffer.length, 0};
98 
99         size_t remainingToFlush = ZSTD_endStream(cstream, &output);
100         // TODO: Provide finish(ref size_t remainingToFlush) version?
101         if (remainingToFlush > 0)
102             throw new ZstdException("not fully flushed.");
103         closeStream();
104 
105         return buffer[0..output.pos];
106     }
107 
108   private:
109     void closeStream()
110     {
111         if (cstream) {
112             ZSTD_freeCStream(cstream);
113             cstream = null;
114         }
115     }
116 }