File: lz4.d

package info (click to toggle)
sambamba 1.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 3,528 kB
  • sloc: sh: 220; python: 166; ruby: 147; makefile: 103
file content (322 lines) | stat: -rw-r--r-- 11,152 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/*
    This file is part of Sambamba.
    Copyright (C) 2012-2015    Artem Tarasov <lomereiter@gmail.com>

    Sambamba is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    Sambamba is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/
module utils.lz4;
import std.stdio, contrib.undead.stream, std.conv;

extern(C) {
  alias size_t LZ4F_errorCode_t;
  uint LZ4F_isError(LZ4F_errorCode_t code);

  immutable(char)* LZ4F_getErrorName(LZ4F_errorCode_t code); /* return error code string; useful for debugging */

  enum blockSizeID_t { LZ4F_default=0, max64KB=4, max256KB=5, max1MB=6, max4MB=7 }
  enum blockMode_t { blockLinked=0, blockIndependent}
  enum contentChecksum_t { noContentChecksum=0, contentChecksumEnabled }
  enum frameType_t { LZ4F_frame=0, skippableFrame }

  struct LZ4F_frameInfo_t {
    blockSizeID_t blockSizeID; /* max64KB, max256KB, max1MB, max4MB ; 0 == default */
    blockMode_t blockMode; /* blockLinked, blockIndependent ; 0 == default */
    contentChecksum_t contentChecksumFlag; /* noContentChecksum, contentChecksumEnabled ; 0 == default */
    frameType_t frameType; /* LZ4F_frame, skippableFrame ; 0 == default */
    ulong contentSize; /* Size of uncompressed (original) content ; 0 == unknown */
    uint[2] reserved; /* must be zero for forward compatibility */
  }

  struct LZ4F_preferences_t {
    LZ4F_frameInfo_t frameInfo;
    uint compressionLevel; /* 0 == default (fast mode); values above 16 count as 16 */
    uint autoFlush; /* 1 == always flush (reduce need for tmp buffer) */
    uint[4] reserved; /* must be zero for forward compatibility */
  }

  size_t LZ4F_compressFrameBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr);
  size_t LZ4F_compressFrame(void* dstBuffer, size_t dstMaxSize, const void* srcBuffer, size_t srcSize, const LZ4F_preferences_t* preferencesPtr);

  alias void* LZ4F_compressionContext_t;
  struct LZ4F_compressOptions_t {
    uint stableSrc; /* 1 == src content will remain available on future calls to LZ4F_compress(); avoid saving src content within tmp buffer as future dictionary */
    uint[3] reserved;
  }

  enum LZ4F_VERSION = 100;
  LZ4F_errorCode_t LZ4F_createCompressionContext(LZ4F_compressionContext_t* cctxPtr, uint version_);
  LZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_compressionContext_t cctx);
  size_t LZ4F_compressBegin(LZ4F_compressionContext_t cctx, void* dstBuffer, size_t dstMaxSize, const LZ4F_preferences_t* prefsPtr);
  size_t LZ4F_compressBound(size_t srcSize, const LZ4F_preferences_t* prefsPtr);
  size_t LZ4F_compressUpdate(LZ4F_compressionContext_t cctx, void* dstBuffer, size_t dstMaxSize, const void* srcBuffer, size_t srcSize, const LZ4F_compressOptions_t* cOptPtr);
  size_t LZ4F_flush(LZ4F_compressionContext_t cctx, void* dstBuffer, size_t dstMaxSize, const LZ4F_compressOptions_t* cOptPtr);
  size_t LZ4F_compressEnd(LZ4F_compressionContext_t cctx, void* dstBuffer, size_t dstMaxSize, const LZ4F_compressOptions_t* cOptPtr);
  alias void* LZ4F_decompressionContext_t;
  struct LZ4F_decompressOptions_t {
    uint stableDst; /* guarantee that decompressed data will still be there on next function calls (avoid storage into tmp buffers) */
    uint[3] reserved;
  }
  LZ4F_errorCode_t LZ4F_createDecompressionContext(LZ4F_decompressionContext_t* dctxPtr, uint version_);
  LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_decompressionContext_t dctx);
  size_t LZ4F_getFrameInfo(LZ4F_decompressionContext_t dctx, LZ4F_frameInfo_t* frameInfoPtr, const void* srcBuffer, size_t* srcSizePtr);
  size_t LZ4F_decompress(LZ4F_decompressionContext_t dctx, void* dstBuffer, size_t* dstSizePtr, const void* srcBuffer, size_t* srcSizePtr, const LZ4F_decompressOptions_t* dOptPtr);

}

class LZ4Exception : Exception {
  this(string description, LZ4F_errorCode_t errorCode) {
    super(description ~ " : " ~ LZ4F_getErrorName(errorCode).to!string);
  }

  this(string description) {
    super(description);
  }
}

class LZ4Compressor {
  private {
    LZ4F_preferences_t prefs;
    ubyte[] in_buff;
    ubyte[] out_buff;
    LZ4F_compressionContext_t ctx;

    LZ4F_compressionContext_t createCompressionContext() {
      LZ4F_compressionContext_t ctx;
      auto code = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
      if (LZ4F_isError(code))
        throw new LZ4Exception("Failure in LZ4F_createCompressionContext", code);
      return ctx;
    }

    void freeCompressionContext(LZ4F_compressionContext_t ctx) {
      auto code = LZ4F_freeCompressionContext(ctx);
      if (LZ4F_isError(code))
        throw new LZ4Exception("Failed to free LZ4F compression context", code);
    }
  }

  this() {
    prefs.autoFlush = 1;
    prefs.frameInfo.blockMode = blockMode_t.blockIndependent;
    prefs.frameInfo.blockSizeID = blockSizeID_t.max256KB;
    prefs.frameInfo.contentChecksumFlag = contentChecksum_t.contentChecksumEnabled;

    int block_size = (1 << (8 + (2 * prefs.frameInfo.blockSizeID)));
    in_buff.length = block_size;
    out_buff.length = LZ4F_compressBound(block_size, &prefs);
  }

  private {
    size_t compressBegin() {
      auto sz = LZ4F_compressBegin(ctx, out_buff.ptr, out_buff.length, &prefs);
      if (LZ4F_isError(sz))
        throw new LZ4Exception("Failure in LZ4F_compressBegin", sz);
      return sz;
    }

    size_t compressUpdate(ubyte[] block) {
      auto sz = LZ4F_compressUpdate(ctx, out_buff.ptr, out_buff.length, block.ptr, block.length, null);
      if (LZ4F_isError(sz))
        throw new LZ4Exception("Failure in LZ4F_compressUpdate", sz);
      return sz;
    }

    size_t compressEnd() {
      auto sz = LZ4F_compressEnd(ctx, out_buff.ptr, out_buff.length, null);
      if (LZ4F_isError(sz))
        throw new LZ4Exception("Failure in LZ4F_compressEnd", sz);
      return sz;
    }
  }

  void compress(ubyte[] function(ubyte[], void*) read_block, void* data,
                std.stdio.File output_file,
                int compressionLevel=0)
  {
    prefs.compressionLevel = compressionLevel;

    ctx = createCompressionContext();
    assert(ctx !is null);

    auto sz = compressBegin();
    output_file.rawWrite(out_buff[0 .. sz]);

    while (true) {
      ubyte[] block = read_block(in_buff, data);
      if (block.length == 0) break;
      sz = compressUpdate(block);
      output_file.rawWrite(out_buff[0 .. sz]);
    }

    sz = compressEnd();
    output_file.rawWrite(out_buff[0 .. sz]);

    freeCompressionContext(ctx);
  }

  private {
    static ubyte[] read_block_from_file(ubyte[] buf, void* data) {
      auto input_file = cast(std.stdio.File*)data;
      return input_file.rawRead(buf);
    }

    import std.algorithm : min;
    static ubyte[] read_block_from_array(ubyte[] buf, void* data) {
      auto arr = cast(ubyte[]*)data;
      auto n = min(buf.length, (*arr).length);
      buf[0 .. n] = (*arr)[0 .. n];
      *arr = (*arr)[n .. $];
      return buf[0 .. n];
    }
  }
  void compress(std.stdio.File input_file, std.stdio.File output_file,
                int compressionLevel=0) {
    compress(&read_block_from_file, &input_file, output_file, compressionLevel);
  }

  void compress(ubyte[] data, std.stdio.File output_file,
                int compressionLevel=0) {
    compress(&read_block_from_array, &data, output_file, compressionLevel);
  }
}

private {
  LZ4F_decompressionContext_t createDecompressionContext() {
    LZ4F_decompressionContext_t ctx;
    auto code = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
    if (LZ4F_isError(code))
      throw new LZ4Exception("Failure in LZ4F_createDecompressionContext", code);
    return ctx;
  }

  void freeDecompressionContext(LZ4F_compressionContext_t ctx) {
    auto code = LZ4F_freeDecompressionContext(ctx);
    if (LZ4F_isError(code))
      throw new LZ4Exception("Failed to free LZ4F decompression context", code);
  }
}

struct LZ4File {
  private {
    std.stdio.File input_file;
    LZ4F_decompressionContext_t ctx;

    size_t bytes_read, bytes_written;

    void decompress(ubyte[] block, ubyte[] out_buff) {
        bytes_read = block.length;
        bytes_written = out_buff.length;
        auto code = LZ4F_decompress(ctx, out_buff.ptr, &bytes_written, block.ptr, &bytes_read, null);
        if (LZ4F_isError(code))
          throw new LZ4Exception("Failure in LZ4F_decompress", code);
    }

    ubyte[] in_buff;
    ubyte[] block;
  }

  this(string filename) {
    input_file = std.stdio.File(filename);

    in_buff.length = 256 << 10;

    ctx = createDecompressionContext();
    assert(ctx !is null);

    auto header = input_file.rawRead(in_buff[0 .. 4]);
    assert(header.length == 4);

    ubyte[256] tmp;
    decompress(in_buff[0 .. 4], tmp[]); // header
    assert(bytes_read == 4);
    assert(bytes_written == 0);
  }

  void close() {
    freeDecompressionContext(ctx);
    input_file.close();
  }

  ubyte[] rawRead(ubyte[] buffer) {
    while (true) {
      decompress(block, buffer);
      block = block[bytes_read .. $];
      if (bytes_written > 0)
        break;
      if (block.length == 0 && !readNextBlock())
        break;
    }
    return buffer[0 .. bytes_written];
  }

  private bool readNextBlock() {
      block = input_file.rawRead(in_buff);
      return block.length > 0;
  }
}

class LZ4Decompressor {
  private {
    ubyte[] in_buff;
    ubyte[] out_buff;
  }

  this() {
    in_buff.length = 256 << 10;
    out_buff.length = 256 << 10;
  }

  void decompress(contrib.undead.stream.InputStream input_file,
                  std.stdio.File output_file) {
    size_t bytes_read, bytes_written;

    auto ctx = createDecompressionContext();
    assert(ctx !is null);

    void decompress(ubyte[] block) {
      bytes_read = block.length;
      bytes_written = out_buff.length;
      auto code = LZ4F_decompress(ctx, out_buff.ptr, &bytes_written, block.ptr, &bytes_read, null);
      if (LZ4F_isError(code))
        throw new LZ4Exception("Failure in LZ4F_decompress", code);
    }

    input_file.readExact(in_buff.ptr, 4);

    decompress(in_buff[0 .. 4]); // header
    assert(bytes_read == 4);
    assert(bytes_written == 0);

    while (true) {
      size_t raw_read = input_file.read(in_buff);
      if (raw_read == 0) break;
      auto block = in_buff[0 .. raw_read];
      while (block.length > 0) {
        decompress(block);
        block = block[bytes_read .. $];
        if (bytes_written > 0)
          output_file.rawWrite(out_buff[0 .. bytes_written]);
      }
    }
    freeDecompressionContext(ctx);
  }
}

int lz4compress_main() {
  auto compressor = new LZ4Compressor();
  compressor.compress(stdin, stdout);
  return 0;
}