タイトルママです。
過去のソースコードから発掘したので紹介。
ベースはdeimosのliblzma
出力Rangeベースにラッピングしました。
pragma(lib, "lzma");
pragma(lib, "liblzma");
import deimos.lzma;
import std.array, std.exception;
/*******************************************************************************
* エンコーダー
*/
struct LzmaStreamEncoder
{
private:
lzma_stream _strm = lzma_stream.init; /* alloc and init lzma_stream struct */
Appender!(ubyte[]) _app;
public:
/***************************************************************************
* 設定
*/
void reset(uint preset = 6 | LZMA_PRESET_EXTREME, lzma_check check = lzma_check.LZMA_CHECK_CRC64)
{
if (_strm != lzma_stream.init)
{
lzma_end(&_strm);
_strm = lzma_stream.init;
}
_app.shrinkTo(0);
enforce(lzma_easy_encoder(&_strm, preset, check) == lzma_ret.LZMA_OK);
}
~this()
{
lzma_end(&_strm);
}
/***************************************************************************
* データ追加
*/
void put(in ubyte[] buf)
{
if (_strm is lzma_stream.init)
reset();
ubyte[1024] outbuf = void;
_strm.next_in = buf.ptr;
_strm.avail_in = buf.length;
while (1)
{
_strm.next_out = outbuf.ptr;
_strm.avail_out = outbuf.length;
/* compress data */
auto ret = lzma_code(&_strm, lzma_action.LZMA_RUN);
enforce((ret == lzma_ret.LZMA_OK) || (ret == lzma_ret.LZMA_STREAM_END));
auto len = outbuf.length - _strm.avail_out;
if (len == 0)
{
break;
}
_app.put(outbuf[0..len]);
if ( _strm.avail_out != 0 )
{
break;
}
}
}
/***************************************************************************
* 圧縮されたデータを取得
*/
ubyte[] complete()
{
ubyte[1024] outbuf = void;
_strm.next_in = null;
_strm.avail_in = 0;
while (1)
{
_strm.next_out = outbuf.ptr;
_strm.avail_out = outbuf.length;
/* compress data */
auto ret = lzma_code(&_strm, lzma_action.LZMA_FINISH);
enforce((ret == lzma_ret.LZMA_OK) || (ret == lzma_ret.LZMA_STREAM_END));
auto len = outbuf.length - _strm.avail_out;
if (len == 0)
{
break;
}
_app.put(outbuf[0..len]);
if ( _strm.avail_out != 0 )
{
break;
}
}
return _app.data();
}
}
/*******************************************************************************
* デコーダー
*/
struct LzmaStreamDecoder
{
private:
lzma_stream _strm = lzma_stream.init; /* alloc and init lzma_stream struct */
Appender!(ubyte[]) _app;
public:
/***************************************************************************
* 設定
*/
void reset(ulong memlimit = lzma_easy_decoder_memusage(9))
{
if (_strm != lzma_stream.init)
{
lzma_end(&_strm);
_strm = lzma_stream.init;
}
_app.clear();
enforce(lzma_stream_decoder(&_strm, memlimit, 0) == lzma_ret.LZMA_OK);
}
~this()
{
lzma_end(&_strm);
}
/***************************************************************************
* 圧縮されたデータを追加
*/
void put(in ubyte[] buf)
{
if (_strm is lzma_stream.init)
reset();
ubyte[1024] outbuf = void;
_strm.next_in = buf.ptr;
_strm.avail_in = buf.length;
while (1)
{
_strm.next_out = outbuf.ptr;
_strm.avail_out = outbuf.length;
/* compress data */
auto ret = lzma_code(&_strm, lzma_action.LZMA_RUN);
enforce((ret == lzma_ret.LZMA_OK) || (ret == lzma_ret.LZMA_STREAM_END));
auto len = outbuf.length - _strm.avail_out;
if (len == 0)
{
break;
}
_app.put(outbuf[0..len]);
if ( _strm.avail_out != 0 )
{
break;
}
}
}
/***************************************************************************
* 進展されたデータを取得
*/
@property ubyte[] get()
{
scope (exit) _app.clear();
return _app.data();
}
}
unittest
{
LzmaStreamEncoder enc;
LzmaStreamDecoder dec;
ubyte[1024] buf;
// 単純なデータで圧縮のテスト
buf[] = 1;
enc.put(buf);
buf[] = 2;
enc.put(buf);
buf[] = 3;
enc.put(buf);
buf[] = 4;
enc.put(buf);
auto compressed = enc.complete();
// 圧縮されてる
assert(compressed.length < 1024 * 4);
// 伸展のテスト
dec.put(compressed);
auto decompressed = dec.get();
assert(decompressed.length == 1024 * 4);
import std.algorithm, std.range;
assert(equal(decompressed[1024*0 .. 1024*1], repeat(1, 1024)));
assert(equal(decompressed[1024*1 .. 1024*2], repeat(2, 1024)));
assert(equal(decompressed[1024*2 .. 1024*3], repeat(3, 1024)));
assert(equal(decompressed[1024*3 .. 1024*4], repeat(4, 1024)));
}
特別説明することはありませんね……
dub使えば先頭のpragma(lib, ...)は要らないかも?